4e906d41f241998066c535f07c1f8a0d0ab81147
[roojs1] / roojs-ui-debug.js
1 /*
2  * Based on:
3  * Ext JS Library 1.1.1
4  * Copyright(c) 2006-2007, Ext JS, LLC.
5  *
6  * Originally Released Under LGPL - original licence link has changed is not relivant.
7  *
8  * Fork - LGPL
9  * <script type="text/javascript">
10  */
11
12
13
14 /*
15  * These classes are derivatives of the similarly named classes in the YUI Library.
16  * The original license:
17  * Copyright (c) 2006, Yahoo! Inc. All rights reserved.
18  * Code licensed under the BSD License:
19  * http://developer.yahoo.net/yui/license.txt
20  */
21
22 (function() {
23
24 var Event=Roo.EventManager;
25 var Dom=Roo.lib.Dom;
26
27 /**
28  * @class Roo.dd.DragDrop
29  * @extends Roo.util.Observable
30  * Defines the interface and base operation of items that that can be
31  * dragged or can be drop targets.  It was designed to be extended, overriding
32  * the event handlers for startDrag, onDrag, onDragOver and onDragOut.
33  * Up to three html elements can be associated with a DragDrop instance:
34  * <ul>
35  * <li>linked element: the element that is passed into the constructor.
36  * This is the element which defines the boundaries for interaction with
37  * other DragDrop objects.</li>
38  * <li>handle element(s): The drag operation only occurs if the element that
39  * was clicked matches a handle element.  By default this is the linked
40  * element, but there are times that you will want only a portion of the
41  * linked element to initiate the drag operation, and the setHandleElId()
42  * method provides a way to define this.</li>
43  * <li>drag element: this represents the element that would be moved along
44  * with the cursor during a drag operation.  By default, this is the linked
45  * element itself as in {@link Roo.dd.DD}.  setDragElId() lets you define
46  * a separate element that would be moved, as in {@link Roo.dd.DDProxy}.
47  * </li>
48  * </ul>
49  * This class should not be instantiated until the onload event to ensure that
50  * the associated elements are available.
51  * The following would define a DragDrop obj that would interact with any
52  * other DragDrop obj in the "group1" group:
53  * <pre>
54  *  dd = new Roo.dd.DragDrop("div1", "group1");
55  * </pre>
56  * Since none of the event handlers have been implemented, nothing would
57  * actually happen if you were to run the code above.  Normally you would
58  * override this class or one of the default implementations, but you can
59  * also override the methods you want on an instance of the class...
60  * <pre>
61  *  dd.onDragDrop = function(e, id) {
62  *  &nbsp;&nbsp;alert("dd was dropped on " + id);
63  *  }
64  * </pre>
65  * @constructor
66  * @param {String} id of the element that is linked to this instance
67  * @param {String} sGroup the group of related DragDrop objects
68  * @param {object} config an object containing configurable attributes
69  *                Valid properties for DragDrop:
70  *                    padding, isTarget, maintainOffset, primaryButtonOnly
71  */
72 Roo.dd.DragDrop = function(id, sGroup, config) {
73     if (id) {
74         this.init(id, sGroup, config);
75     }
76     
77 };
78
79 Roo.extend(Roo.dd.DragDrop, Roo.util.Observable , {
80
81     /**
82      * The id of the element associated with this object.  This is what we
83      * refer to as the "linked element" because the size and position of
84      * this element is used to determine when the drag and drop objects have
85      * interacted.
86      * @property id
87      * @type String
88      */
89     id: null,
90
91     /**
92      * Configuration attributes passed into the constructor
93      * @property config
94      * @type object
95      */
96     config: null,
97
98     /**
99      * The id of the element that will be dragged.  By default this is same
100      * as the linked element , but could be changed to another element. Ex:
101      * Roo.dd.DDProxy
102      * @property dragElId
103      * @type String
104      * @private
105      */
106     dragElId: null,
107
108     /**
109      * the id of the element that initiates the drag operation.  By default
110      * this is the linked element, but could be changed to be a child of this
111      * element.  This lets us do things like only starting the drag when the
112      * header element within the linked html element is clicked.
113      * @property handleElId
114      * @type String
115      * @private
116      */
117     handleElId: null,
118
119     /**
120      * An associative array of HTML tags that will be ignored if clicked.
121      * @property invalidHandleTypes
122      * @type {string: string}
123      */
124     invalidHandleTypes: null,
125
126     /**
127      * An associative array of ids for elements that will be ignored if clicked
128      * @property invalidHandleIds
129      * @type {string: string}
130      */
131     invalidHandleIds: null,
132
133     /**
134      * An indexted array of css class names for elements that will be ignored
135      * if clicked.
136      * @property invalidHandleClasses
137      * @type string[]
138      */
139     invalidHandleClasses: null,
140
141     /**
142      * The linked element's absolute X position at the time the drag was
143      * started
144      * @property startPageX
145      * @type int
146      * @private
147      */
148     startPageX: 0,
149
150     /**
151      * The linked element's absolute X position at the time the drag was
152      * started
153      * @property startPageY
154      * @type int
155      * @private
156      */
157     startPageY: 0,
158
159     /**
160      * The group defines a logical collection of DragDrop objects that are
161      * related.  Instances only get events when interacting with other
162      * DragDrop object in the same group.  This lets us define multiple
163      * groups using a single DragDrop subclass if we want.
164      * @property groups
165      * @type {string: string}
166      */
167     groups: null,
168
169     /**
170      * Individual drag/drop instances can be locked.  This will prevent
171      * onmousedown start drag.
172      * @property locked
173      * @type boolean
174      * @private
175      */
176     locked: false,
177
178     /**
179      * Lock this instance
180      * @method lock
181      */
182     lock: function() { this.locked = true; },
183
184     /**
185      * Unlock this instace
186      * @method unlock
187      */
188     unlock: function() { this.locked = false; },
189
190     /**
191      * By default, all insances can be a drop target.  This can be disabled by
192      * setting isTarget to false.
193      * @method isTarget
194      * @type boolean
195      */
196     isTarget: true,
197
198     /**
199      * The padding configured for this drag and drop object for calculating
200      * the drop zone intersection with this object.
201      * @method padding
202      * @type int[]
203      */
204     padding: null,
205
206     /**
207      * Cached reference to the linked element
208      * @property _domRef
209      * @private
210      */
211     _domRef: null,
212
213     /**
214      * Internal typeof flag
215      * @property __ygDragDrop
216      * @private
217      */
218     __ygDragDrop: true,
219
220     /**
221      * Set to true when horizontal contraints are applied
222      * @property constrainX
223      * @type boolean
224      * @private
225      */
226     constrainX: false,
227
228     /**
229      * Set to true when vertical contraints are applied
230      * @property constrainY
231      * @type boolean
232      * @private
233      */
234     constrainY: false,
235
236     /**
237      * The left constraint
238      * @property minX
239      * @type int
240      * @private
241      */
242     minX: 0,
243
244     /**
245      * The right constraint
246      * @property maxX
247      * @type int
248      * @private
249      */
250     maxX: 0,
251
252     /**
253      * The up constraint
254      * @property minY
255      * @type int
256      * @type int
257      * @private
258      */
259     minY: 0,
260
261     /**
262      * The down constraint
263      * @property maxY
264      * @type int
265      * @private
266      */
267     maxY: 0,
268
269     /**
270      * Maintain offsets when we resetconstraints.  Set to true when you want
271      * the position of the element relative to its parent to stay the same
272      * when the page changes
273      *
274      * @property maintainOffset
275      * @type boolean
276      */
277     maintainOffset: false,
278
279     /**
280      * Array of pixel locations the element will snap to if we specified a
281      * horizontal graduation/interval.  This array is generated automatically
282      * when you define a tick interval.
283      * @property xTicks
284      * @type int[]
285      */
286     xTicks: null,
287
288     /**
289      * Array of pixel locations the element will snap to if we specified a
290      * vertical graduation/interval.  This array is generated automatically
291      * when you define a tick interval.
292      * @property yTicks
293      * @type int[]
294      */
295     yTicks: null,
296
297     /**
298      * By default the drag and drop instance will only respond to the primary
299      * button click (left button for a right-handed mouse).  Set to true to
300      * allow drag and drop to start with any mouse click that is propogated
301      * by the browser
302      * @property primaryButtonOnly
303      * @type boolean
304      */
305     primaryButtonOnly: true,
306
307     /**
308      * The availabe property is false until the linked dom element is accessible.
309      * @property available
310      * @type boolean
311      */
312     available: false,
313
314     /**
315      * By default, drags can only be initiated if the mousedown occurs in the
316      * region the linked element is.  This is done in part to work around a
317      * bug in some browsers that mis-report the mousedown if the previous
318      * mouseup happened outside of the window.  This property is set to true
319      * if outer handles are defined.
320      *
321      * @property hasOuterHandles
322      * @type boolean
323      * @default false
324      */
325     hasOuterHandles: false,
326
327     /**
328      * Code that executes immediately before the startDrag event
329      * @method b4StartDrag
330      * @private
331      */
332     b4StartDrag: function(x, y) { },
333
334     /**
335      * Abstract method called after a drag/drop object is clicked
336      * and the drag or mousedown time thresholds have beeen met.
337      * @method startDrag
338      * @param {int} X click location
339      * @param {int} Y click location
340      */
341     startDrag: function(x, y) { /* override this */ },
342
343     /**
344      * Code that executes immediately before the onDrag event
345      * @method b4Drag
346      * @private
347      */
348     b4Drag: function(e) { },
349
350     /**
351      * Abstract method called during the onMouseMove event while dragging an
352      * object.
353      * @method onDrag
354      * @param {Event} e the mousemove event
355      */
356     onDrag: function(e) { /* override this */ },
357
358     /**
359      * Abstract method called when this element fist begins hovering over
360      * another DragDrop obj
361      * @method onDragEnter
362      * @param {Event} e the mousemove event
363      * @param {String|DragDrop[]} id In POINT mode, the element
364      * id this is hovering over.  In INTERSECT mode, an array of one or more
365      * dragdrop items being hovered over.
366      */
367     onDragEnter: function(e, id) { /* override this */ },
368
369     /**
370      * Code that executes immediately before the onDragOver event
371      * @method b4DragOver
372      * @private
373      */
374     b4DragOver: function(e) { },
375
376     /**
377      * Abstract method called when this element is hovering over another
378      * DragDrop obj
379      * @method onDragOver
380      * @param {Event} e the mousemove event
381      * @param {String|DragDrop[]} id In POINT mode, the element
382      * id this is hovering over.  In INTERSECT mode, an array of dd items
383      * being hovered over.
384      */
385     onDragOver: function(e, id) { /* override this */ },
386
387     /**
388      * Code that executes immediately before the onDragOut event
389      * @method b4DragOut
390      * @private
391      */
392     b4DragOut: function(e) { },
393
394     /**
395      * Abstract method called when we are no longer hovering over an element
396      * @method onDragOut
397      * @param {Event} e the mousemove event
398      * @param {String|DragDrop[]} id In POINT mode, the element
399      * id this was hovering over.  In INTERSECT mode, an array of dd items
400      * that the mouse is no longer over.
401      */
402     onDragOut: function(e, id) { /* override this */ },
403
404     /**
405      * Code that executes immediately before the onDragDrop event
406      * @method b4DragDrop
407      * @private
408      */
409     b4DragDrop: function(e) { },
410
411     /**
412      * Abstract method called when this item is dropped on another DragDrop
413      * obj
414      * @method onDragDrop
415      * @param {Event} e the mouseup event
416      * @param {String|DragDrop[]} id In POINT mode, the element
417      * id this was dropped on.  In INTERSECT mode, an array of dd items this
418      * was dropped on.
419      */
420     onDragDrop: function(e, id) { /* override this */ },
421
422     /**
423      * Abstract method called when this item is dropped on an area with no
424      * drop target
425      * @method onInvalidDrop
426      * @param {Event} e the mouseup event
427      */
428     onInvalidDrop: function(e) { /* override this */ },
429
430     /**
431      * Code that executes immediately before the endDrag event
432      * @method b4EndDrag
433      * @private
434      */
435     b4EndDrag: function(e) { },
436
437     /**
438      * Fired when we are done dragging the object
439      * @method endDrag
440      * @param {Event} e the mouseup event
441      */
442     endDrag: function(e) { /* override this */ },
443
444     /**
445      * Code executed immediately before the onMouseDown event
446      * @method b4MouseDown
447      * @param {Event} e the mousedown event
448      * @private
449      */
450     b4MouseDown: function(e) {  },
451
452     /**
453      * Event handler that fires when a drag/drop obj gets a mousedown
454      * @method onMouseDown
455      * @param {Event} e the mousedown event
456      */
457     onMouseDown: function(e) { /* override this */ },
458
459     /**
460      * Event handler that fires when a drag/drop obj gets a mouseup
461      * @method onMouseUp
462      * @param {Event} e the mouseup event
463      */
464     onMouseUp: function(e) { /* override this */ },
465
466     /**
467      * Override the onAvailable method to do what is needed after the initial
468      * position was determined.
469      * @method onAvailable
470      */
471     onAvailable: function () {
472     },
473
474     /*
475      * Provides default constraint padding to "constrainTo" elements (defaults to {left: 0, right:0, top:0, bottom:0}).
476      * @type Object
477      */
478     defaultPadding : {left:0, right:0, top:0, bottom:0},
479
480     /*
481      * Initializes the drag drop object's constraints to restrict movement to a certain element.
482  *
483  * Usage:
484  <pre><code>
485  var dd = new Roo.dd.DDProxy("dragDiv1", "proxytest",
486                 { dragElId: "existingProxyDiv" });
487  dd.startDrag = function(){
488      this.constrainTo("parent-id");
489  };
490  </code></pre>
491  * Or you can initalize it using the {@link Roo.Element} object:
492  <pre><code>
493  Roo.get("dragDiv1").initDDProxy("proxytest", {dragElId: "existingProxyDiv"}, {
494      startDrag : function(){
495          this.constrainTo("parent-id");
496      }
497  });
498  </code></pre>
499      * @param {String/HTMLElement/Element} constrainTo The element to constrain to.
500      * @param {Object/Number} pad (optional) Pad provides a way to specify "padding" of the constraints,
501      * and can be either a number for symmetrical padding (4 would be equal to {left:4, right:4, top:4, bottom:4}) or
502      * an object containing the sides to pad. For example: {right:10, bottom:10}
503      * @param {Boolean} inContent (optional) Constrain the draggable in the content box of the element (inside padding and borders)
504      */
505     constrainTo : function(constrainTo, pad, inContent){
506         if(typeof pad == "number"){
507             pad = {left: pad, right:pad, top:pad, bottom:pad};
508         }
509         pad = pad || this.defaultPadding;
510         var b = Roo.get(this.getEl()).getBox();
511         var ce = Roo.get(constrainTo);
512         var s = ce.getScroll();
513         var c, cd = ce.dom;
514         if(cd == document.body){
515             c = { x: s.left, y: s.top, width: Roo.lib.Dom.getViewWidth(), height: Roo.lib.Dom.getViewHeight()};
516         }else{
517             xy = ce.getXY();
518             c = {x : xy[0]+s.left, y: xy[1]+s.top, width: cd.clientWidth, height: cd.clientHeight};
519         }
520
521
522         var topSpace = b.y - c.y;
523         var leftSpace = b.x - c.x;
524
525         this.resetConstraints();
526         this.setXConstraint(leftSpace - (pad.left||0), // left
527                 c.width - leftSpace - b.width - (pad.right||0) //right
528         );
529         this.setYConstraint(topSpace - (pad.top||0), //top
530                 c.height - topSpace - b.height - (pad.bottom||0) //bottom
531         );
532     },
533
534     /**
535      * Returns a reference to the linked element
536      * @method getEl
537      * @return {HTMLElement} the html element
538      */
539     getEl: function() {
540         if (!this._domRef) {
541             this._domRef = Roo.getDom(this.id);
542         }
543
544         return this._domRef;
545     },
546
547     /**
548      * Returns a reference to the actual element to drag.  By default this is
549      * the same as the html element, but it can be assigned to another
550      * element. An example of this can be found in Roo.dd.DDProxy
551      * @method getDragEl
552      * @return {HTMLElement} the html element
553      */
554     getDragEl: function() {
555         return Roo.getDom(this.dragElId);
556     },
557
558     /**
559      * Sets up the DragDrop object.  Must be called in the constructor of any
560      * Roo.dd.DragDrop subclass
561      * @method init
562      * @param id the id of the linked element
563      * @param {String} sGroup the group of related items
564      * @param {object} config configuration attributes
565      */
566     init: function(id, sGroup, config) {
567         this.initTarget(id, sGroup, config);
568         Event.on(this.id, "mousedown", this.handleMouseDown, this);
569         // Event.on(this.id, "selectstart", Event.preventDefault);
570     },
571
572     /**
573      * Initializes Targeting functionality only... the object does not
574      * get a mousedown handler.
575      * @method initTarget
576      * @param id the id of the linked element
577      * @param {String} sGroup the group of related items
578      * @param {object} config configuration attributes
579      */
580     initTarget: function(id, sGroup, config) {
581
582         // configuration attributes
583         this.config = config || {};
584
585         // create a local reference to the drag and drop manager
586         this.DDM = Roo.dd.DDM;
587         // initialize the groups array
588         this.groups = {};
589
590         // assume that we have an element reference instead of an id if the
591         // parameter is not a string
592         if (typeof id !== "string") {
593             id = Roo.id(id);
594         }
595
596         // set the id
597         this.id = id;
598
599         // add to an interaction group
600         this.addToGroup((sGroup) ? sGroup : "default");
601
602         // We don't want to register this as the handle with the manager
603         // so we just set the id rather than calling the setter.
604         this.handleElId = id;
605
606         // the linked element is the element that gets dragged by default
607         this.setDragElId(id);
608
609         // by default, clicked anchors will not start drag operations.
610         this.invalidHandleTypes = { A: "A" };
611         this.invalidHandleIds = {};
612         this.invalidHandleClasses = [];
613
614         this.applyConfig();
615
616         this.handleOnAvailable();
617     },
618
619     /**
620      * Applies the configuration parameters that were passed into the constructor.
621      * This is supposed to happen at each level through the inheritance chain.  So
622      * a DDProxy implentation will execute apply config on DDProxy, DD, and
623      * DragDrop in order to get all of the parameters that are available in
624      * each object.
625      * @method applyConfig
626      */
627     applyConfig: function() {
628
629         // configurable properties:
630         //    padding, isTarget, maintainOffset, primaryButtonOnly
631         this.padding           = this.config.padding || [0, 0, 0, 0];
632         this.isTarget          = (this.config.isTarget !== false);
633         this.maintainOffset    = (this.config.maintainOffset);
634         this.primaryButtonOnly = (this.config.primaryButtonOnly !== false);
635
636     },
637
638     /**
639      * Executed when the linked element is available
640      * @method handleOnAvailable
641      * @private
642      */
643     handleOnAvailable: function() {
644         this.available = true;
645         this.resetConstraints();
646         this.onAvailable();
647     },
648
649      /**
650      * Configures the padding for the target zone in px.  Effectively expands
651      * (or reduces) the virtual object size for targeting calculations.
652      * Supports css-style shorthand; if only one parameter is passed, all sides
653      * will have that padding, and if only two are passed, the top and bottom
654      * will have the first param, the left and right the second.
655      * @method setPadding
656      * @param {int} iTop    Top pad
657      * @param {int} iRight  Right pad
658      * @param {int} iBot    Bot pad
659      * @param {int} iLeft   Left pad
660      */
661     setPadding: function(iTop, iRight, iBot, iLeft) {
662         // this.padding = [iLeft, iRight, iTop, iBot];
663         if (!iRight && 0 !== iRight) {
664             this.padding = [iTop, iTop, iTop, iTop];
665         } else if (!iBot && 0 !== iBot) {
666             this.padding = [iTop, iRight, iTop, iRight];
667         } else {
668             this.padding = [iTop, iRight, iBot, iLeft];
669         }
670     },
671
672     /**
673      * Stores the initial placement of the linked element.
674      * @method setInitialPosition
675      * @param {int} diffX   the X offset, default 0
676      * @param {int} diffY   the Y offset, default 0
677      */
678     setInitPosition: function(diffX, diffY) {
679         var el = this.getEl();
680
681         if (!this.DDM.verifyEl(el)) {
682             return;
683         }
684
685         var dx = diffX || 0;
686         var dy = diffY || 0;
687
688         var p = Dom.getXY( el );
689
690         this.initPageX = p[0] - dx;
691         this.initPageY = p[1] - dy;
692
693         this.lastPageX = p[0];
694         this.lastPageY = p[1];
695
696
697         this.setStartPosition(p);
698     },
699
700     /**
701      * Sets the start position of the element.  This is set when the obj
702      * is initialized, the reset when a drag is started.
703      * @method setStartPosition
704      * @param pos current position (from previous lookup)
705      * @private
706      */
707     setStartPosition: function(pos) {
708         var p = pos || Dom.getXY( this.getEl() );
709         this.deltaSetXY = null;
710
711         this.startPageX = p[0];
712         this.startPageY = p[1];
713     },
714
715     /**
716      * Add this instance to a group of related drag/drop objects.  All
717      * instances belong to at least one group, and can belong to as many
718      * groups as needed.
719      * @method addToGroup
720      * @param sGroup {string} the name of the group
721      */
722     addToGroup: function(sGroup) {
723         this.groups[sGroup] = true;
724         this.DDM.regDragDrop(this, sGroup);
725     },
726
727     /**
728      * Remove's this instance from the supplied interaction group
729      * @method removeFromGroup
730      * @param {string}  sGroup  The group to drop
731      */
732     removeFromGroup: function(sGroup) {
733         if (this.groups[sGroup]) {
734             delete this.groups[sGroup];
735         }
736
737         this.DDM.removeDDFromGroup(this, sGroup);
738     },
739
740     /**
741      * Allows you to specify that an element other than the linked element
742      * will be moved with the cursor during a drag
743      * @method setDragElId
744      * @param id {string} the id of the element that will be used to initiate the drag
745      */
746     setDragElId: function(id) {
747         this.dragElId = id;
748     },
749
750     /**
751      * Allows you to specify a child of the linked element that should be
752      * used to initiate the drag operation.  An example of this would be if
753      * you have a content div with text and links.  Clicking anywhere in the
754      * content area would normally start the drag operation.  Use this method
755      * to specify that an element inside of the content div is the element
756      * that starts the drag operation.
757      * @method setHandleElId
758      * @param id {string} the id of the element that will be used to
759      * initiate the drag.
760      */
761     setHandleElId: function(id) {
762         if (typeof id !== "string") {
763             id = Roo.id(id);
764         }
765         this.handleElId = id;
766         this.DDM.regHandle(this.id, id);
767     },
768
769     /**
770      * Allows you to set an element outside of the linked element as a drag
771      * handle
772      * @method setOuterHandleElId
773      * @param id the id of the element that will be used to initiate the drag
774      */
775     setOuterHandleElId: function(id) {
776         if (typeof id !== "string") {
777             id = Roo.id(id);
778         }
779         Event.on(id, "mousedown",
780                 this.handleMouseDown, this);
781         this.setHandleElId(id);
782
783         this.hasOuterHandles = true;
784     },
785
786     /**
787      * Remove all drag and drop hooks for this element
788      * @method unreg
789      */
790     unreg: function() {
791         Event.un(this.id, "mousedown",
792                 this.handleMouseDown);
793         this._domRef = null;
794         this.DDM._remove(this);
795     },
796
797     destroy : function(){
798         this.unreg();
799     },
800
801     /**
802      * Returns true if this instance is locked, or the drag drop mgr is locked
803      * (meaning that all drag/drop is disabled on the page.)
804      * @method isLocked
805      * @return {boolean} true if this obj or all drag/drop is locked, else
806      * false
807      */
808     isLocked: function() {
809         return (this.DDM.isLocked() || this.locked);
810     },
811
812     /**
813      * Fired when this object is clicked
814      * @method handleMouseDown
815      * @param {Event} e
816      * @param {Roo.dd.DragDrop} oDD the clicked dd object (this dd obj)
817      * @private
818      */
819     handleMouseDown: function(e, oDD){
820         if (this.primaryButtonOnly && e.button != 0) {
821             return;
822         }
823
824         if (this.isLocked()) {
825             return;
826         }
827
828         this.DDM.refreshCache(this.groups);
829
830         var pt = new Roo.lib.Point(Roo.lib.Event.getPageX(e), Roo.lib.Event.getPageY(e));
831         if (!this.hasOuterHandles && !this.DDM.isOverTarget(pt, this) )  {
832         } else {
833             if (this.clickValidator(e)) {
834
835                 // set the initial element position
836                 this.setStartPosition();
837
838
839                 this.b4MouseDown(e);
840                 this.onMouseDown(e);
841
842                 this.DDM.handleMouseDown(e, this);
843
844                 this.DDM.stopEvent(e);
845             } else {
846
847
848             }
849         }
850     },
851
852     clickValidator: function(e) {
853         var target = e.getTarget();
854         return ( this.isValidHandleChild(target) &&
855                     (this.id == this.handleElId ||
856                         this.DDM.handleWasClicked(target, this.id)) );
857     },
858
859     /**
860      * Allows you to specify a tag name that should not start a drag operation
861      * when clicked.  This is designed to facilitate embedding links within a
862      * drag handle that do something other than start the drag.
863      * @method addInvalidHandleType
864      * @param {string} tagName the type of element to exclude
865      */
866     addInvalidHandleType: function(tagName) {
867         var type = tagName.toUpperCase();
868         this.invalidHandleTypes[type] = type;
869     },
870
871     /**
872      * Lets you to specify an element id for a child of a drag handle
873      * that should not initiate a drag
874      * @method addInvalidHandleId
875      * @param {string} id the element id of the element you wish to ignore
876      */
877     addInvalidHandleId: function(id) {
878         if (typeof id !== "string") {
879             id = Roo.id(id);
880         }
881         this.invalidHandleIds[id] = id;
882     },
883
884     /**
885      * Lets you specify a css class of elements that will not initiate a drag
886      * @method addInvalidHandleClass
887      * @param {string} cssClass the class of the elements you wish to ignore
888      */
889     addInvalidHandleClass: function(cssClass) {
890         this.invalidHandleClasses.push(cssClass);
891     },
892
893     /**
894      * Unsets an excluded tag name set by addInvalidHandleType
895      * @method removeInvalidHandleType
896      * @param {string} tagName the type of element to unexclude
897      */
898     removeInvalidHandleType: function(tagName) {
899         var type = tagName.toUpperCase();
900         // this.invalidHandleTypes[type] = null;
901         delete this.invalidHandleTypes[type];
902     },
903
904     /**
905      * Unsets an invalid handle id
906      * @method removeInvalidHandleId
907      * @param {string} id the id of the element to re-enable
908      */
909     removeInvalidHandleId: function(id) {
910         if (typeof id !== "string") {
911             id = Roo.id(id);
912         }
913         delete this.invalidHandleIds[id];
914     },
915
916     /**
917      * Unsets an invalid css class
918      * @method removeInvalidHandleClass
919      * @param {string} cssClass the class of the element(s) you wish to
920      * re-enable
921      */
922     removeInvalidHandleClass: function(cssClass) {
923         for (var i=0, len=this.invalidHandleClasses.length; i<len; ++i) {
924             if (this.invalidHandleClasses[i] == cssClass) {
925                 delete this.invalidHandleClasses[i];
926             }
927         }
928     },
929
930     /**
931      * Checks the tag exclusion list to see if this click should be ignored
932      * @method isValidHandleChild
933      * @param {HTMLElement} node the HTMLElement to evaluate
934      * @return {boolean} true if this is a valid tag type, false if not
935      */
936     isValidHandleChild: function(node) {
937
938         var valid = true;
939         // var n = (node.nodeName == "#text") ? node.parentNode : node;
940         var nodeName;
941         try {
942             nodeName = node.nodeName.toUpperCase();
943         } catch(e) {
944             nodeName = node.nodeName;
945         }
946         valid = valid && !this.invalidHandleTypes[nodeName];
947         valid = valid && !this.invalidHandleIds[node.id];
948
949         for (var i=0, len=this.invalidHandleClasses.length; valid && i<len; ++i) {
950             valid = !Dom.hasClass(node, this.invalidHandleClasses[i]);
951         }
952
953
954         return valid;
955
956     },
957
958     /**
959      * Create the array of horizontal tick marks if an interval was specified
960      * in setXConstraint().
961      * @method setXTicks
962      * @private
963      */
964     setXTicks: function(iStartX, iTickSize) {
965         this.xTicks = [];
966         this.xTickSize = iTickSize;
967
968         var tickMap = {};
969
970         for (var i = this.initPageX; i >= this.minX; i = i - iTickSize) {
971             if (!tickMap[i]) {
972                 this.xTicks[this.xTicks.length] = i;
973                 tickMap[i] = true;
974             }
975         }
976
977         for (i = this.initPageX; i <= this.maxX; i = i + iTickSize) {
978             if (!tickMap[i]) {
979                 this.xTicks[this.xTicks.length] = i;
980                 tickMap[i] = true;
981             }
982         }
983
984         this.xTicks.sort(this.DDM.numericSort) ;
985     },
986
987     /**
988      * Create the array of vertical tick marks if an interval was specified in
989      * setYConstraint().
990      * @method setYTicks
991      * @private
992      */
993     setYTicks: function(iStartY, iTickSize) {
994         this.yTicks = [];
995         this.yTickSize = iTickSize;
996
997         var tickMap = {};
998
999         for (var i = this.initPageY; i >= this.minY; i = i - iTickSize) {
1000             if (!tickMap[i]) {
1001                 this.yTicks[this.yTicks.length] = i;
1002                 tickMap[i] = true;
1003             }
1004         }
1005
1006         for (i = this.initPageY; i <= this.maxY; i = i + iTickSize) {
1007             if (!tickMap[i]) {
1008                 this.yTicks[this.yTicks.length] = i;
1009                 tickMap[i] = true;
1010             }
1011         }
1012
1013         this.yTicks.sort(this.DDM.numericSort) ;
1014     },
1015
1016     /**
1017      * By default, the element can be dragged any place on the screen.  Use
1018      * this method to limit the horizontal travel of the element.  Pass in
1019      * 0,0 for the parameters if you want to lock the drag to the y axis.
1020      * @method setXConstraint
1021      * @param {int} iLeft the number of pixels the element can move to the left
1022      * @param {int} iRight the number of pixels the element can move to the
1023      * right
1024      * @param {int} iTickSize optional parameter for specifying that the
1025      * element
1026      * should move iTickSize pixels at a time.
1027      */
1028     setXConstraint: function(iLeft, iRight, iTickSize) {
1029         this.leftConstraint = iLeft;
1030         this.rightConstraint = iRight;
1031
1032         this.minX = this.initPageX - iLeft;
1033         this.maxX = this.initPageX + iRight;
1034         if (iTickSize) { this.setXTicks(this.initPageX, iTickSize); }
1035
1036         this.constrainX = true;
1037     },
1038
1039     /**
1040      * Clears any constraints applied to this instance.  Also clears ticks
1041      * since they can't exist independent of a constraint at this time.
1042      * @method clearConstraints
1043      */
1044     clearConstraints: function() {
1045         this.constrainX = false;
1046         this.constrainY = false;
1047         this.clearTicks();
1048     },
1049
1050     /**
1051      * Clears any tick interval defined for this instance
1052      * @method clearTicks
1053      */
1054     clearTicks: function() {
1055         this.xTicks = null;
1056         this.yTicks = null;
1057         this.xTickSize = 0;
1058         this.yTickSize = 0;
1059     },
1060
1061     /**
1062      * By default, the element can be dragged any place on the screen.  Set
1063      * this to limit the vertical travel of the element.  Pass in 0,0 for the
1064      * parameters if you want to lock the drag to the x axis.
1065      * @method setYConstraint
1066      * @param {int} iUp the number of pixels the element can move up
1067      * @param {int} iDown the number of pixels the element can move down
1068      * @param {int} iTickSize optional parameter for specifying that the
1069      * element should move iTickSize pixels at a time.
1070      */
1071     setYConstraint: function(iUp, iDown, iTickSize) {
1072         this.topConstraint = iUp;
1073         this.bottomConstraint = iDown;
1074
1075         this.minY = this.initPageY - iUp;
1076         this.maxY = this.initPageY + iDown;
1077         if (iTickSize) { this.setYTicks(this.initPageY, iTickSize); }
1078
1079         this.constrainY = true;
1080
1081     },
1082
1083     /**
1084      * resetConstraints must be called if you manually reposition a dd element.
1085      * @method resetConstraints
1086      * @param {boolean} maintainOffset
1087      */
1088     resetConstraints: function() {
1089
1090
1091         // Maintain offsets if necessary
1092         if (this.initPageX || this.initPageX === 0) {
1093             // figure out how much this thing has moved
1094             var dx = (this.maintainOffset) ? this.lastPageX - this.initPageX : 0;
1095             var dy = (this.maintainOffset) ? this.lastPageY - this.initPageY : 0;
1096
1097             this.setInitPosition(dx, dy);
1098
1099         // This is the first time we have detected the element's position
1100         } else {
1101             this.setInitPosition();
1102         }
1103
1104         if (this.constrainX) {
1105             this.setXConstraint( this.leftConstraint,
1106                                  this.rightConstraint,
1107                                  this.xTickSize        );
1108         }
1109
1110         if (this.constrainY) {
1111             this.setYConstraint( this.topConstraint,
1112                                  this.bottomConstraint,
1113                                  this.yTickSize         );
1114         }
1115     },
1116
1117     /**
1118      * Normally the drag element is moved pixel by pixel, but we can specify
1119      * that it move a number of pixels at a time.  This method resolves the
1120      * location when we have it set up like this.
1121      * @method getTick
1122      * @param {int} val where we want to place the object
1123      * @param {int[]} tickArray sorted array of valid points
1124      * @return {int} the closest tick
1125      * @private
1126      */
1127     getTick: function(val, tickArray) {
1128
1129         if (!tickArray) {
1130             // If tick interval is not defined, it is effectively 1 pixel,
1131             // so we return the value passed to us.
1132             return val;
1133         } else if (tickArray[0] >= val) {
1134             // The value is lower than the first tick, so we return the first
1135             // tick.
1136             return tickArray[0];
1137         } else {
1138             for (var i=0, len=tickArray.length; i<len; ++i) {
1139                 var next = i + 1;
1140                 if (tickArray[next] && tickArray[next] >= val) {
1141                     var diff1 = val - tickArray[i];
1142                     var diff2 = tickArray[next] - val;
1143                     return (diff2 > diff1) ? tickArray[i] : tickArray[next];
1144                 }
1145             }
1146
1147             // The value is larger than the last tick, so we return the last
1148             // tick.
1149             return tickArray[tickArray.length - 1];
1150         }
1151     },
1152
1153     /**
1154      * toString method
1155      * @method toString
1156      * @return {string} string representation of the dd obj
1157      */
1158     toString: function() {
1159         return ("DragDrop " + this.id);
1160     }
1161
1162 });
1163
1164 })();
1165 /*
1166  * Based on:
1167  * Ext JS Library 1.1.1
1168  * Copyright(c) 2006-2007, Ext JS, LLC.
1169  *
1170  * Originally Released Under LGPL - original licence link has changed is not relivant.
1171  *
1172  * Fork - LGPL
1173  * <script type="text/javascript">
1174  */
1175
1176
1177 /**
1178  * The drag and drop utility provides a framework for building drag and drop
1179  * applications.  In addition to enabling drag and drop for specific elements,
1180  * the drag and drop elements are tracked by the manager class, and the
1181  * interactions between the various elements are tracked during the drag and
1182  * the implementing code is notified about these important moments.
1183  */
1184
1185 // Only load the library once.  Rewriting the manager class would orphan
1186 // existing drag and drop instances.
1187 if (!Roo.dd.DragDropMgr) {
1188
1189 /**
1190  * @class Roo.dd.DragDropMgr
1191  * DragDropMgr is a singleton that tracks the element interaction for
1192  * all DragDrop items in the window.  Generally, you will not call
1193  * this class directly, but it does have helper methods that could
1194  * be useful in your DragDrop implementations.
1195  * @singleton
1196  */
1197 Roo.dd.DragDropMgr = function() {
1198
1199     var Event = Roo.EventManager;
1200
1201     return {
1202
1203         /**
1204          * Two dimensional Array of registered DragDrop objects.  The first
1205          * dimension is the DragDrop item group, the second the DragDrop
1206          * object.
1207          * @property ids
1208          * @type {string: string}
1209          * @private
1210          * @static
1211          */
1212         ids: {},
1213
1214         /**
1215          * Array of element ids defined as drag handles.  Used to determine
1216          * if the element that generated the mousedown event is actually the
1217          * handle and not the html element itself.
1218          * @property handleIds
1219          * @type {string: string}
1220          * @private
1221          * @static
1222          */
1223         handleIds: {},
1224
1225         /**
1226          * the DragDrop object that is currently being dragged
1227          * @property dragCurrent
1228          * @type DragDrop
1229          * @private
1230          * @static
1231          **/
1232         dragCurrent: null,
1233
1234         /**
1235          * the DragDrop object(s) that are being hovered over
1236          * @property dragOvers
1237          * @type Array
1238          * @private
1239          * @static
1240          */
1241         dragOvers: {},
1242
1243         /**
1244          * the X distance between the cursor and the object being dragged
1245          * @property deltaX
1246          * @type int
1247          * @private
1248          * @static
1249          */
1250         deltaX: 0,
1251
1252         /**
1253          * the Y distance between the cursor and the object being dragged
1254          * @property deltaY
1255          * @type int
1256          * @private
1257          * @static
1258          */
1259         deltaY: 0,
1260
1261         /**
1262          * Flag to determine if we should prevent the default behavior of the
1263          * events we define. By default this is true, but this can be set to
1264          * false if you need the default behavior (not recommended)
1265          * @property preventDefault
1266          * @type boolean
1267          * @static
1268          */
1269         preventDefault: true,
1270
1271         /**
1272          * Flag to determine if we should stop the propagation of the events
1273          * we generate. This is true by default but you may want to set it to
1274          * false if the html element contains other features that require the
1275          * mouse click.
1276          * @property stopPropagation
1277          * @type boolean
1278          * @static
1279          */
1280         stopPropagation: true,
1281
1282         /**
1283          * Internal flag that is set to true when drag and drop has been
1284          * intialized
1285          * @property initialized
1286          * @private
1287          * @static
1288          */
1289         initalized: false,
1290
1291         /**
1292          * All drag and drop can be disabled.
1293          * @property locked
1294          * @private
1295          * @static
1296          */
1297         locked: false,
1298
1299         /**
1300          * Called the first time an element is registered.
1301          * @method init
1302          * @private
1303          * @static
1304          */
1305         init: function() {
1306             this.initialized = true;
1307         },
1308
1309         /**
1310          * In point mode, drag and drop interaction is defined by the
1311          * location of the cursor during the drag/drop
1312          * @property POINT
1313          * @type int
1314          * @static
1315          */
1316         POINT: 0,
1317
1318         /**
1319          * In intersect mode, drag and drop interactio nis defined by the
1320          * overlap of two or more drag and drop objects.
1321          * @property INTERSECT
1322          * @type int
1323          * @static
1324          */
1325         INTERSECT: 1,
1326
1327         /**
1328          * The current drag and drop mode.  Default: POINT
1329          * @property mode
1330          * @type int
1331          * @static
1332          */
1333         mode: 0,
1334
1335         /**
1336          * Runs method on all drag and drop objects
1337          * @method _execOnAll
1338          * @private
1339          * @static
1340          */
1341         _execOnAll: function(sMethod, args) {
1342             for (var i in this.ids) {
1343                 for (var j in this.ids[i]) {
1344                     var oDD = this.ids[i][j];
1345                     if (! this.isTypeOfDD(oDD)) {
1346                         continue;
1347                     }
1348                     oDD[sMethod].apply(oDD, args);
1349                 }
1350             }
1351         },
1352
1353         /**
1354          * Drag and drop initialization.  Sets up the global event handlers
1355          * @method _onLoad
1356          * @private
1357          * @static
1358          */
1359         _onLoad: function() {
1360
1361             this.init();
1362
1363
1364             Event.on(document, "mouseup",   this.handleMouseUp, this, true);
1365             Event.on(document, "mousemove", this.handleMouseMove, this, true);
1366             Event.on(window,   "unload",    this._onUnload, this, true);
1367             Event.on(window,   "resize",    this._onResize, this, true);
1368             // Event.on(window,   "mouseout",    this._test);
1369
1370         },
1371
1372         /**
1373          * Reset constraints on all drag and drop objs
1374          * @method _onResize
1375          * @private
1376          * @static
1377          */
1378         _onResize: function(e) {
1379             this._execOnAll("resetConstraints", []);
1380         },
1381
1382         /**
1383          * Lock all drag and drop functionality
1384          * @method lock
1385          * @static
1386          */
1387         lock: function() { this.locked = true; },
1388
1389         /**
1390          * Unlock all drag and drop functionality
1391          * @method unlock
1392          * @static
1393          */
1394         unlock: function() { this.locked = false; },
1395
1396         /**
1397          * Is drag and drop locked?
1398          * @method isLocked
1399          * @return {boolean} True if drag and drop is locked, false otherwise.
1400          * @static
1401          */
1402         isLocked: function() { return this.locked; },
1403
1404         /**
1405          * Location cache that is set for all drag drop objects when a drag is
1406          * initiated, cleared when the drag is finished.
1407          * @property locationCache
1408          * @private
1409          * @static
1410          */
1411         locationCache: {},
1412
1413         /**
1414          * Set useCache to false if you want to force object the lookup of each
1415          * drag and drop linked element constantly during a drag.
1416          * @property useCache
1417          * @type boolean
1418          * @static
1419          */
1420         useCache: true,
1421
1422         /**
1423          * The number of pixels that the mouse needs to move after the
1424          * mousedown before the drag is initiated.  Default=3;
1425          * @property clickPixelThresh
1426          * @type int
1427          * @static
1428          */
1429         clickPixelThresh: 3,
1430
1431         /**
1432          * The number of milliseconds after the mousedown event to initiate the
1433          * drag if we don't get a mouseup event. Default=1000
1434          * @property clickTimeThresh
1435          * @type int
1436          * @static
1437          */
1438         clickTimeThresh: 350,
1439
1440         /**
1441          * Flag that indicates that either the drag pixel threshold or the
1442          * mousdown time threshold has been met
1443          * @property dragThreshMet
1444          * @type boolean
1445          * @private
1446          * @static
1447          */
1448         dragThreshMet: false,
1449
1450         /**
1451          * Timeout used for the click time threshold
1452          * @property clickTimeout
1453          * @type Object
1454          * @private
1455          * @static
1456          */
1457         clickTimeout: null,
1458
1459         /**
1460          * The X position of the mousedown event stored for later use when a
1461          * drag threshold is met.
1462          * @property startX
1463          * @type int
1464          * @private
1465          * @static
1466          */
1467         startX: 0,
1468
1469         /**
1470          * The Y position of the mousedown event stored for later use when a
1471          * drag threshold is met.
1472          * @property startY
1473          * @type int
1474          * @private
1475          * @static
1476          */
1477         startY: 0,
1478
1479         /**
1480          * Each DragDrop instance must be registered with the DragDropMgr.
1481          * This is executed in DragDrop.init()
1482          * @method regDragDrop
1483          * @param {DragDrop} oDD the DragDrop object to register
1484          * @param {String} sGroup the name of the group this element belongs to
1485          * @static
1486          */
1487         regDragDrop: function(oDD, sGroup) {
1488             if (!this.initialized) { this.init(); }
1489
1490             if (!this.ids[sGroup]) {
1491                 this.ids[sGroup] = {};
1492             }
1493             this.ids[sGroup][oDD.id] = oDD;
1494         },
1495
1496         /**
1497          * Removes the supplied dd instance from the supplied group. Executed
1498          * by DragDrop.removeFromGroup, so don't call this function directly.
1499          * @method removeDDFromGroup
1500          * @private
1501          * @static
1502          */
1503         removeDDFromGroup: function(oDD, sGroup) {
1504             if (!this.ids[sGroup]) {
1505                 this.ids[sGroup] = {};
1506             }
1507
1508             var obj = this.ids[sGroup];
1509             if (obj && obj[oDD.id]) {
1510                 delete obj[oDD.id];
1511             }
1512         },
1513
1514         /**
1515          * Unregisters a drag and drop item.  This is executed in
1516          * DragDrop.unreg, use that method instead of calling this directly.
1517          * @method _remove
1518          * @private
1519          * @static
1520          */
1521         _remove: function(oDD) {
1522             for (var g in oDD.groups) {
1523                 if (g && this.ids[g][oDD.id]) {
1524                     delete this.ids[g][oDD.id];
1525                 }
1526             }
1527             delete this.handleIds[oDD.id];
1528         },
1529
1530         /**
1531          * Each DragDrop handle element must be registered.  This is done
1532          * automatically when executing DragDrop.setHandleElId()
1533          * @method regHandle
1534          * @param {String} sDDId the DragDrop id this element is a handle for
1535          * @param {String} sHandleId the id of the element that is the drag
1536          * handle
1537          * @static
1538          */
1539         regHandle: function(sDDId, sHandleId) {
1540             if (!this.handleIds[sDDId]) {
1541                 this.handleIds[sDDId] = {};
1542             }
1543             this.handleIds[sDDId][sHandleId] = sHandleId;
1544         },
1545
1546         /**
1547          * Utility function to determine if a given element has been
1548          * registered as a drag drop item.
1549          * @method isDragDrop
1550          * @param {String} id the element id to check
1551          * @return {boolean} true if this element is a DragDrop item,
1552          * false otherwise
1553          * @static
1554          */
1555         isDragDrop: function(id) {
1556             return ( this.getDDById(id) ) ? true : false;
1557         },
1558
1559         /**
1560          * Returns the drag and drop instances that are in all groups the
1561          * passed in instance belongs to.
1562          * @method getRelated
1563          * @param {DragDrop} p_oDD the obj to get related data for
1564          * @param {boolean} bTargetsOnly if true, only return targetable objs
1565          * @return {DragDrop[]} the related instances
1566          * @static
1567          */
1568         getRelated: function(p_oDD, bTargetsOnly) {
1569             var oDDs = [];
1570             for (var i in p_oDD.groups) {
1571                 for (j in this.ids[i]) {
1572                     var dd = this.ids[i][j];
1573                     if (! this.isTypeOfDD(dd)) {
1574                         continue;
1575                     }
1576                     if (!bTargetsOnly || dd.isTarget) {
1577                         oDDs[oDDs.length] = dd;
1578                     }
1579                 }
1580             }
1581
1582             return oDDs;
1583         },
1584
1585         /**
1586          * Returns true if the specified dd target is a legal target for
1587          * the specifice drag obj
1588          * @method isLegalTarget
1589          * @param {DragDrop} the drag obj
1590          * @param {DragDrop} the target
1591          * @return {boolean} true if the target is a legal target for the
1592          * dd obj
1593          * @static
1594          */
1595         isLegalTarget: function (oDD, oTargetDD) {
1596             var targets = this.getRelated(oDD, true);
1597             for (var i=0, len=targets.length;i<len;++i) {
1598                 if (targets[i].id == oTargetDD.id) {
1599                     return true;
1600                 }
1601             }
1602
1603             return false;
1604         },
1605
1606         /**
1607          * My goal is to be able to transparently determine if an object is
1608          * typeof DragDrop, and the exact subclass of DragDrop.  typeof
1609          * returns "object", oDD.constructor.toString() always returns
1610          * "DragDrop" and not the name of the subclass.  So for now it just
1611          * evaluates a well-known variable in DragDrop.
1612          * @method isTypeOfDD
1613          * @param {Object} the object to evaluate
1614          * @return {boolean} true if typeof oDD = DragDrop
1615          * @static
1616          */
1617         isTypeOfDD: function (oDD) {
1618             return (oDD && oDD.__ygDragDrop);
1619         },
1620
1621         /**
1622          * Utility function to determine if a given element has been
1623          * registered as a drag drop handle for the given Drag Drop object.
1624          * @method isHandle
1625          * @param {String} id the element id to check
1626          * @return {boolean} true if this element is a DragDrop handle, false
1627          * otherwise
1628          * @static
1629          */
1630         isHandle: function(sDDId, sHandleId) {
1631             return ( this.handleIds[sDDId] &&
1632                             this.handleIds[sDDId][sHandleId] );
1633         },
1634
1635         /**
1636          * Returns the DragDrop instance for a given id
1637          * @method getDDById
1638          * @param {String} id the id of the DragDrop object
1639          * @return {DragDrop} the drag drop object, null if it is not found
1640          * @static
1641          */
1642         getDDById: function(id) {
1643             for (var i in this.ids) {
1644                 if (this.ids[i][id]) {
1645                     return this.ids[i][id];
1646                 }
1647             }
1648             return null;
1649         },
1650
1651         /**
1652          * Fired after a registered DragDrop object gets the mousedown event.
1653          * Sets up the events required to track the object being dragged
1654          * @method handleMouseDown
1655          * @param {Event} e the event
1656          * @param oDD the DragDrop object being dragged
1657          * @private
1658          * @static
1659          */
1660         handleMouseDown: function(e, oDD) {
1661             if(Roo.QuickTips){
1662                 Roo.QuickTips.disable();
1663             }
1664             this.currentTarget = e.getTarget();
1665
1666             this.dragCurrent = oDD;
1667
1668             var el = oDD.getEl();
1669
1670             // track start position
1671             this.startX = e.getPageX();
1672             this.startY = e.getPageY();
1673
1674             this.deltaX = this.startX - el.offsetLeft;
1675             this.deltaY = this.startY - el.offsetTop;
1676
1677             this.dragThreshMet = false;
1678
1679             this.clickTimeout = setTimeout(
1680                     function() {
1681                         var DDM = Roo.dd.DDM;
1682                         DDM.startDrag(DDM.startX, DDM.startY);
1683                     },
1684                     this.clickTimeThresh );
1685         },
1686
1687         /**
1688          * Fired when either the drag pixel threshol or the mousedown hold
1689          * time threshold has been met.
1690          * @method startDrag
1691          * @param x {int} the X position of the original mousedown
1692          * @param y {int} the Y position of the original mousedown
1693          * @static
1694          */
1695         startDrag: function(x, y) {
1696             clearTimeout(this.clickTimeout);
1697             if (this.dragCurrent) {
1698                 this.dragCurrent.b4StartDrag(x, y);
1699                 this.dragCurrent.startDrag(x, y);
1700             }
1701             this.dragThreshMet = true;
1702         },
1703
1704         /**
1705          * Internal function to handle the mouseup event.  Will be invoked
1706          * from the context of the document.
1707          * @method handleMouseUp
1708          * @param {Event} e the event
1709          * @private
1710          * @static
1711          */
1712         handleMouseUp: function(e) {
1713
1714             if(Roo.QuickTips){
1715                 Roo.QuickTips.enable();
1716             }
1717             if (! this.dragCurrent) {
1718                 return;
1719             }
1720
1721             clearTimeout(this.clickTimeout);
1722
1723             if (this.dragThreshMet) {
1724                 this.fireEvents(e, true);
1725             } else {
1726             }
1727
1728             this.stopDrag(e);
1729
1730             this.stopEvent(e);
1731         },
1732
1733         /**
1734          * Utility to stop event propagation and event default, if these
1735          * features are turned on.
1736          * @method stopEvent
1737          * @param {Event} e the event as returned by this.getEvent()
1738          * @static
1739          */
1740         stopEvent: function(e){
1741             if(this.stopPropagation) {
1742                 e.stopPropagation();
1743             }
1744
1745             if (this.preventDefault) {
1746                 e.preventDefault();
1747             }
1748         },
1749
1750         /**
1751          * Internal function to clean up event handlers after the drag
1752          * operation is complete
1753          * @method stopDrag
1754          * @param {Event} e the event
1755          * @private
1756          * @static
1757          */
1758         stopDrag: function(e) {
1759             // Fire the drag end event for the item that was dragged
1760             if (this.dragCurrent) {
1761                 if (this.dragThreshMet) {
1762                     this.dragCurrent.b4EndDrag(e);
1763                     this.dragCurrent.endDrag(e);
1764                 }
1765
1766                 this.dragCurrent.onMouseUp(e);
1767             }
1768
1769             this.dragCurrent = null;
1770             this.dragOvers = {};
1771         },
1772
1773         /**
1774          * Internal function to handle the mousemove event.  Will be invoked
1775          * from the context of the html element.
1776          *
1777          * @TODO figure out what we can do about mouse events lost when the
1778          * user drags objects beyond the window boundary.  Currently we can
1779          * detect this in internet explorer by verifying that the mouse is
1780          * down during the mousemove event.  Firefox doesn't give us the
1781          * button state on the mousemove event.
1782          * @method handleMouseMove
1783          * @param {Event} e the event
1784          * @private
1785          * @static
1786          */
1787         handleMouseMove: function(e) {
1788             if (! this.dragCurrent) {
1789                 return true;
1790             }
1791
1792             // var button = e.which || e.button;
1793
1794             // check for IE mouseup outside of page boundary
1795             if (Roo.isIE && (e.button !== 0 && e.button !== 1 && e.button !== 2)) {
1796                 this.stopEvent(e);
1797                 return this.handleMouseUp(e);
1798             }
1799
1800             if (!this.dragThreshMet) {
1801                 var diffX = Math.abs(this.startX - e.getPageX());
1802                 var diffY = Math.abs(this.startY - e.getPageY());
1803                 if (diffX > this.clickPixelThresh ||
1804                             diffY > this.clickPixelThresh) {
1805                     this.startDrag(this.startX, this.startY);
1806                 }
1807             }
1808
1809             if (this.dragThreshMet) {
1810                 this.dragCurrent.b4Drag(e);
1811                 this.dragCurrent.onDrag(e);
1812                 if(!this.dragCurrent.moveOnly){
1813                     this.fireEvents(e, false);
1814                 }
1815             }
1816
1817             this.stopEvent(e);
1818
1819             return true;
1820         },
1821
1822         /**
1823          * Iterates over all of the DragDrop elements to find ones we are
1824          * hovering over or dropping on
1825          * @method fireEvents
1826          * @param {Event} e the event
1827          * @param {boolean} isDrop is this a drop op or a mouseover op?
1828          * @private
1829          * @static
1830          */
1831         fireEvents: function(e, isDrop) {
1832             var dc = this.dragCurrent;
1833
1834             // If the user did the mouse up outside of the window, we could
1835             // get here even though we have ended the drag.
1836             if (!dc || dc.isLocked()) {
1837                 return;
1838             }
1839
1840             var pt = e.getPoint();
1841
1842             // cache the previous dragOver array
1843             var oldOvers = [];
1844
1845             var outEvts   = [];
1846             var overEvts  = [];
1847             var dropEvts  = [];
1848             var enterEvts = [];
1849
1850             // Check to see if the object(s) we were hovering over is no longer
1851             // being hovered over so we can fire the onDragOut event
1852             for (var i in this.dragOvers) {
1853
1854                 var ddo = this.dragOvers[i];
1855
1856                 if (! this.isTypeOfDD(ddo)) {
1857                     continue;
1858                 }
1859
1860                 if (! this.isOverTarget(pt, ddo, this.mode)) {
1861                     outEvts.push( ddo );
1862                 }
1863
1864                 oldOvers[i] = true;
1865                 delete this.dragOvers[i];
1866             }
1867
1868             for (var sGroup in dc.groups) {
1869
1870                 if ("string" != typeof sGroup) {
1871                     continue;
1872                 }
1873
1874                 for (i in this.ids[sGroup]) {
1875                     var oDD = this.ids[sGroup][i];
1876                     if (! this.isTypeOfDD(oDD)) {
1877                         continue;
1878                     }
1879
1880                     if (oDD.isTarget && !oDD.isLocked() && oDD != dc) {
1881                         if (this.isOverTarget(pt, oDD, this.mode)) {
1882                             // look for drop interactions
1883                             if (isDrop) {
1884                                 dropEvts.push( oDD );
1885                             // look for drag enter and drag over interactions
1886                             } else {
1887
1888                                 // initial drag over: dragEnter fires
1889                                 if (!oldOvers[oDD.id]) {
1890                                     enterEvts.push( oDD );
1891                                 // subsequent drag overs: dragOver fires
1892                                 } else {
1893                                     overEvts.push( oDD );
1894                                 }
1895
1896                                 this.dragOvers[oDD.id] = oDD;
1897                             }
1898                         }
1899                     }
1900                 }
1901             }
1902
1903             if (this.mode) {
1904                 if (outEvts.length) {
1905                     dc.b4DragOut(e, outEvts);
1906                     dc.onDragOut(e, outEvts);
1907                 }
1908
1909                 if (enterEvts.length) {
1910                     dc.onDragEnter(e, enterEvts);
1911                 }
1912
1913                 if (overEvts.length) {
1914                     dc.b4DragOver(e, overEvts);
1915                     dc.onDragOver(e, overEvts);
1916                 }
1917
1918                 if (dropEvts.length) {
1919                     dc.b4DragDrop(e, dropEvts);
1920                     dc.onDragDrop(e, dropEvts);
1921                 }
1922
1923             } else {
1924                 // fire dragout events
1925                 var len = 0;
1926                 for (i=0, len=outEvts.length; i<len; ++i) {
1927                     dc.b4DragOut(e, outEvts[i].id);
1928                     dc.onDragOut(e, outEvts[i].id);
1929                 }
1930
1931                 // fire enter events
1932                 for (i=0,len=enterEvts.length; i<len; ++i) {
1933                     // dc.b4DragEnter(e, oDD.id);
1934                     dc.onDragEnter(e, enterEvts[i].id);
1935                 }
1936
1937                 // fire over events
1938                 for (i=0,len=overEvts.length; i<len; ++i) {
1939                     dc.b4DragOver(e, overEvts[i].id);
1940                     dc.onDragOver(e, overEvts[i].id);
1941                 }
1942
1943                 // fire drop events
1944                 for (i=0, len=dropEvts.length; i<len; ++i) {
1945                     dc.b4DragDrop(e, dropEvts[i].id);
1946                     dc.onDragDrop(e, dropEvts[i].id);
1947                 }
1948
1949             }
1950
1951             // notify about a drop that did not find a target
1952             if (isDrop && !dropEvts.length) {
1953                 dc.onInvalidDrop(e);
1954             }
1955
1956         },
1957
1958         /**
1959          * Helper function for getting the best match from the list of drag
1960          * and drop objects returned by the drag and drop events when we are
1961          * in INTERSECT mode.  It returns either the first object that the
1962          * cursor is over, or the object that has the greatest overlap with
1963          * the dragged element.
1964          * @method getBestMatch
1965          * @param  {DragDrop[]} dds The array of drag and drop objects
1966          * targeted
1967          * @return {DragDrop}       The best single match
1968          * @static
1969          */
1970         getBestMatch: function(dds) {
1971             var winner = null;
1972             // Return null if the input is not what we expect
1973             //if (!dds || !dds.length || dds.length == 0) {
1974                // winner = null;
1975             // If there is only one item, it wins
1976             //} else if (dds.length == 1) {
1977
1978             var len = dds.length;
1979
1980             if (len == 1) {
1981                 winner = dds[0];
1982             } else {
1983                 // Loop through the targeted items
1984                 for (var i=0; i<len; ++i) {
1985                     var dd = dds[i];
1986                     // If the cursor is over the object, it wins.  If the
1987                     // cursor is over multiple matches, the first one we come
1988                     // to wins.
1989                     if (dd.cursorIsOver) {
1990                         winner = dd;
1991                         break;
1992                     // Otherwise the object with the most overlap wins
1993                     } else {
1994                         if (!winner ||
1995                             winner.overlap.getArea() < dd.overlap.getArea()) {
1996                             winner = dd;
1997                         }
1998                     }
1999                 }
2000             }
2001
2002             return winner;
2003         },
2004
2005         /**
2006          * Refreshes the cache of the top-left and bottom-right points of the
2007          * drag and drop objects in the specified group(s).  This is in the
2008          * format that is stored in the drag and drop instance, so typical
2009          * usage is:
2010          * <code>
2011          * Roo.dd.DragDropMgr.refreshCache(ddinstance.groups);
2012          * </code>
2013          * Alternatively:
2014          * <code>
2015          * Roo.dd.DragDropMgr.refreshCache({group1:true, group2:true});
2016          * </code>
2017          * @TODO this really should be an indexed array.  Alternatively this
2018          * method could accept both.
2019          * @method refreshCache
2020          * @param {Object} groups an associative array of groups to refresh
2021          * @static
2022          */
2023         refreshCache: function(groups) {
2024             for (var sGroup in groups) {
2025                 if ("string" != typeof sGroup) {
2026                     continue;
2027                 }
2028                 for (var i in this.ids[sGroup]) {
2029                     var oDD = this.ids[sGroup][i];
2030
2031                     if (this.isTypeOfDD(oDD)) {
2032                     // if (this.isTypeOfDD(oDD) && oDD.isTarget) {
2033                         var loc = this.getLocation(oDD);
2034                         if (loc) {
2035                             this.locationCache[oDD.id] = loc;
2036                         } else {
2037                             delete this.locationCache[oDD.id];
2038                             // this will unregister the drag and drop object if
2039                             // the element is not in a usable state
2040                             // oDD.unreg();
2041                         }
2042                     }
2043                 }
2044             }
2045         },
2046
2047         /**
2048          * This checks to make sure an element exists and is in the DOM.  The
2049          * main purpose is to handle cases where innerHTML is used to remove
2050          * drag and drop objects from the DOM.  IE provides an 'unspecified
2051          * error' when trying to access the offsetParent of such an element
2052          * @method verifyEl
2053          * @param {HTMLElement} el the element to check
2054          * @return {boolean} true if the element looks usable
2055          * @static
2056          */
2057         verifyEl: function(el) {
2058             if (el) {
2059                 var parent;
2060                 if(Roo.isIE){
2061                     try{
2062                         parent = el.offsetParent;
2063                     }catch(e){}
2064                 }else{
2065                     parent = el.offsetParent;
2066                 }
2067                 if (parent) {
2068                     return true;
2069                 }
2070             }
2071
2072             return false;
2073         },
2074
2075         /**
2076          * Returns a Region object containing the drag and drop element's position
2077          * and size, including the padding configured for it
2078          * @method getLocation
2079          * @param {DragDrop} oDD the drag and drop object to get the
2080          *                       location for
2081          * @return {Roo.lib.Region} a Region object representing the total area
2082          *                             the element occupies, including any padding
2083          *                             the instance is configured for.
2084          * @static
2085          */
2086         getLocation: function(oDD) {
2087             if (! this.isTypeOfDD(oDD)) {
2088                 return null;
2089             }
2090
2091             var el = oDD.getEl(), pos, x1, x2, y1, y2, t, r, b, l;
2092
2093             try {
2094                 pos= Roo.lib.Dom.getXY(el);
2095             } catch (e) { }
2096
2097             if (!pos) {
2098                 return null;
2099             }
2100
2101             x1 = pos[0];
2102             x2 = x1 + el.offsetWidth;
2103             y1 = pos[1];
2104             y2 = y1 + el.offsetHeight;
2105
2106             t = y1 - oDD.padding[0];
2107             r = x2 + oDD.padding[1];
2108             b = y2 + oDD.padding[2];
2109             l = x1 - oDD.padding[3];
2110
2111             return new Roo.lib.Region( t, r, b, l );
2112         },
2113
2114         /**
2115          * Checks the cursor location to see if it over the target
2116          * @method isOverTarget
2117          * @param {Roo.lib.Point} pt The point to evaluate
2118          * @param {DragDrop} oTarget the DragDrop object we are inspecting
2119          * @return {boolean} true if the mouse is over the target
2120          * @private
2121          * @static
2122          */
2123         isOverTarget: function(pt, oTarget, intersect) {
2124             // use cache if available
2125             var loc = this.locationCache[oTarget.id];
2126             if (!loc || !this.useCache) {
2127                 loc = this.getLocation(oTarget);
2128                 this.locationCache[oTarget.id] = loc;
2129
2130             }
2131
2132             if (!loc) {
2133                 return false;
2134             }
2135
2136             oTarget.cursorIsOver = loc.contains( pt );
2137
2138             // DragDrop is using this as a sanity check for the initial mousedown
2139             // in this case we are done.  In POINT mode, if the drag obj has no
2140             // contraints, we are also done. Otherwise we need to evaluate the
2141             // location of the target as related to the actual location of the
2142             // dragged element.
2143             var dc = this.dragCurrent;
2144             if (!dc || !dc.getTargetCoord ||
2145                     (!intersect && !dc.constrainX && !dc.constrainY)) {
2146                 return oTarget.cursorIsOver;
2147             }
2148
2149             oTarget.overlap = null;
2150
2151             // Get the current location of the drag element, this is the
2152             // location of the mouse event less the delta that represents
2153             // where the original mousedown happened on the element.  We
2154             // need to consider constraints and ticks as well.
2155             var pos = dc.getTargetCoord(pt.x, pt.y);
2156
2157             var el = dc.getDragEl();
2158             var curRegion = new Roo.lib.Region( pos.y,
2159                                                    pos.x + el.offsetWidth,
2160                                                    pos.y + el.offsetHeight,
2161                                                    pos.x );
2162
2163             var overlap = curRegion.intersect(loc);
2164
2165             if (overlap) {
2166                 oTarget.overlap = overlap;
2167                 return (intersect) ? true : oTarget.cursorIsOver;
2168             } else {
2169                 return false;
2170             }
2171         },
2172
2173         /**
2174          * unload event handler
2175          * @method _onUnload
2176          * @private
2177          * @static
2178          */
2179         _onUnload: function(e, me) {
2180             Roo.dd.DragDropMgr.unregAll();
2181         },
2182
2183         /**
2184          * Cleans up the drag and drop events and objects.
2185          * @method unregAll
2186          * @private
2187          * @static
2188          */
2189         unregAll: function() {
2190
2191             if (this.dragCurrent) {
2192                 this.stopDrag();
2193                 this.dragCurrent = null;
2194             }
2195
2196             this._execOnAll("unreg", []);
2197
2198             for (i in this.elementCache) {
2199                 delete this.elementCache[i];
2200             }
2201
2202             this.elementCache = {};
2203             this.ids = {};
2204         },
2205
2206         /**
2207          * A cache of DOM elements
2208          * @property elementCache
2209          * @private
2210          * @static
2211          */
2212         elementCache: {},
2213
2214         /**
2215          * Get the wrapper for the DOM element specified
2216          * @method getElWrapper
2217          * @param {String} id the id of the element to get
2218          * @return {Roo.dd.DDM.ElementWrapper} the wrapped element
2219          * @private
2220          * @deprecated This wrapper isn't that useful
2221          * @static
2222          */
2223         getElWrapper: function(id) {
2224             var oWrapper = this.elementCache[id];
2225             if (!oWrapper || !oWrapper.el) {
2226                 oWrapper = this.elementCache[id] =
2227                     new this.ElementWrapper(Roo.getDom(id));
2228             }
2229             return oWrapper;
2230         },
2231
2232         /**
2233          * Returns the actual DOM element
2234          * @method getElement
2235          * @param {String} id the id of the elment to get
2236          * @return {Object} The element
2237          * @deprecated use Roo.getDom instead
2238          * @static
2239          */
2240         getElement: function(id) {
2241             return Roo.getDom(id);
2242         },
2243
2244         /**
2245          * Returns the style property for the DOM element (i.e.,
2246          * document.getElById(id).style)
2247          * @method getCss
2248          * @param {String} id the id of the elment to get
2249          * @return {Object} The style property of the element
2250          * @deprecated use Roo.getDom instead
2251          * @static
2252          */
2253         getCss: function(id) {
2254             var el = Roo.getDom(id);
2255             return (el) ? el.style : null;
2256         },
2257
2258         /**
2259          * Inner class for cached elements
2260          * @class DragDropMgr.ElementWrapper
2261          * @for DragDropMgr
2262          * @private
2263          * @deprecated
2264          */
2265         ElementWrapper: function(el) {
2266                 /**
2267                  * The element
2268                  * @property el
2269                  */
2270                 this.el = el || null;
2271                 /**
2272                  * The element id
2273                  * @property id
2274                  */
2275                 this.id = this.el && el.id;
2276                 /**
2277                  * A reference to the style property
2278                  * @property css
2279                  */
2280                 this.css = this.el && el.style;
2281             },
2282
2283         /**
2284          * Returns the X position of an html element
2285          * @method getPosX
2286          * @param el the element for which to get the position
2287          * @return {int} the X coordinate
2288          * @for DragDropMgr
2289          * @deprecated use Roo.lib.Dom.getX instead
2290          * @static
2291          */
2292         getPosX: function(el) {
2293             return Roo.lib.Dom.getX(el);
2294         },
2295
2296         /**
2297          * Returns the Y position of an html element
2298          * @method getPosY
2299          * @param el the element for which to get the position
2300          * @return {int} the Y coordinate
2301          * @deprecated use Roo.lib.Dom.getY instead
2302          * @static
2303          */
2304         getPosY: function(el) {
2305             return Roo.lib.Dom.getY(el);
2306         },
2307
2308         /**
2309          * Swap two nodes.  In IE, we use the native method, for others we
2310          * emulate the IE behavior
2311          * @method swapNode
2312          * @param n1 the first node to swap
2313          * @param n2 the other node to swap
2314          * @static
2315          */
2316         swapNode: function(n1, n2) {
2317             if (n1.swapNode) {
2318                 n1.swapNode(n2);
2319             } else {
2320                 var p = n2.parentNode;
2321                 var s = n2.nextSibling;
2322
2323                 if (s == n1) {
2324                     p.insertBefore(n1, n2);
2325                 } else if (n2 == n1.nextSibling) {
2326                     p.insertBefore(n2, n1);
2327                 } else {
2328                     n1.parentNode.replaceChild(n2, n1);
2329                     p.insertBefore(n1, s);
2330                 }
2331             }
2332         },
2333
2334         /**
2335          * Returns the current scroll position
2336          * @method getScroll
2337          * @private
2338          * @static
2339          */
2340         getScroll: function () {
2341             var t, l, dde=document.documentElement, db=document.body;
2342             if (dde && (dde.scrollTop || dde.scrollLeft)) {
2343                 t = dde.scrollTop;
2344                 l = dde.scrollLeft;
2345             } else if (db) {
2346                 t = db.scrollTop;
2347                 l = db.scrollLeft;
2348             } else {
2349
2350             }
2351             return { top: t, left: l };
2352         },
2353
2354         /**
2355          * Returns the specified element style property
2356          * @method getStyle
2357          * @param {HTMLElement} el          the element
2358          * @param {string}      styleProp   the style property
2359          * @return {string} The value of the style property
2360          * @deprecated use Roo.lib.Dom.getStyle
2361          * @static
2362          */
2363         getStyle: function(el, styleProp) {
2364             return Roo.fly(el).getStyle(styleProp);
2365         },
2366
2367         /**
2368          * Gets the scrollTop
2369          * @method getScrollTop
2370          * @return {int} the document's scrollTop
2371          * @static
2372          */
2373         getScrollTop: function () { return this.getScroll().top; },
2374
2375         /**
2376          * Gets the scrollLeft
2377          * @method getScrollLeft
2378          * @return {int} the document's scrollTop
2379          * @static
2380          */
2381         getScrollLeft: function () { return this.getScroll().left; },
2382
2383         /**
2384          * Sets the x/y position of an element to the location of the
2385          * target element.
2386          * @method moveToEl
2387          * @param {HTMLElement} moveEl      The element to move
2388          * @param {HTMLElement} targetEl    The position reference element
2389          * @static
2390          */
2391         moveToEl: function (moveEl, targetEl) {
2392             var aCoord = Roo.lib.Dom.getXY(targetEl);
2393             Roo.lib.Dom.setXY(moveEl, aCoord);
2394         },
2395
2396         /**
2397          * Numeric array sort function
2398          * @method numericSort
2399          * @static
2400          */
2401         numericSort: function(a, b) { return (a - b); },
2402
2403         /**
2404          * Internal counter
2405          * @property _timeoutCount
2406          * @private
2407          * @static
2408          */
2409         _timeoutCount: 0,
2410
2411         /**
2412          * Trying to make the load order less important.  Without this we get
2413          * an error if this file is loaded before the Event Utility.
2414          * @method _addListeners
2415          * @private
2416          * @static
2417          */
2418         _addListeners: function() {
2419             var DDM = Roo.dd.DDM;
2420             if ( Roo.lib.Event && document ) {
2421                 DDM._onLoad();
2422             } else {
2423                 if (DDM._timeoutCount > 2000) {
2424                 } else {
2425                     setTimeout(DDM._addListeners, 10);
2426                     if (document && document.body) {
2427                         DDM._timeoutCount += 1;
2428                     }
2429                 }
2430             }
2431         },
2432
2433         /**
2434          * Recursively searches the immediate parent and all child nodes for
2435          * the handle element in order to determine wheter or not it was
2436          * clicked.
2437          * @method handleWasClicked
2438          * @param node the html element to inspect
2439          * @static
2440          */
2441         handleWasClicked: function(node, id) {
2442             if (this.isHandle(id, node.id)) {
2443                 return true;
2444             } else {
2445                 // check to see if this is a text node child of the one we want
2446                 var p = node.parentNode;
2447
2448                 while (p) {
2449                     if (this.isHandle(id, p.id)) {
2450                         return true;
2451                     } else {
2452                         p = p.parentNode;
2453                     }
2454                 }
2455             }
2456
2457             return false;
2458         }
2459
2460     };
2461
2462 }();
2463
2464 // shorter alias, save a few bytes
2465 Roo.dd.DDM = Roo.dd.DragDropMgr;
2466 Roo.dd.DDM._addListeners();
2467
2468 }/*
2469  * Based on:
2470  * Ext JS Library 1.1.1
2471  * Copyright(c) 2006-2007, Ext JS, LLC.
2472  *
2473  * Originally Released Under LGPL - original licence link has changed is not relivant.
2474  *
2475  * Fork - LGPL
2476  * <script type="text/javascript">
2477  */
2478
2479 /**
2480  * @class Roo.dd.DD
2481  * A DragDrop implementation where the linked element follows the
2482  * mouse cursor during a drag.
2483  * @extends Roo.dd.DragDrop
2484  * @constructor
2485  * @param {String} id the id of the linked element
2486  * @param {String} sGroup the group of related DragDrop items
2487  * @param {object} config an object containing configurable attributes
2488  *                Valid properties for DD:
2489  *                    scroll
2490  */
2491 Roo.dd.DD = function(id, sGroup, config) {
2492     if (id) {
2493         this.init(id, sGroup, config);
2494     }
2495 };
2496
2497 Roo.extend(Roo.dd.DD, Roo.dd.DragDrop, {
2498
2499     /**
2500      * When set to true, the utility automatically tries to scroll the browser
2501      * window wehn a drag and drop element is dragged near the viewport boundary.
2502      * Defaults to true.
2503      * @property scroll
2504      * @type boolean
2505      */
2506     scroll: true,
2507
2508     /**
2509      * Sets the pointer offset to the distance between the linked element's top
2510      * left corner and the location the element was clicked
2511      * @method autoOffset
2512      * @param {int} iPageX the X coordinate of the click
2513      * @param {int} iPageY the Y coordinate of the click
2514      */
2515     autoOffset: function(iPageX, iPageY) {
2516         var x = iPageX - this.startPageX;
2517         var y = iPageY - this.startPageY;
2518         this.setDelta(x, y);
2519     },
2520
2521     /**
2522      * Sets the pointer offset.  You can call this directly to force the
2523      * offset to be in a particular location (e.g., pass in 0,0 to set it
2524      * to the center of the object)
2525      * @method setDelta
2526      * @param {int} iDeltaX the distance from the left
2527      * @param {int} iDeltaY the distance from the top
2528      */
2529     setDelta: function(iDeltaX, iDeltaY) {
2530         this.deltaX = iDeltaX;
2531         this.deltaY = iDeltaY;
2532     },
2533
2534     /**
2535      * Sets the drag element to the location of the mousedown or click event,
2536      * maintaining the cursor location relative to the location on the element
2537      * that was clicked.  Override this if you want to place the element in a
2538      * location other than where the cursor is.
2539      * @method setDragElPos
2540      * @param {int} iPageX the X coordinate of the mousedown or drag event
2541      * @param {int} iPageY the Y coordinate of the mousedown or drag event
2542      */
2543     setDragElPos: function(iPageX, iPageY) {
2544         // the first time we do this, we are going to check to make sure
2545         // the element has css positioning
2546
2547         var el = this.getDragEl();
2548         this.alignElWithMouse(el, iPageX, iPageY);
2549     },
2550
2551     /**
2552      * Sets the element to the location of the mousedown or click event,
2553      * maintaining the cursor location relative to the location on the element
2554      * that was clicked.  Override this if you want to place the element in a
2555      * location other than where the cursor is.
2556      * @method alignElWithMouse
2557      * @param {HTMLElement} el the element to move
2558      * @param {int} iPageX the X coordinate of the mousedown or drag event
2559      * @param {int} iPageY the Y coordinate of the mousedown or drag event
2560      */
2561     alignElWithMouse: function(el, iPageX, iPageY) {
2562         var oCoord = this.getTargetCoord(iPageX, iPageY);
2563         var fly = el.dom ? el : Roo.fly(el);
2564         if (!this.deltaSetXY) {
2565             var aCoord = [oCoord.x, oCoord.y];
2566             fly.setXY(aCoord);
2567             var newLeft = fly.getLeft(true);
2568             var newTop  = fly.getTop(true);
2569             this.deltaSetXY = [ newLeft - oCoord.x, newTop - oCoord.y ];
2570         } else {
2571             fly.setLeftTop(oCoord.x + this.deltaSetXY[0], oCoord.y + this.deltaSetXY[1]);
2572         }
2573
2574         this.cachePosition(oCoord.x, oCoord.y);
2575         this.autoScroll(oCoord.x, oCoord.y, el.offsetHeight, el.offsetWidth);
2576         return oCoord;
2577     },
2578
2579     /**
2580      * Saves the most recent position so that we can reset the constraints and
2581      * tick marks on-demand.  We need to know this so that we can calculate the
2582      * number of pixels the element is offset from its original position.
2583      * @method cachePosition
2584      * @param iPageX the current x position (optional, this just makes it so we
2585      * don't have to look it up again)
2586      * @param iPageY the current y position (optional, this just makes it so we
2587      * don't have to look it up again)
2588      */
2589     cachePosition: function(iPageX, iPageY) {
2590         if (iPageX) {
2591             this.lastPageX = iPageX;
2592             this.lastPageY = iPageY;
2593         } else {
2594             var aCoord = Roo.lib.Dom.getXY(this.getEl());
2595             this.lastPageX = aCoord[0];
2596             this.lastPageY = aCoord[1];
2597         }
2598     },
2599
2600     /**
2601      * Auto-scroll the window if the dragged object has been moved beyond the
2602      * visible window boundary.
2603      * @method autoScroll
2604      * @param {int} x the drag element's x position
2605      * @param {int} y the drag element's y position
2606      * @param {int} h the height of the drag element
2607      * @param {int} w the width of the drag element
2608      * @private
2609      */
2610     autoScroll: function(x, y, h, w) {
2611
2612         if (this.scroll) {
2613             // The client height
2614             var clientH = Roo.lib.Dom.getViewWidth();
2615
2616             // The client width
2617             var clientW = Roo.lib.Dom.getViewHeight();
2618
2619             // The amt scrolled down
2620             var st = this.DDM.getScrollTop();
2621
2622             // The amt scrolled right
2623             var sl = this.DDM.getScrollLeft();
2624
2625             // Location of the bottom of the element
2626             var bot = h + y;
2627
2628             // Location of the right of the element
2629             var right = w + x;
2630
2631             // The distance from the cursor to the bottom of the visible area,
2632             // adjusted so that we don't scroll if the cursor is beyond the
2633             // element drag constraints
2634             var toBot = (clientH + st - y - this.deltaY);
2635
2636             // The distance from the cursor to the right of the visible area
2637             var toRight = (clientW + sl - x - this.deltaX);
2638
2639
2640             // How close to the edge the cursor must be before we scroll
2641             // var thresh = (document.all) ? 100 : 40;
2642             var thresh = 40;
2643
2644             // How many pixels to scroll per autoscroll op.  This helps to reduce
2645             // clunky scrolling. IE is more sensitive about this ... it needs this
2646             // value to be higher.
2647             var scrAmt = (document.all) ? 80 : 30;
2648
2649             // Scroll down if we are near the bottom of the visible page and the
2650             // obj extends below the crease
2651             if ( bot > clientH && toBot < thresh ) {
2652                 window.scrollTo(sl, st + scrAmt);
2653             }
2654
2655             // Scroll up if the window is scrolled down and the top of the object
2656             // goes above the top border
2657             if ( y < st && st > 0 && y - st < thresh ) {
2658                 window.scrollTo(sl, st - scrAmt);
2659             }
2660
2661             // Scroll right if the obj is beyond the right border and the cursor is
2662             // near the border.
2663             if ( right > clientW && toRight < thresh ) {
2664                 window.scrollTo(sl + scrAmt, st);
2665             }
2666
2667             // Scroll left if the window has been scrolled to the right and the obj
2668             // extends past the left border
2669             if ( x < sl && sl > 0 && x - sl < thresh ) {
2670                 window.scrollTo(sl - scrAmt, st);
2671             }
2672         }
2673     },
2674
2675     /**
2676      * Finds the location the element should be placed if we want to move
2677      * it to where the mouse location less the click offset would place us.
2678      * @method getTargetCoord
2679      * @param {int} iPageX the X coordinate of the click
2680      * @param {int} iPageY the Y coordinate of the click
2681      * @return an object that contains the coordinates (Object.x and Object.y)
2682      * @private
2683      */
2684     getTargetCoord: function(iPageX, iPageY) {
2685
2686
2687         var x = iPageX - this.deltaX;
2688         var y = iPageY - this.deltaY;
2689
2690         if (this.constrainX) {
2691             if (x < this.minX) { x = this.minX; }
2692             if (x > this.maxX) { x = this.maxX; }
2693         }
2694
2695         if (this.constrainY) {
2696             if (y < this.minY) { y = this.minY; }
2697             if (y > this.maxY) { y = this.maxY; }
2698         }
2699
2700         x = this.getTick(x, this.xTicks);
2701         y = this.getTick(y, this.yTicks);
2702
2703
2704         return {x:x, y:y};
2705     },
2706
2707     /*
2708      * Sets up config options specific to this class. Overrides
2709      * Roo.dd.DragDrop, but all versions of this method through the
2710      * inheritance chain are called
2711      */
2712     applyConfig: function() {
2713         Roo.dd.DD.superclass.applyConfig.call(this);
2714         this.scroll = (this.config.scroll !== false);
2715     },
2716
2717     /*
2718      * Event that fires prior to the onMouseDown event.  Overrides
2719      * Roo.dd.DragDrop.
2720      */
2721     b4MouseDown: function(e) {
2722         // this.resetConstraints();
2723         this.autoOffset(e.getPageX(),
2724                             e.getPageY());
2725     },
2726
2727     /*
2728      * Event that fires prior to the onDrag event.  Overrides
2729      * Roo.dd.DragDrop.
2730      */
2731     b4Drag: function(e) {
2732         this.setDragElPos(e.getPageX(),
2733                             e.getPageY());
2734     },
2735
2736     toString: function() {
2737         return ("DD " + this.id);
2738     }
2739
2740     //////////////////////////////////////////////////////////////////////////
2741     // Debugging ygDragDrop events that can be overridden
2742     //////////////////////////////////////////////////////////////////////////
2743     /*
2744     startDrag: function(x, y) {
2745     },
2746
2747     onDrag: function(e) {
2748     },
2749
2750     onDragEnter: function(e, id) {
2751     },
2752
2753     onDragOver: function(e, id) {
2754     },
2755
2756     onDragOut: function(e, id) {
2757     },
2758
2759     onDragDrop: function(e, id) {
2760     },
2761
2762     endDrag: function(e) {
2763     }
2764
2765     */
2766
2767 });/*
2768  * Based on:
2769  * Ext JS Library 1.1.1
2770  * Copyright(c) 2006-2007, Ext JS, LLC.
2771  *
2772  * Originally Released Under LGPL - original licence link has changed is not relivant.
2773  *
2774  * Fork - LGPL
2775  * <script type="text/javascript">
2776  */
2777
2778 /**
2779  * @class Roo.dd.DDProxy
2780  * A DragDrop implementation that inserts an empty, bordered div into
2781  * the document that follows the cursor during drag operations.  At the time of
2782  * the click, the frame div is resized to the dimensions of the linked html
2783  * element, and moved to the exact location of the linked element.
2784  *
2785  * References to the "frame" element refer to the single proxy element that
2786  * was created to be dragged in place of all DDProxy elements on the
2787  * page.
2788  *
2789  * @extends Roo.dd.DD
2790  * @constructor
2791  * @param {String} id the id of the linked html element
2792  * @param {String} sGroup the group of related DragDrop objects
2793  * @param {object} config an object containing configurable attributes
2794  *                Valid properties for DDProxy in addition to those in DragDrop:
2795  *                   resizeFrame, centerFrame, dragElId
2796  */
2797 Roo.dd.DDProxy = function(id, sGroup, config) {
2798     if (id) {
2799         this.init(id, sGroup, config);
2800         this.initFrame();
2801     }
2802 };
2803
2804 /**
2805  * The default drag frame div id
2806  * @property Roo.dd.DDProxy.dragElId
2807  * @type String
2808  * @static
2809  */
2810 Roo.dd.DDProxy.dragElId = "ygddfdiv";
2811
2812 Roo.extend(Roo.dd.DDProxy, Roo.dd.DD, {
2813
2814     /**
2815      * By default we resize the drag frame to be the same size as the element
2816      * we want to drag (this is to get the frame effect).  We can turn it off
2817      * if we want a different behavior.
2818      * @property resizeFrame
2819      * @type boolean
2820      */
2821     resizeFrame: true,
2822
2823     /**
2824      * By default the frame is positioned exactly where the drag element is, so
2825      * we use the cursor offset provided by Roo.dd.DD.  Another option that works only if
2826      * you do not have constraints on the obj is to have the drag frame centered
2827      * around the cursor.  Set centerFrame to true for this effect.
2828      * @property centerFrame
2829      * @type boolean
2830      */
2831     centerFrame: false,
2832
2833     /**
2834      * Creates the proxy element if it does not yet exist
2835      * @method createFrame
2836      */
2837     createFrame: function() {
2838         var self = this;
2839         var body = document.body;
2840
2841         if (!body || !body.firstChild) {
2842             setTimeout( function() { self.createFrame(); }, 50 );
2843             return;
2844         }
2845
2846         var div = this.getDragEl();
2847
2848         if (!div) {
2849             div    = document.createElement("div");
2850             div.id = this.dragElId;
2851             var s  = div.style;
2852
2853             s.position   = "absolute";
2854             s.visibility = "hidden";
2855             s.cursor     = "move";
2856             s.border     = "2px solid #aaa";
2857             s.zIndex     = 999;
2858
2859             // appendChild can blow up IE if invoked prior to the window load event
2860             // while rendering a table.  It is possible there are other scenarios
2861             // that would cause this to happen as well.
2862             body.insertBefore(div, body.firstChild);
2863         }
2864     },
2865
2866     /**
2867      * Initialization for the drag frame element.  Must be called in the
2868      * constructor of all subclasses
2869      * @method initFrame
2870      */
2871     initFrame: function() {
2872         this.createFrame();
2873     },
2874
2875     applyConfig: function() {
2876         Roo.dd.DDProxy.superclass.applyConfig.call(this);
2877
2878         this.resizeFrame = (this.config.resizeFrame !== false);
2879         this.centerFrame = (this.config.centerFrame);
2880         this.setDragElId(this.config.dragElId || Roo.dd.DDProxy.dragElId);
2881     },
2882
2883     /**
2884      * Resizes the drag frame to the dimensions of the clicked object, positions
2885      * it over the object, and finally displays it
2886      * @method showFrame
2887      * @param {int} iPageX X click position
2888      * @param {int} iPageY Y click position
2889      * @private
2890      */
2891     showFrame: function(iPageX, iPageY) {
2892         var el = this.getEl();
2893         var dragEl = this.getDragEl();
2894         var s = dragEl.style;
2895
2896         this._resizeProxy();
2897
2898         if (this.centerFrame) {
2899             this.setDelta( Math.round(parseInt(s.width,  10)/2),
2900                            Math.round(parseInt(s.height, 10)/2) );
2901         }
2902
2903         this.setDragElPos(iPageX, iPageY);
2904
2905         Roo.fly(dragEl).show();
2906     },
2907
2908     /**
2909      * The proxy is automatically resized to the dimensions of the linked
2910      * element when a drag is initiated, unless resizeFrame is set to false
2911      * @method _resizeProxy
2912      * @private
2913      */
2914     _resizeProxy: function() {
2915         if (this.resizeFrame) {
2916             var el = this.getEl();
2917             Roo.fly(this.getDragEl()).setSize(el.offsetWidth, el.offsetHeight);
2918         }
2919     },
2920
2921     // overrides Roo.dd.DragDrop
2922     b4MouseDown: function(e) {
2923         var x = e.getPageX();
2924         var y = e.getPageY();
2925         this.autoOffset(x, y);
2926         this.setDragElPos(x, y);
2927     },
2928
2929     // overrides Roo.dd.DragDrop
2930     b4StartDrag: function(x, y) {
2931         // show the drag frame
2932         this.showFrame(x, y);
2933     },
2934
2935     // overrides Roo.dd.DragDrop
2936     b4EndDrag: function(e) {
2937         Roo.fly(this.getDragEl()).hide();
2938     },
2939
2940     // overrides Roo.dd.DragDrop
2941     // By default we try to move the element to the last location of the frame.
2942     // This is so that the default behavior mirrors that of Roo.dd.DD.
2943     endDrag: function(e) {
2944
2945         var lel = this.getEl();
2946         var del = this.getDragEl();
2947
2948         // Show the drag frame briefly so we can get its position
2949         del.style.visibility = "";
2950
2951         this.beforeMove();
2952         // Hide the linked element before the move to get around a Safari
2953         // rendering bug.
2954         lel.style.visibility = "hidden";
2955         Roo.dd.DDM.moveToEl(lel, del);
2956         del.style.visibility = "hidden";
2957         lel.style.visibility = "";
2958
2959         this.afterDrag();
2960     },
2961
2962     beforeMove : function(){
2963
2964     },
2965
2966     afterDrag : function(){
2967
2968     },
2969
2970     toString: function() {
2971         return ("DDProxy " + this.id);
2972     }
2973
2974 });
2975 /*
2976  * Based on:
2977  * Ext JS Library 1.1.1
2978  * Copyright(c) 2006-2007, Ext JS, LLC.
2979  *
2980  * Originally Released Under LGPL - original licence link has changed is not relivant.
2981  *
2982  * Fork - LGPL
2983  * <script type="text/javascript">
2984  */
2985
2986  /**
2987  * @class Roo.dd.DDTarget
2988  * A DragDrop implementation that does not move, but can be a drop
2989  * target.  You would get the same result by simply omitting implementation
2990  * for the event callbacks, but this way we reduce the processing cost of the
2991  * event listener and the callbacks.
2992  * @extends Roo.dd.DragDrop
2993  * @constructor
2994  * @param {String} id the id of the element that is a drop target
2995  * @param {String} sGroup the group of related DragDrop objects
2996  * @param {object} config an object containing configurable attributes
2997  *                 Valid properties for DDTarget in addition to those in
2998  *                 DragDrop:
2999  *                    none
3000  */
3001 Roo.dd.DDTarget = function(id, sGroup, config) {
3002     if (id) {
3003         this.initTarget(id, sGroup, config);
3004     }
3005     if (config.listeners || config.events) { 
3006        Roo.dd.DragDrop.superclass.constructor.call(this,  { 
3007             listeners : config.listeners || {}, 
3008             events : config.events || {} 
3009         });    
3010     }
3011 };
3012
3013 // Roo.dd.DDTarget.prototype = new Roo.dd.DragDrop();
3014 Roo.extend(Roo.dd.DDTarget, Roo.dd.DragDrop, {
3015     toString: function() {
3016         return ("DDTarget " + this.id);
3017     }
3018 });
3019 /*
3020  * Based on:
3021  * Ext JS Library 1.1.1
3022  * Copyright(c) 2006-2007, Ext JS, LLC.
3023  *
3024  * Originally Released Under LGPL - original licence link has changed is not relivant.
3025  *
3026  * Fork - LGPL
3027  * <script type="text/javascript">
3028  */
3029  
3030
3031 /**
3032  * @class Roo.dd.ScrollManager
3033  * Provides automatic scrolling of overflow regions in the page during drag operations.<br><br>
3034  * <b>Note: This class uses "Point Mode" and is untested in "Intersect Mode".</b>
3035  * @singleton
3036  */
3037 Roo.dd.ScrollManager = function(){
3038     var ddm = Roo.dd.DragDropMgr;
3039     var els = {};
3040     var dragEl = null;
3041     var proc = {};
3042     
3043     var onStop = function(e){
3044         dragEl = null;
3045         clearProc();
3046     };
3047     
3048     var triggerRefresh = function(){
3049         if(ddm.dragCurrent){
3050              ddm.refreshCache(ddm.dragCurrent.groups);
3051         }
3052     };
3053     
3054     var doScroll = function(){
3055         if(ddm.dragCurrent){
3056             var dds = Roo.dd.ScrollManager;
3057             if(!dds.animate){
3058                 if(proc.el.scroll(proc.dir, dds.increment)){
3059                     triggerRefresh();
3060                 }
3061             }else{
3062                 proc.el.scroll(proc.dir, dds.increment, true, dds.animDuration, triggerRefresh);
3063             }
3064         }
3065     };
3066     
3067     var clearProc = function(){
3068         if(proc.id){
3069             clearInterval(proc.id);
3070         }
3071         proc.id = 0;
3072         proc.el = null;
3073         proc.dir = "";
3074     };
3075     
3076     var startProc = function(el, dir){
3077         clearProc();
3078         proc.el = el;
3079         proc.dir = dir;
3080         proc.id = setInterval(doScroll, Roo.dd.ScrollManager.frequency);
3081     };
3082     
3083     var onFire = function(e, isDrop){
3084         if(isDrop || !ddm.dragCurrent){ return; }
3085         var dds = Roo.dd.ScrollManager;
3086         if(!dragEl || dragEl != ddm.dragCurrent){
3087             dragEl = ddm.dragCurrent;
3088             // refresh regions on drag start
3089             dds.refreshCache();
3090         }
3091         
3092         var xy = Roo.lib.Event.getXY(e);
3093         var pt = new Roo.lib.Point(xy[0], xy[1]);
3094         for(var id in els){
3095             var el = els[id], r = el._region;
3096             if(r && r.contains(pt) && el.isScrollable()){
3097                 if(r.bottom - pt.y <= dds.thresh){
3098                     if(proc.el != el){
3099                         startProc(el, "down");
3100                     }
3101                     return;
3102                 }else if(r.right - pt.x <= dds.thresh){
3103                     if(proc.el != el){
3104                         startProc(el, "left");
3105                     }
3106                     return;
3107                 }else if(pt.y - r.top <= dds.thresh){
3108                     if(proc.el != el){
3109                         startProc(el, "up");
3110                     }
3111                     return;
3112                 }else if(pt.x - r.left <= dds.thresh){
3113                     if(proc.el != el){
3114                         startProc(el, "right");
3115                     }
3116                     return;
3117                 }
3118             }
3119         }
3120         clearProc();
3121     };
3122     
3123     ddm.fireEvents = ddm.fireEvents.createSequence(onFire, ddm);
3124     ddm.stopDrag = ddm.stopDrag.createSequence(onStop, ddm);
3125     
3126     return {
3127         /**
3128          * Registers new overflow element(s) to auto scroll
3129          * @param {String/HTMLElement/Element/Array} el The id of or the element to be scrolled or an array of either
3130          */
3131         register : function(el){
3132             if(el instanceof Array){
3133                 for(var i = 0, len = el.length; i < len; i++) {
3134                         this.register(el[i]);
3135                 }
3136             }else{
3137                 el = Roo.get(el);
3138                 els[el.id] = el;
3139             }
3140         },
3141         
3142         /**
3143          * Unregisters overflow element(s) so they are no longer scrolled
3144          * @param {String/HTMLElement/Element/Array} el The id of or the element to be removed or an array of either
3145          */
3146         unregister : function(el){
3147             if(el instanceof Array){
3148                 for(var i = 0, len = el.length; i < len; i++) {
3149                         this.unregister(el[i]);
3150                 }
3151             }else{
3152                 el = Roo.get(el);
3153                 delete els[el.id];
3154             }
3155         },
3156         
3157         /**
3158          * The number of pixels from the edge of a container the pointer needs to be to 
3159          * trigger scrolling (defaults to 25)
3160          * @type Number
3161          */
3162         thresh : 25,
3163         
3164         /**
3165          * The number of pixels to scroll in each scroll increment (defaults to 50)
3166          * @type Number
3167          */
3168         increment : 100,
3169         
3170         /**
3171          * The frequency of scrolls in milliseconds (defaults to 500)
3172          * @type Number
3173          */
3174         frequency : 500,
3175         
3176         /**
3177          * True to animate the scroll (defaults to true)
3178          * @type Boolean
3179          */
3180         animate: true,
3181         
3182         /**
3183          * The animation duration in seconds - 
3184          * MUST BE less than Roo.dd.ScrollManager.frequency! (defaults to .4)
3185          * @type Number
3186          */
3187         animDuration: .4,
3188         
3189         /**
3190          * Manually trigger a cache refresh.
3191          */
3192         refreshCache : function(){
3193             for(var id in els){
3194                 if(typeof els[id] == 'object'){ // for people extending the object prototype
3195                     els[id]._region = els[id].getRegion();
3196                 }
3197             }
3198         }
3199     };
3200 }();/*
3201  * Based on:
3202  * Ext JS Library 1.1.1
3203  * Copyright(c) 2006-2007, Ext JS, LLC.
3204  *
3205  * Originally Released Under LGPL - original licence link has changed is not relivant.
3206  *
3207  * Fork - LGPL
3208  * <script type="text/javascript">
3209  */
3210  
3211
3212 /**
3213  * @class Roo.dd.Registry
3214  * Provides easy access to all drag drop components that are registered on a page.  Items can be retrieved either
3215  * directly by DOM node id, or by passing in the drag drop event that occurred and looking up the event target.
3216  * @singleton
3217  */
3218 Roo.dd.Registry = function(){
3219     var elements = {}; 
3220     var handles = {}; 
3221     var autoIdSeed = 0;
3222
3223     var getId = function(el, autogen){
3224         if(typeof el == "string"){
3225             return el;
3226         }
3227         var id = el.id;
3228         if(!id && autogen !== false){
3229             id = "roodd-" + (++autoIdSeed);
3230             el.id = id;
3231         }
3232         return id;
3233     };
3234     
3235     return {
3236     /**
3237      * Register a drag drop element
3238      * @param {String|HTMLElement} element The id or DOM node to register
3239      * @param {Object} data (optional) A custom data object that will be passed between the elements that are involved
3240      * in drag drop operations.  You can populate this object with any arbitrary properties that your own code
3241      * knows how to interpret, plus there are some specific properties known to the Registry that should be
3242      * populated in the data object (if applicable):
3243      * <pre>
3244 Value      Description<br />
3245 ---------  ------------------------------------------<br />
3246 handles    Array of DOM nodes that trigger dragging<br />
3247            for the element being registered<br />
3248 isHandle   True if the element passed in triggers<br />
3249            dragging itself, else false
3250 </pre>
3251      */
3252         register : function(el, data){
3253             data = data || {};
3254             if(typeof el == "string"){
3255                 el = document.getElementById(el);
3256             }
3257             data.ddel = el;
3258             elements[getId(el)] = data;
3259             if(data.isHandle !== false){
3260                 handles[data.ddel.id] = data;
3261             }
3262             if(data.handles){
3263                 var hs = data.handles;
3264                 for(var i = 0, len = hs.length; i < len; i++){
3265                         handles[getId(hs[i])] = data;
3266                 }
3267             }
3268         },
3269
3270     /**
3271      * Unregister a drag drop element
3272      * @param {String|HTMLElement}  element The id or DOM node to unregister
3273      */
3274         unregister : function(el){
3275             var id = getId(el, false);
3276             var data = elements[id];
3277             if(data){
3278                 delete elements[id];
3279                 if(data.handles){
3280                     var hs = data.handles;
3281                     for(var i = 0, len = hs.length; i < len; i++){
3282                         delete handles[getId(hs[i], false)];
3283                     }
3284                 }
3285             }
3286         },
3287
3288     /**
3289      * Returns the handle registered for a DOM Node by id
3290      * @param {String|HTMLElement} id The DOM node or id to look up
3291      * @return {Object} handle The custom handle data
3292      */
3293         getHandle : function(id){
3294             if(typeof id != "string"){ // must be element?
3295                 id = id.id;
3296             }
3297             return handles[id];
3298         },
3299
3300     /**
3301      * Returns the handle that is registered for the DOM node that is the target of the event
3302      * @param {Event} e The event
3303      * @return {Object} handle The custom handle data
3304      */
3305         getHandleFromEvent : function(e){
3306             var t = Roo.lib.Event.getTarget(e);
3307             return t ? handles[t.id] : null;
3308         },
3309
3310     /**
3311      * Returns a custom data object that is registered for a DOM node by id
3312      * @param {String|HTMLElement} id The DOM node or id to look up
3313      * @return {Object} data The custom data
3314      */
3315         getTarget : function(id){
3316             if(typeof id != "string"){ // must be element?
3317                 id = id.id;
3318             }
3319             return elements[id];
3320         },
3321
3322     /**
3323      * Returns a custom data object that is registered for the DOM node that is the target of the event
3324      * @param {Event} e The event
3325      * @return {Object} data The custom data
3326      */
3327         getTargetFromEvent : function(e){
3328             var t = Roo.lib.Event.getTarget(e);
3329             return t ? elements[t.id] || handles[t.id] : null;
3330         }
3331     };
3332 }();/*
3333  * Based on:
3334  * Ext JS Library 1.1.1
3335  * Copyright(c) 2006-2007, Ext JS, LLC.
3336  *
3337  * Originally Released Under LGPL - original licence link has changed is not relivant.
3338  *
3339  * Fork - LGPL
3340  * <script type="text/javascript">
3341  */
3342  
3343
3344 /**
3345  * @class Roo.dd.StatusProxy
3346  * A specialized drag proxy that supports a drop status icon, {@link Roo.Layer} styles and auto-repair.  This is the
3347  * default drag proxy used by all Roo.dd components.
3348  * @constructor
3349  * @param {Object} config
3350  */
3351 Roo.dd.StatusProxy = function(config){
3352     Roo.apply(this, config);
3353     this.id = this.id || Roo.id();
3354     this.el = new Roo.Layer({
3355         dh: {
3356             id: this.id, tag: "div", cls: "x-dd-drag-proxy "+this.dropNotAllowed, children: [
3357                 {tag: "div", cls: "x-dd-drop-icon"},
3358                 {tag: "div", cls: "x-dd-drag-ghost"}
3359             ]
3360         }, 
3361         shadow: !config || config.shadow !== false
3362     });
3363     this.ghost = Roo.get(this.el.dom.childNodes[1]);
3364     this.dropStatus = this.dropNotAllowed;
3365 };
3366
3367 Roo.dd.StatusProxy.prototype = {
3368     /**
3369      * @cfg {String} dropAllowed
3370      * The CSS class to apply to the status element when drop is allowed (defaults to "x-dd-drop-ok").
3371      */
3372     dropAllowed : "x-dd-drop-ok",
3373     /**
3374      * @cfg {String} dropNotAllowed
3375      * The CSS class to apply to the status element when drop is not allowed (defaults to "x-dd-drop-nodrop").
3376      */
3377     dropNotAllowed : "x-dd-drop-nodrop",
3378
3379     /**
3380      * Updates the proxy's visual element to indicate the status of whether or not drop is allowed
3381      * over the current target element.
3382      * @param {String} cssClass The css class for the new drop status indicator image
3383      */
3384     setStatus : function(cssClass){
3385         cssClass = cssClass || this.dropNotAllowed;
3386         if(this.dropStatus != cssClass){
3387             this.el.replaceClass(this.dropStatus, cssClass);
3388             this.dropStatus = cssClass;
3389         }
3390     },
3391
3392     /**
3393      * Resets the status indicator to the default dropNotAllowed value
3394      * @param {Boolean} clearGhost True to also remove all content from the ghost, false to preserve it
3395      */
3396     reset : function(clearGhost){
3397         this.el.dom.className = "x-dd-drag-proxy " + this.dropNotAllowed;
3398         this.dropStatus = this.dropNotAllowed;
3399         if(clearGhost){
3400             this.ghost.update("");
3401         }
3402     },
3403
3404     /**
3405      * Updates the contents of the ghost element
3406      * @param {String} html The html that will replace the current innerHTML of the ghost element
3407      */
3408     update : function(html){
3409         if(typeof html == "string"){
3410             this.ghost.update(html);
3411         }else{
3412             this.ghost.update("");
3413             html.style.margin = "0";
3414             this.ghost.dom.appendChild(html);
3415         }
3416         // ensure float = none set?? cant remember why though.
3417         var el = this.ghost.dom.firstChild;
3418                 if(el){
3419                         Roo.fly(el).setStyle('float', 'none');
3420                 }
3421     },
3422     
3423     /**
3424      * Returns the underlying proxy {@link Roo.Layer}
3425      * @return {Roo.Layer} el
3426     */
3427     getEl : function(){
3428         return this.el;
3429     },
3430
3431     /**
3432      * Returns the ghost element
3433      * @return {Roo.Element} el
3434      */
3435     getGhost : function(){
3436         return this.ghost;
3437     },
3438
3439     /**
3440      * Hides the proxy
3441      * @param {Boolean} clear True to reset the status and clear the ghost contents, false to preserve them
3442      */
3443     hide : function(clear){
3444         this.el.hide();
3445         if(clear){
3446             this.reset(true);
3447         }
3448     },
3449
3450     /**
3451      * Stops the repair animation if it's currently running
3452      */
3453     stop : function(){
3454         if(this.anim && this.anim.isAnimated && this.anim.isAnimated()){
3455             this.anim.stop();
3456         }
3457     },
3458
3459     /**
3460      * Displays this proxy
3461      */
3462     show : function(){
3463         this.el.show();
3464     },
3465
3466     /**
3467      * Force the Layer to sync its shadow and shim positions to the element
3468      */
3469     sync : function(){
3470         this.el.sync();
3471     },
3472
3473     /**
3474      * Causes the proxy to return to its position of origin via an animation.  Should be called after an
3475      * invalid drop operation by the item being dragged.
3476      * @param {Array} xy The XY position of the element ([x, y])
3477      * @param {Function} callback The function to call after the repair is complete
3478      * @param {Object} scope The scope in which to execute the callback
3479      */
3480     repair : function(xy, callback, scope){
3481         this.callback = callback;
3482         this.scope = scope;
3483         if(xy && this.animRepair !== false){
3484             this.el.addClass("x-dd-drag-repair");
3485             this.el.hideUnders(true);
3486             this.anim = this.el.shift({
3487                 duration: this.repairDuration || .5,
3488                 easing: 'easeOut',
3489                 xy: xy,
3490                 stopFx: true,
3491                 callback: this.afterRepair,
3492                 scope: this
3493             });
3494         }else{
3495             this.afterRepair();
3496         }
3497     },
3498
3499     // private
3500     afterRepair : function(){
3501         this.hide(true);
3502         if(typeof this.callback == "function"){
3503             this.callback.call(this.scope || this);
3504         }
3505         this.callback = null;
3506         this.scope = null;
3507     }
3508 };/*
3509  * Based on:
3510  * Ext JS Library 1.1.1
3511  * Copyright(c) 2006-2007, Ext JS, LLC.
3512  *
3513  * Originally Released Under LGPL - original licence link has changed is not relivant.
3514  *
3515  * Fork - LGPL
3516  * <script type="text/javascript">
3517  */
3518
3519 /**
3520  * @class Roo.dd.DragSource
3521  * @extends Roo.dd.DDProxy
3522  * A simple class that provides the basic implementation needed to make any element draggable.
3523  * @constructor
3524  * @param {String/HTMLElement/Element} el The container element
3525  * @param {Object} config
3526  */
3527 Roo.dd.DragSource = function(el, config){
3528     this.el = Roo.get(el);
3529     this.dragData = {};
3530     
3531     Roo.apply(this, config);
3532     
3533     if(!this.proxy){
3534         this.proxy = new Roo.dd.StatusProxy();
3535     }
3536
3537     Roo.dd.DragSource.superclass.constructor.call(this, this.el.dom, this.ddGroup || this.group,
3538           {dragElId : this.proxy.id, resizeFrame: false, isTarget: false, scroll: this.scroll === true});
3539     
3540     this.dragging = false;
3541 };
3542
3543 Roo.extend(Roo.dd.DragSource, Roo.dd.DDProxy, {
3544     /**
3545      * @cfg {String} dropAllowed
3546      * The CSS class returned to the drag source when drop is allowed (defaults to "x-dd-drop-ok").
3547      */
3548     dropAllowed : "x-dd-drop-ok",
3549     /**
3550      * @cfg {String} dropNotAllowed
3551      * The CSS class returned to the drag source when drop is not allowed (defaults to "x-dd-drop-nodrop").
3552      */
3553     dropNotAllowed : "x-dd-drop-nodrop",
3554
3555     /**
3556      * Returns the data object associated with this drag source
3557      * @return {Object} data An object containing arbitrary data
3558      */
3559     getDragData : function(e){
3560         return this.dragData;
3561     },
3562
3563     // private
3564     onDragEnter : function(e, id){
3565         var target = Roo.dd.DragDropMgr.getDDById(id);
3566         this.cachedTarget = target;
3567         if(this.beforeDragEnter(target, e, id) !== false){
3568             if(target.isNotifyTarget){
3569                 var status = target.notifyEnter(this, e, this.dragData);
3570                 this.proxy.setStatus(status);
3571             }else{
3572                 this.proxy.setStatus(this.dropAllowed);
3573             }
3574             
3575             if(this.afterDragEnter){
3576                 /**
3577                  * An empty function by default, but provided so that you can perform a custom action
3578                  * when the dragged item enters the drop target by providing an implementation.
3579                  * @param {Roo.dd.DragDrop} target The drop target
3580                  * @param {Event} e The event object
3581                  * @param {String} id The id of the dragged element
3582                  * @method afterDragEnter
3583                  */
3584                 this.afterDragEnter(target, e, id);
3585             }
3586         }
3587     },
3588
3589     /**
3590      * An empty function by default, but provided so that you can perform a custom action
3591      * before the dragged item enters the drop target and optionally cancel the onDragEnter.
3592      * @param {Roo.dd.DragDrop} target The drop target
3593      * @param {Event} e The event object
3594      * @param {String} id The id of the dragged element
3595      * @return {Boolean} isValid True if the drag event is valid, else false to cancel
3596      */
3597     beforeDragEnter : function(target, e, id){
3598         return true;
3599     },
3600
3601     // private
3602     alignElWithMouse: function() {
3603         Roo.dd.DragSource.superclass.alignElWithMouse.apply(this, arguments);
3604         this.proxy.sync();
3605     },
3606
3607     // private
3608     onDragOver : function(e, id){
3609         var target = this.cachedTarget || Roo.dd.DragDropMgr.getDDById(id);
3610         if(this.beforeDragOver(target, e, id) !== false){
3611             if(target.isNotifyTarget){
3612                 var status = target.notifyOver(this, e, this.dragData);
3613                 this.proxy.setStatus(status);
3614             }
3615
3616             if(this.afterDragOver){
3617                 /**
3618                  * An empty function by default, but provided so that you can perform a custom action
3619                  * while the dragged item is over the drop target by providing an implementation.
3620                  * @param {Roo.dd.DragDrop} target The drop target
3621                  * @param {Event} e The event object
3622                  * @param {String} id The id of the dragged element
3623                  * @method afterDragOver
3624                  */
3625                 this.afterDragOver(target, e, id);
3626             }
3627         }
3628     },
3629
3630     /**
3631      * An empty function by default, but provided so that you can perform a custom action
3632      * while the dragged item is over the drop target and optionally cancel the onDragOver.
3633      * @param {Roo.dd.DragDrop} target The drop target
3634      * @param {Event} e The event object
3635      * @param {String} id The id of the dragged element
3636      * @return {Boolean} isValid True if the drag event is valid, else false to cancel
3637      */
3638     beforeDragOver : function(target, e, id){
3639         return true;
3640     },
3641
3642     // private
3643     onDragOut : function(e, id){
3644         var target = this.cachedTarget || Roo.dd.DragDropMgr.getDDById(id);
3645         if(this.beforeDragOut(target, e, id) !== false){
3646             if(target.isNotifyTarget){
3647                 target.notifyOut(this, e, this.dragData);
3648             }
3649             this.proxy.reset();
3650             if(this.afterDragOut){
3651                 /**
3652                  * An empty function by default, but provided so that you can perform a custom action
3653                  * after the dragged item is dragged out of the target without dropping.
3654                  * @param {Roo.dd.DragDrop} target The drop target
3655                  * @param {Event} e The event object
3656                  * @param {String} id The id of the dragged element
3657                  * @method afterDragOut
3658                  */
3659                 this.afterDragOut(target, e, id);
3660             }
3661         }
3662         this.cachedTarget = null;
3663     },
3664
3665     /**
3666      * An empty function by default, but provided so that you can perform a custom action before the dragged
3667      * item is dragged out of the target without dropping, and optionally cancel the onDragOut.
3668      * @param {Roo.dd.DragDrop} target The drop target
3669      * @param {Event} e The event object
3670      * @param {String} id The id of the dragged element
3671      * @return {Boolean} isValid True if the drag event is valid, else false to cancel
3672      */
3673     beforeDragOut : function(target, e, id){
3674         return true;
3675     },
3676     
3677     // private
3678     onDragDrop : function(e, id){
3679         var target = this.cachedTarget || Roo.dd.DragDropMgr.getDDById(id);
3680         if(this.beforeDragDrop(target, e, id) !== false){
3681             if(target.isNotifyTarget){
3682                 if(target.notifyDrop(this, e, this.dragData)){ // valid drop?
3683                     this.onValidDrop(target, e, id);
3684                 }else{
3685                     this.onInvalidDrop(target, e, id);
3686                 }
3687             }else{
3688                 this.onValidDrop(target, e, id);
3689             }
3690             
3691             if(this.afterDragDrop){
3692                 /**
3693                  * An empty function by default, but provided so that you can perform a custom action
3694                  * after a valid drag drop has occurred by providing an implementation.
3695                  * @param {Roo.dd.DragDrop} target The drop target
3696                  * @param {Event} e The event object
3697                  * @param {String} id The id of the dropped element
3698                  * @method afterDragDrop
3699                  */
3700                 this.afterDragDrop(target, e, id);
3701             }
3702         }
3703         delete this.cachedTarget;
3704     },
3705
3706     /**
3707      * An empty function by default, but provided so that you can perform a custom action before the dragged
3708      * item is dropped onto the target and optionally cancel the onDragDrop.
3709      * @param {Roo.dd.DragDrop} target The drop target
3710      * @param {Event} e The event object
3711      * @param {String} id The id of the dragged element
3712      * @return {Boolean} isValid True if the drag drop event is valid, else false to cancel
3713      */
3714     beforeDragDrop : function(target, e, id){
3715         return true;
3716     },
3717
3718     // private
3719     onValidDrop : function(target, e, id){
3720         this.hideProxy();
3721         if(this.afterValidDrop){
3722             /**
3723              * An empty function by default, but provided so that you can perform a custom action
3724              * after a valid drop has occurred by providing an implementation.
3725              * @param {Object} target The target DD 
3726              * @param {Event} e The event object
3727              * @param {String} id The id of the dropped element
3728              * @method afterInvalidDrop
3729              */
3730             this.afterValidDrop(target, e, id);
3731         }
3732     },
3733
3734     // private
3735     getRepairXY : function(e, data){
3736         return this.el.getXY();  
3737     },
3738
3739     // private
3740     onInvalidDrop : function(target, e, id){
3741         this.beforeInvalidDrop(target, e, id);
3742         if(this.cachedTarget){
3743             if(this.cachedTarget.isNotifyTarget){
3744                 this.cachedTarget.notifyOut(this, e, this.dragData);
3745             }
3746             this.cacheTarget = null;
3747         }
3748         this.proxy.repair(this.getRepairXY(e, this.dragData), this.afterRepair, this);
3749
3750         if(this.afterInvalidDrop){
3751             /**
3752              * An empty function by default, but provided so that you can perform a custom action
3753              * after an invalid drop has occurred by providing an implementation.
3754              * @param {Event} e The event object
3755              * @param {String} id The id of the dropped element
3756              * @method afterInvalidDrop
3757              */
3758             this.afterInvalidDrop(e, id);
3759         }
3760     },
3761
3762     // private
3763     afterRepair : function(){
3764         if(Roo.enableFx){
3765             this.el.highlight(this.hlColor || "c3daf9");
3766         }
3767         this.dragging = false;
3768     },
3769
3770     /**
3771      * An empty function by default, but provided so that you can perform a custom action after an invalid
3772      * drop has occurred.
3773      * @param {Roo.dd.DragDrop} target The drop target
3774      * @param {Event} e The event object
3775      * @param {String} id The id of the dragged element
3776      * @return {Boolean} isValid True if the invalid drop should proceed, else false to cancel
3777      */
3778     beforeInvalidDrop : function(target, e, id){
3779         return true;
3780     },
3781
3782     // private
3783     handleMouseDown : function(e){
3784         if(this.dragging) {
3785             return;
3786         }
3787         var data = this.getDragData(e);
3788         if(data && this.onBeforeDrag(data, e) !== false){
3789             this.dragData = data;
3790             this.proxy.stop();
3791             Roo.dd.DragSource.superclass.handleMouseDown.apply(this, arguments);
3792         } 
3793     },
3794
3795     /**
3796      * An empty function by default, but provided so that you can perform a custom action before the initial
3797      * drag event begins and optionally cancel it.
3798      * @param {Object} data An object containing arbitrary data to be shared with drop targets
3799      * @param {Event} e The event object
3800      * @return {Boolean} isValid True if the drag event is valid, else false to cancel
3801      */
3802     onBeforeDrag : function(data, e){
3803         return true;
3804     },
3805
3806     /**
3807      * An empty function by default, but provided so that you can perform a custom action once the initial
3808      * drag event has begun.  The drag cannot be canceled from this function.
3809      * @param {Number} x The x position of the click on the dragged object
3810      * @param {Number} y The y position of the click on the dragged object
3811      */
3812     onStartDrag : Roo.emptyFn,
3813
3814     // private - YUI override
3815     startDrag : function(x, y){
3816         this.proxy.reset();
3817         this.dragging = true;
3818         this.proxy.update("");
3819         this.onInitDrag(x, y);
3820         this.proxy.show();
3821     },
3822
3823     // private
3824     onInitDrag : function(x, y){
3825         var clone = this.el.dom.cloneNode(true);
3826         clone.id = Roo.id(); // prevent duplicate ids
3827         this.proxy.update(clone);
3828         this.onStartDrag(x, y);
3829         return true;
3830     },
3831
3832     /**
3833      * Returns the drag source's underlying {@link Roo.dd.StatusProxy}
3834      * @return {Roo.dd.StatusProxy} proxy The StatusProxy
3835      */
3836     getProxy : function(){
3837         return this.proxy;  
3838     },
3839
3840     /**
3841      * Hides the drag source's {@link Roo.dd.StatusProxy}
3842      */
3843     hideProxy : function(){
3844         this.proxy.hide();  
3845         this.proxy.reset(true);
3846         this.dragging = false;
3847     },
3848
3849     // private
3850     triggerCacheRefresh : function(){
3851         Roo.dd.DDM.refreshCache(this.groups);
3852     },
3853
3854     // private - override to prevent hiding
3855     b4EndDrag: function(e) {
3856     },
3857
3858     // private - override to prevent moving
3859     endDrag : function(e){
3860         this.onEndDrag(this.dragData, e);
3861     },
3862
3863     // private
3864     onEndDrag : function(data, e){
3865     },
3866     
3867     // private - pin to cursor
3868     autoOffset : function(x, y) {
3869         this.setDelta(-12, -20);
3870     }    
3871 });/*
3872  * Based on:
3873  * Ext JS Library 1.1.1
3874  * Copyright(c) 2006-2007, Ext JS, LLC.
3875  *
3876  * Originally Released Under LGPL - original licence link has changed is not relivant.
3877  *
3878  * Fork - LGPL
3879  * <script type="text/javascript">
3880  */
3881
3882
3883 /**
3884  * @class Roo.dd.DropTarget
3885  * @extends Roo.dd.DDTarget
3886  * A simple class that provides the basic implementation needed to make any element a drop target that can have
3887  * draggable items dropped onto it.  The drop has no effect until an implementation of notifyDrop is provided.
3888  * @constructor
3889  * @param {String/HTMLElement/Element} el The container element
3890  * @param {Object} config
3891  */
3892 Roo.dd.DropTarget = function(el, config){
3893     this.el = Roo.get(el);
3894     
3895     var listeners = false; ;
3896     if (config && config.listeners) {
3897         listeners= config.listeners;
3898         delete config.listeners;
3899     }
3900     Roo.apply(this, config);
3901     
3902     if(this.containerScroll){
3903         Roo.dd.ScrollManager.register(this.el);
3904     }
3905     this.addEvents( {
3906          /**
3907          * @scope Roo.dd.DropTarget
3908          */
3909          
3910          /**
3911          * @event enter
3912          * The function a {@link Roo.dd.DragSource} calls once to notify this drop target that the source is now over the
3913          * target.  This default implementation adds the CSS class specified by overClass (if any) to the drop element
3914          * and returns the dropAllowed config value.  This method should be overridden if drop validation is required.
3915          * 
3916          * IMPORTANT : it should set this.overClass and this.dropAllowed
3917          * 
3918          * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop target
3919          * @param {Event} e The event
3920          * @param {Object} data An object containing arbitrary data supplied by the drag source
3921          */
3922         "enter" : true,
3923         
3924          /**
3925          * @event over
3926          * The function a {@link Roo.dd.DragSource} calls continuously while it is being dragged over the target.
3927          * This method will be called on every mouse movement while the drag source is over the drop target.
3928          * This default implementation simply returns the dropAllowed config value.
3929          * 
3930          * IMPORTANT : it should set this.dropAllowed
3931          * 
3932          * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop target
3933          * @param {Event} e The event
3934          * @param {Object} data An object containing arbitrary data supplied by the drag source
3935          
3936          */
3937         "over" : true,
3938         /**
3939          * @event out
3940          * The function a {@link Roo.dd.DragSource} calls once to notify this drop target that the source has been dragged
3941          * out of the target without dropping.  This default implementation simply removes the CSS class specified by
3942          * overClass (if any) from the drop element.
3943          * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop target
3944          * @param {Event} e The event
3945          * @param {Object} data An object containing arbitrary data supplied by the drag source
3946          */
3947          "out" : true,
3948          
3949         /**
3950          * @event drop
3951          * The function a {@link Roo.dd.DragSource} calls once to notify this drop target that the dragged item has
3952          * been dropped on it.  This method has no default implementation and returns false, so you must provide an
3953          * implementation that does something to process the drop event and returns true so that the drag source's
3954          * repair action does not run.
3955          * 
3956          * IMPORTANT : it should set this.success
3957          * 
3958          * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop target
3959          * @param {Event} e The event
3960          * @param {Object} data An object containing arbitrary data supplied by the drag source
3961         */
3962          "drop" : true
3963     });
3964             
3965      
3966     Roo.dd.DropTarget.superclass.constructor.call(  this, 
3967         this.el.dom, 
3968         this.ddGroup || this.group,
3969         {
3970             isTarget: true,
3971             listeners : listeners || {} 
3972            
3973         
3974         }
3975     );
3976
3977 };
3978
3979 Roo.extend(Roo.dd.DropTarget, Roo.dd.DDTarget, {
3980     /**
3981      * @cfg {String} overClass
3982      * The CSS class applied to the drop target element while the drag source is over it (defaults to "").
3983      */
3984      /**
3985      * @cfg {String} ddGroup
3986      * The drag drop group to handle drop events for
3987      */
3988      
3989     /**
3990      * @cfg {String} dropAllowed
3991      * The CSS class returned to the drag source when drop is allowed (defaults to "x-dd-drop-ok").
3992      */
3993     dropAllowed : "x-dd-drop-ok",
3994     /**
3995      * @cfg {String} dropNotAllowed
3996      * The CSS class returned to the drag source when drop is not allowed (defaults to "x-dd-drop-nodrop").
3997      */
3998     dropNotAllowed : "x-dd-drop-nodrop",
3999     /**
4000      * @cfg {boolean} success
4001      * set this after drop listener.. 
4002      */
4003     success : false,
4004     /**
4005      * @cfg {boolean|String} valid true/false or string (ok-add/ok-sub/ok/nodrop)
4006      * if the drop point is valid for over/enter..
4007      */
4008     valid : false,
4009     // private
4010     isTarget : true,
4011
4012     // private
4013     isNotifyTarget : true,
4014     
4015     /**
4016      * @hide
4017      */
4018     notifyEnter : function(dd, e, data)
4019     {
4020         this.valid = true;
4021         this.fireEvent('enter', dd, e, data);
4022         if(this.overClass){
4023             this.el.addClass(this.overClass);
4024         }
4025         return typeof(this.valid) == 'string' ? 'x-dd-drop-' + this.valid : (
4026             this.valid ? this.dropAllowed : this.dropNotAllowed
4027         );
4028     },
4029
4030     /**
4031      * @hide
4032      */
4033     notifyOver : function(dd, e, data)
4034     {
4035         this.valid = true;
4036         this.fireEvent('over', dd, e, data);
4037         return typeof(this.valid) == 'string' ? 'x-dd-drop-' + this.valid : (
4038             this.valid ? this.dropAllowed : this.dropNotAllowed
4039         );
4040     },
4041
4042     /**
4043      * @hide
4044      */
4045     notifyOut : function(dd, e, data)
4046     {
4047         this.fireEvent('out', dd, e, data);
4048         if(this.overClass){
4049             this.el.removeClass(this.overClass);
4050         }
4051     },
4052
4053     /**
4054      * @hide
4055      */
4056     notifyDrop : function(dd, e, data)
4057     {
4058         this.success = false;
4059         this.fireEvent('drop', dd, e, data);
4060         return this.success;
4061     }
4062 });/*
4063  * Based on:
4064  * Ext JS Library 1.1.1
4065  * Copyright(c) 2006-2007, Ext JS, LLC.
4066  *
4067  * Originally Released Under LGPL - original licence link has changed is not relivant.
4068  *
4069  * Fork - LGPL
4070  * <script type="text/javascript">
4071  */
4072
4073
4074 /**
4075  * @class Roo.dd.DragZone
4076  * @extends Roo.dd.DragSource
4077  * This class provides a container DD instance that proxies for multiple child node sources.<br />
4078  * By default, this class requires that draggable child nodes are registered with {@link Roo.dd.Registry}.
4079  * @constructor
4080  * @param {String/HTMLElement/Element} el The container element
4081  * @param {Object} config
4082  */
4083 Roo.dd.DragZone = function(el, config){
4084     Roo.dd.DragZone.superclass.constructor.call(this, el, config);
4085     if(this.containerScroll){
4086         Roo.dd.ScrollManager.register(this.el);
4087     }
4088 };
4089
4090 Roo.extend(Roo.dd.DragZone, Roo.dd.DragSource, {
4091     /**
4092      * @cfg {Boolean} containerScroll True to register this container with the Scrollmanager
4093      * for auto scrolling during drag operations.
4094      */
4095     /**
4096      * @cfg {String} hlColor The color to use when visually highlighting the drag source in the afterRepair
4097      * method after a failed drop (defaults to "c3daf9" - light blue)
4098      */
4099
4100     /**
4101      * Called when a mousedown occurs in this container. Looks in {@link Roo.dd.Registry}
4102      * for a valid target to drag based on the mouse down. Override this method
4103      * to provide your own lookup logic (e.g. finding a child by class name). Make sure your returned
4104      * object has a "ddel" attribute (with an HTML Element) for other functions to work.
4105      * @param {EventObject} e The mouse down event
4106      * @return {Object} The dragData
4107      */
4108     getDragData : function(e){
4109         return Roo.dd.Registry.getHandleFromEvent(e);
4110     },
4111     
4112     /**
4113      * Called once drag threshold has been reached to initialize the proxy element. By default, it clones the
4114      * this.dragData.ddel
4115      * @param {Number} x The x position of the click on the dragged object
4116      * @param {Number} y The y position of the click on the dragged object
4117      * @return {Boolean} true to continue the drag, false to cancel
4118      */
4119     onInitDrag : function(x, y){
4120         this.proxy.update(this.dragData.ddel.cloneNode(true));
4121         this.onStartDrag(x, y);
4122         return true;
4123     },
4124     
4125     /**
4126      * Called after a repair of an invalid drop. By default, highlights this.dragData.ddel 
4127      */
4128     afterRepair : function(){
4129         if(Roo.enableFx){
4130             Roo.Element.fly(this.dragData.ddel).highlight(this.hlColor || "c3daf9");
4131         }
4132         this.dragging = false;
4133     },
4134
4135     /**
4136      * Called before a repair of an invalid drop to get the XY to animate to. By default returns
4137      * the XY of this.dragData.ddel
4138      * @param {EventObject} e The mouse up event
4139      * @return {Array} The xy location (e.g. [100, 200])
4140      */
4141     getRepairXY : function(e){
4142         return Roo.Element.fly(this.dragData.ddel).getXY();  
4143     }
4144 });/*
4145  * Based on:
4146  * Ext JS Library 1.1.1
4147  * Copyright(c) 2006-2007, Ext JS, LLC.
4148  *
4149  * Originally Released Under LGPL - original licence link has changed is not relivant.
4150  *
4151  * Fork - LGPL
4152  * <script type="text/javascript">
4153  */
4154 /**
4155  * @class Roo.dd.DropZone
4156  * @extends Roo.dd.DropTarget
4157  * This class provides a container DD instance that proxies for multiple child node targets.<br />
4158  * By default, this class requires that child nodes accepting drop are registered with {@link Roo.dd.Registry}.
4159  * @constructor
4160  * @param {String/HTMLElement/Element} el The container element
4161  * @param {Object} config
4162  */
4163 Roo.dd.DropZone = function(el, config){
4164     Roo.dd.DropZone.superclass.constructor.call(this, el, config);
4165 };
4166
4167 Roo.extend(Roo.dd.DropZone, Roo.dd.DropTarget, {
4168     /**
4169      * Returns a custom data object associated with the DOM node that is the target of the event.  By default
4170      * this looks up the event target in the {@link Roo.dd.Registry}, although you can override this method to
4171      * provide your own custom lookup.
4172      * @param {Event} e The event
4173      * @return {Object} data The custom data
4174      */
4175     getTargetFromEvent : function(e){
4176         return Roo.dd.Registry.getTargetFromEvent(e);
4177     },
4178
4179     /**
4180      * Called internally when the DropZone determines that a {@link Roo.dd.DragSource} has entered a drop node
4181      * that it has registered.  This method has no default implementation and should be overridden to provide
4182      * node-specific processing if necessary.
4183      * @param {Object} nodeData The custom data associated with the drop node (this is the same value returned from 
4184      * {@link #getTargetFromEvent} for this node)
4185      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
4186      * @param {Event} e The event
4187      * @param {Object} data An object containing arbitrary data supplied by the drag source
4188      */
4189     onNodeEnter : function(n, dd, e, data){
4190         
4191     },
4192
4193     /**
4194      * Called internally while the DropZone determines that a {@link Roo.dd.DragSource} is over a drop node
4195      * that it has registered.  The default implementation returns this.dropNotAllowed, so it should be
4196      * overridden to provide the proper feedback.
4197      * @param {Object} nodeData The custom data associated with the drop node (this is the same value returned from
4198      * {@link #getTargetFromEvent} for this node)
4199      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
4200      * @param {Event} e The event
4201      * @param {Object} data An object containing arbitrary data supplied by the drag source
4202      * @return {String} status The CSS class that communicates the drop status back to the source so that the
4203      * underlying {@link Roo.dd.StatusProxy} can be updated
4204      */
4205     onNodeOver : function(n, dd, e, data){
4206         return this.dropAllowed;
4207     },
4208
4209     /**
4210      * Called internally when the DropZone determines that a {@link Roo.dd.DragSource} has been dragged out of
4211      * the drop node without dropping.  This method has no default implementation and should be overridden to provide
4212      * node-specific processing if necessary.
4213      * @param {Object} nodeData The custom data associated with the drop node (this is the same value returned from
4214      * {@link #getTargetFromEvent} for this node)
4215      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
4216      * @param {Event} e The event
4217      * @param {Object} data An object containing arbitrary data supplied by the drag source
4218      */
4219     onNodeOut : function(n, dd, e, data){
4220         
4221     },
4222
4223     /**
4224      * Called internally when the DropZone determines that a {@link Roo.dd.DragSource} has been dropped onto
4225      * the drop node.  The default implementation returns false, so it should be overridden to provide the
4226      * appropriate processing of the drop event and return true so that the drag source's repair action does not run.
4227      * @param {Object} nodeData The custom data associated with the drop node (this is the same value returned from
4228      * {@link #getTargetFromEvent} for this node)
4229      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
4230      * @param {Event} e The event
4231      * @param {Object} data An object containing arbitrary data supplied by the drag source
4232      * @return {Boolean} True if the drop was valid, else false
4233      */
4234     onNodeDrop : function(n, dd, e, data){
4235         return false;
4236     },
4237
4238     /**
4239      * Called internally while the DropZone determines that a {@link Roo.dd.DragSource} is being dragged over it,
4240      * but not over any of its registered drop nodes.  The default implementation returns this.dropNotAllowed, so
4241      * it should be overridden to provide the proper feedback if necessary.
4242      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
4243      * @param {Event} e The event
4244      * @param {Object} data An object containing arbitrary data supplied by the drag source
4245      * @return {String} status The CSS class that communicates the drop status back to the source so that the
4246      * underlying {@link Roo.dd.StatusProxy} can be updated
4247      */
4248     onContainerOver : function(dd, e, data){
4249         return this.dropNotAllowed;
4250     },
4251
4252     /**
4253      * Called internally when the DropZone determines that a {@link Roo.dd.DragSource} has been dropped on it,
4254      * but not on any of its registered drop nodes.  The default implementation returns false, so it should be
4255      * overridden to provide the appropriate processing of the drop event if you need the drop zone itself to
4256      * be able to accept drops.  It should return true when valid so that the drag source's repair action does not run.
4257      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
4258      * @param {Event} e The event
4259      * @param {Object} data An object containing arbitrary data supplied by the drag source
4260      * @return {Boolean} True if the drop was valid, else false
4261      */
4262     onContainerDrop : function(dd, e, data){
4263         return false;
4264     },
4265
4266     /**
4267      * The function a {@link Roo.dd.DragSource} calls once to notify this drop zone that the source is now over
4268      * the zone.  The default implementation returns this.dropNotAllowed and expects that only registered drop
4269      * nodes can process drag drop operations, so if you need the drop zone itself to be able to process drops
4270      * you should override this method and provide a custom implementation.
4271      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
4272      * @param {Event} e The event
4273      * @param {Object} data An object containing arbitrary data supplied by the drag source
4274      * @return {String} status The CSS class that communicates the drop status back to the source so that the
4275      * underlying {@link Roo.dd.StatusProxy} can be updated
4276      */
4277     notifyEnter : function(dd, e, data){
4278         return this.dropNotAllowed;
4279     },
4280
4281     /**
4282      * The function a {@link Roo.dd.DragSource} calls continuously while it is being dragged over the drop zone.
4283      * This method will be called on every mouse movement while the drag source is over the drop zone.
4284      * It will call {@link #onNodeOver} while the drag source is over a registered node, and will also automatically
4285      * delegate to the appropriate node-specific methods as necessary when the drag source enters and exits
4286      * registered nodes ({@link #onNodeEnter}, {@link #onNodeOut}). If the drag source is not currently over a
4287      * registered node, it will call {@link #onContainerOver}.
4288      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
4289      * @param {Event} e The event
4290      * @param {Object} data An object containing arbitrary data supplied by the drag source
4291      * @return {String} status The CSS class that communicates the drop status back to the source so that the
4292      * underlying {@link Roo.dd.StatusProxy} can be updated
4293      */
4294     notifyOver : function(dd, e, data){
4295         var n = this.getTargetFromEvent(e);
4296         if(!n){ // not over valid drop target
4297             if(this.lastOverNode){
4298                 this.onNodeOut(this.lastOverNode, dd, e, data);
4299                 this.lastOverNode = null;
4300             }
4301             return this.onContainerOver(dd, e, data);
4302         }
4303         if(this.lastOverNode != n){
4304             if(this.lastOverNode){
4305                 this.onNodeOut(this.lastOverNode, dd, e, data);
4306             }
4307             this.onNodeEnter(n, dd, e, data);
4308             this.lastOverNode = n;
4309         }
4310         return this.onNodeOver(n, dd, e, data);
4311     },
4312
4313     /**
4314      * The function a {@link Roo.dd.DragSource} calls once to notify this drop zone that the source has been dragged
4315      * out of the zone without dropping.  If the drag source is currently over a registered node, the notification
4316      * will be delegated to {@link #onNodeOut} for node-specific handling, otherwise it will be ignored.
4317      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop target
4318      * @param {Event} e The event
4319      * @param {Object} data An object containing arbitrary data supplied by the drag zone
4320      */
4321     notifyOut : function(dd, e, data){
4322         if(this.lastOverNode){
4323             this.onNodeOut(this.lastOverNode, dd, e, data);
4324             this.lastOverNode = null;
4325         }
4326     },
4327
4328     /**
4329      * The function a {@link Roo.dd.DragSource} calls once to notify this drop zone that the dragged item has
4330      * been dropped on it.  The drag zone will look up the target node based on the event passed in, and if there
4331      * is a node registered for that event, it will delegate to {@link #onNodeDrop} for node-specific handling,
4332      * otherwise it will call {@link #onContainerDrop}.
4333      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
4334      * @param {Event} e The event
4335      * @param {Object} data An object containing arbitrary data supplied by the drag source
4336      * @return {Boolean} True if the drop was valid, else false
4337      */
4338     notifyDrop : function(dd, e, data){
4339         if(this.lastOverNode){
4340             this.onNodeOut(this.lastOverNode, dd, e, data);
4341             this.lastOverNode = null;
4342         }
4343         var n = this.getTargetFromEvent(e);
4344         return n ?
4345             this.onNodeDrop(n, dd, e, data) :
4346             this.onContainerDrop(dd, e, data);
4347     },
4348
4349     // private
4350     triggerCacheRefresh : function(){
4351         Roo.dd.DDM.refreshCache(this.groups);
4352     }  
4353 });/*
4354  * Based on:
4355  * Ext JS Library 1.1.1
4356  * Copyright(c) 2006-2007, Ext JS, LLC.
4357  *
4358  * Originally Released Under LGPL - original licence link has changed is not relivant.
4359  *
4360  * Fork - LGPL
4361  * <script type="text/javascript">
4362  */
4363
4364
4365 /**
4366  * @class Roo.data.SortTypes
4367  * @singleton
4368  * Defines the default sorting (casting?) comparison functions used when sorting data.
4369  */
4370 Roo.data.SortTypes = {
4371     /**
4372      * Default sort that does nothing
4373      * @param {Mixed} s The value being converted
4374      * @return {Mixed} The comparison value
4375      */
4376     none : function(s){
4377         return s;
4378     },
4379     
4380     /**
4381      * The regular expression used to strip tags
4382      * @type {RegExp}
4383      * @property
4384      */
4385     stripTagsRE : /<\/?[^>]+>/gi,
4386     
4387     /**
4388      * Strips all HTML tags to sort on text only
4389      * @param {Mixed} s The value being converted
4390      * @return {String} The comparison value
4391      */
4392     asText : function(s){
4393         return String(s).replace(this.stripTagsRE, "");
4394     },
4395     
4396     /**
4397      * Strips all HTML tags to sort on text only - Case insensitive
4398      * @param {Mixed} s The value being converted
4399      * @return {String} The comparison value
4400      */
4401     asUCText : function(s){
4402         return String(s).toUpperCase().replace(this.stripTagsRE, "");
4403     },
4404     
4405     /**
4406      * Case insensitive string
4407      * @param {Mixed} s The value being converted
4408      * @return {String} The comparison value
4409      */
4410     asUCString : function(s) {
4411         return String(s).toUpperCase();
4412     },
4413     
4414     /**
4415      * Date sorting
4416      * @param {Mixed} s The value being converted
4417      * @return {Number} The comparison value
4418      */
4419     asDate : function(s) {
4420         if(!s){
4421             return 0;
4422         }
4423         if(s instanceof Date){
4424             return s.getTime();
4425         }
4426         return Date.parse(String(s));
4427     },
4428     
4429     /**
4430      * Float sorting
4431      * @param {Mixed} s The value being converted
4432      * @return {Float} The comparison value
4433      */
4434     asFloat : function(s) {
4435         var val = parseFloat(String(s).replace(/,/g, ""));
4436         if(isNaN(val)) val = 0;
4437         return val;
4438     },
4439     
4440     /**
4441      * Integer sorting
4442      * @param {Mixed} s The value being converted
4443      * @return {Number} The comparison value
4444      */
4445     asInt : function(s) {
4446         var val = parseInt(String(s).replace(/,/g, ""));
4447         if(isNaN(val)) val = 0;
4448         return val;
4449     }
4450 };/*
4451  * Based on:
4452  * Ext JS Library 1.1.1
4453  * Copyright(c) 2006-2007, Ext JS, LLC.
4454  *
4455  * Originally Released Under LGPL - original licence link has changed is not relivant.
4456  *
4457  * Fork - LGPL
4458  * <script type="text/javascript">
4459  */
4460
4461 /**
4462 * @class Roo.data.Record
4463  * Instances of this class encapsulate both record <em>definition</em> information, and record
4464  * <em>value</em> information for use in {@link Roo.data.Store} objects, or any code which needs
4465  * to access Records cached in an {@link Roo.data.Store} object.<br>
4466  * <p>
4467  * Constructors for this class are generated by passing an Array of field definition objects to {@link #create}.
4468  * Instances are usually only created by {@link Roo.data.Reader} implementations when processing unformatted data
4469  * objects.<br>
4470  * <p>
4471  * Record objects generated by this constructor inherit all the methods of Roo.data.Record listed below.
4472  * @constructor
4473  * This constructor should not be used to create Record objects. Instead, use the constructor generated by
4474  * {@link #create}. The parameters are the same.
4475  * @param {Array} data An associative Array of data values keyed by the field name.
4476  * @param {Object} id (Optional) The id of the record. This id should be unique, and is used by the
4477  * {@link Roo.data.Store} object which owns the Record to index its collection of Records. If
4478  * not specified an integer id is generated.
4479  */
4480 Roo.data.Record = function(data, id){
4481     this.id = (id || id === 0) ? id : ++Roo.data.Record.AUTO_ID;
4482     this.data = data;
4483 };
4484
4485 /**
4486  * Generate a constructor for a specific record layout.
4487  * @param {Array} o An Array of field definition objects which specify field names, and optionally,
4488  * data types, and a mapping for an {@link Roo.data.Reader} to extract the field's value from a data object.
4489  * Each field definition object may contain the following properties: <ul>
4490  * <li><b>name</b> : String<p style="margin-left:1em">The name by which the field is referenced within the Record. This is referenced by,
4491  * for example the <em>dataIndex</em> property in column definition objects passed to {@link Roo.grid.ColumnModel}</p></li>
4492  * <li><b>mapping</b> : String<p style="margin-left:1em">(Optional) A path specification for use by the {@link Roo.data.Reader} implementation
4493  * that is creating the Record to access the data value from the data object. If an {@link Roo.data.JsonReader}
4494  * is being used, then this is a string containing the javascript expression to reference the data relative to 
4495  * the record item's root. If an {@link Roo.data.XmlReader} is being used, this is an {@link Roo.DomQuery} path
4496  * to the data item relative to the record element. If the mapping expression is the same as the field name,
4497  * this may be omitted.</p></li>
4498  * <li><b>type</b> : String<p style="margin-left:1em">(Optional) The data type for conversion to displayable value. Possible values are
4499  * <ul><li>auto (Default, implies no conversion)</li>
4500  * <li>string</li>
4501  * <li>int</li>
4502  * <li>float</li>
4503  * <li>boolean</li>
4504  * <li>date</li></ul></p></li>
4505  * <li><b>sortType</b> : Mixed<p style="margin-left:1em">(Optional) A member of {@link Roo.data.SortTypes}.</p></li>
4506  * <li><b>sortDir</b> : String<p style="margin-left:1em">(Optional) Initial direction to sort. "ASC" or "DESC"</p></li>
4507  * <li><b>convert</b> : Function<p style="margin-left:1em">(Optional) A function which converts the value provided
4508  * by the Reader into an object that will be stored in the Record. It is passed the
4509  * following parameters:<ul>
4510  * <li><b>v</b> : Mixed<p style="margin-left:1em">The data value as read by the Reader.</p></li>
4511  * </ul></p></li>
4512  * <li><b>dateFormat</b> : String<p style="margin-left:1em">(Optional) A format String for the Date.parseDate function.</p></li>
4513  * </ul>
4514  * <br>usage:<br><pre><code>
4515 var TopicRecord = Roo.data.Record.create(
4516     {name: 'title', mapping: 'topic_title'},
4517     {name: 'author', mapping: 'username'},
4518     {name: 'totalPosts', mapping: 'topic_replies', type: 'int'},
4519     {name: 'lastPost', mapping: 'post_time', type: 'date'},
4520     {name: 'lastPoster', mapping: 'user2'},
4521     {name: 'excerpt', mapping: 'post_text'}
4522 );
4523
4524 var myNewRecord = new TopicRecord({
4525     title: 'Do my job please',
4526     author: 'noobie',
4527     totalPosts: 1,
4528     lastPost: new Date(),
4529     lastPoster: 'Animal',
4530     excerpt: 'No way dude!'
4531 });
4532 myStore.add(myNewRecord);
4533 </code></pre>
4534  * @method create
4535  * @static
4536  */
4537 Roo.data.Record.create = function(o){
4538     var f = function(){
4539         f.superclass.constructor.apply(this, arguments);
4540     };
4541     Roo.extend(f, Roo.data.Record);
4542     var p = f.prototype;
4543     p.fields = new Roo.util.MixedCollection(false, function(field){
4544         return field.name;
4545     });
4546     for(var i = 0, len = o.length; i < len; i++){
4547         p.fields.add(new Roo.data.Field(o[i]));
4548     }
4549     f.getField = function(name){
4550         return p.fields.get(name);  
4551     };
4552     return f;
4553 };
4554
4555 Roo.data.Record.AUTO_ID = 1000;
4556 Roo.data.Record.EDIT = 'edit';
4557 Roo.data.Record.REJECT = 'reject';
4558 Roo.data.Record.COMMIT = 'commit';
4559
4560 Roo.data.Record.prototype = {
4561     /**
4562      * Readonly flag - true if this record has been modified.
4563      * @type Boolean
4564      */
4565     dirty : false,
4566     editing : false,
4567     error: null,
4568     modified: null,
4569
4570     // private
4571     join : function(store){
4572         this.store = store;
4573     },
4574
4575     /**
4576      * Set the named field to the specified value.
4577      * @param {String} name The name of the field to set.
4578      * @param {Object} value The value to set the field to.
4579      */
4580     set : function(name, value){
4581         if(this.data[name] == value){
4582             return;
4583         }
4584         this.dirty = true;
4585         if(!this.modified){
4586             this.modified = {};
4587         }
4588         if(typeof this.modified[name] == 'undefined'){
4589             this.modified[name] = this.data[name];
4590         }
4591         this.data[name] = value;
4592         if(!this.editing){
4593             this.store.afterEdit(this);
4594         }       
4595     },
4596
4597     /**
4598      * Get the value of the named field.
4599      * @param {String} name The name of the field to get the value of.
4600      * @return {Object} The value of the field.
4601      */
4602     get : function(name){
4603         return this.data[name]; 
4604     },
4605
4606     // private
4607     beginEdit : function(){
4608         this.editing = true;
4609         this.modified = {}; 
4610     },
4611
4612     // private
4613     cancelEdit : function(){
4614         this.editing = false;
4615         delete this.modified;
4616     },
4617
4618     // private
4619     endEdit : function(){
4620         this.editing = false;
4621         if(this.dirty && this.store){
4622             this.store.afterEdit(this);
4623         }
4624     },
4625
4626     /**
4627      * Usually called by the {@link Roo.data.Store} which owns the Record.
4628      * Rejects all changes made to the Record since either creation, or the last commit operation.
4629      * Modified fields are reverted to their original values.
4630      * <p>
4631      * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
4632      * of reject operations.
4633      */
4634     reject : function(){
4635         var m = this.modified;
4636         for(var n in m){
4637             if(typeof m[n] != "function"){
4638                 this.data[n] = m[n];
4639             }
4640         }
4641         this.dirty = false;
4642         delete this.modified;
4643         this.editing = false;
4644         if(this.store){
4645             this.store.afterReject(this);
4646         }
4647     },
4648
4649     /**
4650      * Usually called by the {@link Roo.data.Store} which owns the Record.
4651      * Commits all changes made to the Record since either creation, or the last commit operation.
4652      * <p>
4653      * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
4654      * of commit operations.
4655      */
4656     commit : function(){
4657         this.dirty = false;
4658         delete this.modified;
4659         this.editing = false;
4660         if(this.store){
4661             this.store.afterCommit(this);
4662         }
4663     },
4664
4665     // private
4666     hasError : function(){
4667         return this.error != null;
4668     },
4669
4670     // private
4671     clearError : function(){
4672         this.error = null;
4673     },
4674
4675     /**
4676      * Creates a copy of this record.
4677      * @param {String} id (optional) A new record id if you don't want to use this record's id
4678      * @return {Record}
4679      */
4680     copy : function(newId) {
4681         return new this.constructor(Roo.apply({}, this.data), newId || this.id);
4682     }
4683 };/*
4684  * Based on:
4685  * Ext JS Library 1.1.1
4686  * Copyright(c) 2006-2007, Ext JS, LLC.
4687  *
4688  * Originally Released Under LGPL - original licence link has changed is not relivant.
4689  *
4690  * Fork - LGPL
4691  * <script type="text/javascript">
4692  */
4693
4694
4695
4696 /**
4697  * @class Roo.data.Store
4698  * @extends Roo.util.Observable
4699  * The Store class encapsulates a client side cache of {@link Roo.data.Record} objects which provide input data
4700  * for widgets such as the Roo.grid.Grid, or the Roo.form.ComboBox.<br>
4701  * <p>
4702  * A Store object uses an implementation of {@link Roo.data.DataProxy} to access a data object unless you call loadData() directly and pass in your data. The Store object
4703  * has no knowledge of the format of the data returned by the Proxy.<br>
4704  * <p>
4705  * A Store object uses its configured implementation of {@link Roo.data.DataReader} to create {@link Roo.data.Record}
4706  * instances from the data object. These records are cached and made available through accessor functions.
4707  * @constructor
4708  * Creates a new Store.
4709  * @param {Object} config A config object containing the objects needed for the Store to access data,
4710  * and read the data into Records.
4711  */
4712 Roo.data.Store = function(config){
4713     this.data = new Roo.util.MixedCollection(false);
4714     this.data.getKey = function(o){
4715         return o.id;
4716     };
4717     this.baseParams = {};
4718     // private
4719     this.paramNames = {
4720         "start" : "start",
4721         "limit" : "limit",
4722         "sort" : "sort",
4723         "dir" : "dir",
4724         "multisort" : "_multisort"
4725     };
4726
4727     if(config && config.data){
4728         this.inlineData = config.data;
4729         delete config.data;
4730     }
4731
4732     Roo.apply(this, config);
4733     
4734     if(this.reader){ // reader passed
4735         this.reader = Roo.factory(this.reader, Roo.data);
4736         this.reader.xmodule = this.xmodule || false;
4737         if(!this.recordType){
4738             this.recordType = this.reader.recordType;
4739         }
4740         if(this.reader.onMetaChange){
4741             this.reader.onMetaChange = this.onMetaChange.createDelegate(this);
4742         }
4743     }
4744
4745     if(this.recordType){
4746         this.fields = this.recordType.prototype.fields;
4747     }
4748     this.modified = [];
4749
4750     this.addEvents({
4751         /**
4752          * @event datachanged
4753          * Fires when the data cache has changed, and a widget which is using this Store
4754          * as a Record cache should refresh its view.
4755          * @param {Store} this
4756          */
4757         datachanged : true,
4758         /**
4759          * @event metachange
4760          * Fires when this store's reader provides new metadata (fields). This is currently only support for JsonReaders.
4761          * @param {Store} this
4762          * @param {Object} meta The JSON metadata
4763          */
4764         metachange : true,
4765         /**
4766          * @event add
4767          * Fires when Records have been added to the Store
4768          * @param {Store} this
4769          * @param {Roo.data.Record[]} records The array of Records added
4770          * @param {Number} index The index at which the record(s) were added
4771          */
4772         add : true,
4773         /**
4774          * @event remove
4775          * Fires when a Record has been removed from the Store
4776          * @param {Store} this
4777          * @param {Roo.data.Record} record The Record that was removed
4778          * @param {Number} index The index at which the record was removed
4779          */
4780         remove : true,
4781         /**
4782          * @event update
4783          * Fires when a Record has been updated
4784          * @param {Store} this
4785          * @param {Roo.data.Record} record The Record that was updated
4786          * @param {String} operation The update operation being performed.  Value may be one of:
4787          * <pre><code>
4788  Roo.data.Record.EDIT
4789  Roo.data.Record.REJECT
4790  Roo.data.Record.COMMIT
4791          * </code></pre>
4792          */
4793         update : true,
4794         /**
4795          * @event clear
4796          * Fires when the data cache has been cleared.
4797          * @param {Store} this
4798          */
4799         clear : true,
4800         /**
4801          * @event beforeload
4802          * Fires before a request is made for a new data object.  If the beforeload handler returns false
4803          * the load action will be canceled.
4804          * @param {Store} this
4805          * @param {Object} options The loading options that were specified (see {@link #load} for details)
4806          */
4807         beforeload : true,
4808         /**
4809          * @event load
4810          * Fires after a new set of Records has been loaded.
4811          * @param {Store} this
4812          * @param {Roo.data.Record[]} records The Records that were loaded
4813          * @param {Object} options The loading options that were specified (see {@link #load} for details)
4814          */
4815         load : true,
4816         /**
4817          * @event loadexception
4818          * Fires if an exception occurs in the Proxy during loading.
4819          * Called with the signature of the Proxy's "loadexception" event.
4820          * If you return Json { data: [] , success: false, .... } then this will be thrown with the following args
4821          * 
4822          * @param {Proxy} 
4823          * @param {Object} return from JsonData.reader() - success, totalRecords, records
4824          * @param {Object} load options 
4825          * @param {Object} jsonData from your request (normally this contains the Exception)
4826          */
4827         loadexception : true
4828     });
4829     
4830     if(this.proxy){
4831         this.proxy = Roo.factory(this.proxy, Roo.data);
4832         this.proxy.xmodule = this.xmodule || false;
4833         this.relayEvents(this.proxy,  ["loadexception"]);
4834     }
4835     this.sortToggle = {};
4836     this.sortOrder = []; // array of order of sorting - updated by grid if multisort is enabled.
4837
4838     Roo.data.Store.superclass.constructor.call(this);
4839
4840     if(this.inlineData){
4841         this.loadData(this.inlineData);
4842         delete this.inlineData;
4843     }
4844 };
4845 Roo.extend(Roo.data.Store, Roo.util.Observable, {
4846      /**
4847     * @cfg {boolean} isLocal   flag if data is locally available (and can be always looked up
4848     * without a remote query - used by combo/forms at present.
4849     */
4850     
4851     /**
4852     * @cfg {Roo.data.DataProxy} proxy The Proxy object which provides access to a data object.
4853     */
4854     /**
4855     * @cfg {Array} data Inline data to be loaded when the store is initialized.
4856     */
4857     /**
4858     * @cfg {Roo.data.Reader} reader The Reader object which processes the data object and returns
4859     * an Array of Roo.data.record objects which are cached keyed by their <em>id</em> property.
4860     */
4861     /**
4862     * @cfg {Object} baseParams An object containing properties which are to be sent as parameters
4863     * on any HTTP request
4864     */
4865     /**
4866     * @cfg {Object} sortInfo A config object in the format: {field: "fieldName", direction: "ASC|DESC"}
4867     */
4868     /**
4869     * @cfg {Boolean} multiSort enable multi column sorting (sort is based on the order of columns, remote only at present)
4870     */
4871     multiSort: false,
4872     /**
4873     * @cfg {boolean} remoteSort True if sorting is to be handled by requesting the Proxy to provide a refreshed
4874     * version of the data object in sorted order, as opposed to sorting the Record cache in place (defaults to false).
4875     */
4876     remoteSort : false,
4877
4878     /**
4879     * @cfg {boolean} pruneModifiedRecords True to clear all modified record information each time the store is
4880      * loaded or when a record is removed. (defaults to false).
4881     */
4882     pruneModifiedRecords : false,
4883
4884     // private
4885     lastOptions : null,
4886
4887     /**
4888      * Add Records to the Store and fires the add event.
4889      * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
4890      */
4891     add : function(records){
4892         records = [].concat(records);
4893         for(var i = 0, len = records.length; i < len; i++){
4894             records[i].join(this);
4895         }
4896         var index = this.data.length;
4897         this.data.addAll(records);
4898         this.fireEvent("add", this, records, index);
4899     },
4900
4901     /**
4902      * Remove a Record from the Store and fires the remove event.
4903      * @param {Ext.data.Record} record The Roo.data.Record object to remove from the cache.
4904      */
4905     remove : function(record){
4906         var index = this.data.indexOf(record);
4907         this.data.removeAt(index);
4908         if(this.pruneModifiedRecords){
4909             this.modified.remove(record);
4910         }
4911         this.fireEvent("remove", this, record, index);
4912     },
4913
4914     /**
4915      * Remove all Records from the Store and fires the clear event.
4916      */
4917     removeAll : function(){
4918         this.data.clear();
4919         if(this.pruneModifiedRecords){
4920             this.modified = [];
4921         }
4922         this.fireEvent("clear", this);
4923     },
4924
4925     /**
4926      * Inserts Records to the Store at the given index and fires the add event.
4927      * @param {Number} index The start index at which to insert the passed Records.
4928      * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
4929      */
4930     insert : function(index, records){
4931         records = [].concat(records);
4932         for(var i = 0, len = records.length; i < len; i++){
4933             this.data.insert(index, records[i]);
4934             records[i].join(this);
4935         }
4936         this.fireEvent("add", this, records, index);
4937     },
4938
4939     /**
4940      * Get the index within the cache of the passed Record.
4941      * @param {Roo.data.Record} record The Roo.data.Record object to to find.
4942      * @return {Number} The index of the passed Record. Returns -1 if not found.
4943      */
4944     indexOf : function(record){
4945         return this.data.indexOf(record);
4946     },
4947
4948     /**
4949      * Get the index within the cache of the Record with the passed id.
4950      * @param {String} id The id of the Record to find.
4951      * @return {Number} The index of the Record. Returns -1 if not found.
4952      */
4953     indexOfId : function(id){
4954         return this.data.indexOfKey(id);
4955     },
4956
4957     /**
4958      * Get the Record with the specified id.
4959      * @param {String} id The id of the Record to find.
4960      * @return {Roo.data.Record} The Record with the passed id. Returns undefined if not found.
4961      */
4962     getById : function(id){
4963         return this.data.key(id);
4964     },
4965
4966     /**
4967      * Get the Record at the specified index.
4968      * @param {Number} index The index of the Record to find.
4969      * @return {Roo.data.Record} The Record at the passed index. Returns undefined if not found.
4970      */
4971     getAt : function(index){
4972         return this.data.itemAt(index);
4973     },
4974
4975     /**
4976      * Returns a range of Records between specified indices.
4977      * @param {Number} startIndex (optional) The starting index (defaults to 0)
4978      * @param {Number} endIndex (optional) The ending index (defaults to the last Record in the Store)
4979      * @return {Roo.data.Record[]} An array of Records
4980      */
4981     getRange : function(start, end){
4982         return this.data.getRange(start, end);
4983     },
4984
4985     // private
4986     storeOptions : function(o){
4987         o = Roo.apply({}, o);
4988         delete o.callback;
4989         delete o.scope;
4990         this.lastOptions = o;
4991     },
4992
4993     /**
4994      * Loads the Record cache from the configured Proxy using the configured Reader.
4995      * <p>
4996      * If using remote paging, then the first load call must specify the <em>start</em>
4997      * and <em>limit</em> properties in the options.params property to establish the initial
4998      * position within the dataset, and the number of Records to cache on each read from the Proxy.
4999      * <p>
5000      * <strong>It is important to note that for remote data sources, loading is asynchronous,
5001      * and this call will return before the new data has been loaded. Perform any post-processing
5002      * in a callback function, or in a "load" event handler.</strong>
5003      * <p>
5004      * @param {Object} options An object containing properties which control loading options:<ul>
5005      * <li>params {Object} An object containing properties to pass as HTTP parameters to a remote data source.</li>
5006      * <li>callback {Function} A function to be called after the Records have been loaded. The callback is
5007      * passed the following arguments:<ul>
5008      * <li>r : Roo.data.Record[]</li>
5009      * <li>options: Options object from the load call</li>
5010      * <li>success: Boolean success indicator</li></ul></li>
5011      * <li>scope {Object} Scope with which to call the callback (defaults to the Store object)</li>
5012      * <li>add {Boolean} indicator to append loaded records rather than replace the current cache.</li>
5013      * </ul>
5014      */
5015     load : function(options){
5016         options = options || {};
5017         if(this.fireEvent("beforeload", this, options) !== false){
5018             this.storeOptions(options);
5019             var p = Roo.apply(options.params || {}, this.baseParams);
5020             // if meta was not loaded from remote source.. try requesting it.
5021             if (!this.reader.metaFromRemote) {
5022                 p._requestMeta = 1;
5023             }
5024             if(this.sortInfo && this.remoteSort){
5025                 var pn = this.paramNames;
5026                 p[pn["sort"]] = this.sortInfo.field;
5027                 p[pn["dir"]] = this.sortInfo.direction;
5028             }
5029             if (this.multiSort) {
5030                 var pn = this.paramNames;
5031                 p[pn["multisort"]] = Roo.encode( { sort : this.sortToggle, order: this.sortOrder });
5032             }
5033             
5034             this.proxy.load(p, this.reader, this.loadRecords, this, options);
5035         }
5036     },
5037
5038     /**
5039      * Reloads the Record cache from the configured Proxy using the configured Reader and
5040      * the options from the last load operation performed.
5041      * @param {Object} options (optional) An object containing properties which may override the options
5042      * used in the last load operation. See {@link #load} for details (defaults to null, in which case
5043      * the most recently used options are reused).
5044      */
5045     reload : function(options){
5046         this.load(Roo.applyIf(options||{}, this.lastOptions));
5047     },
5048
5049     // private
5050     // Called as a callback by the Reader during a load operation.
5051     loadRecords : function(o, options, success){
5052         if(!o || success === false){
5053             if(success !== false){
5054                 this.fireEvent("load", this, [], options);
5055             }
5056             if(options.callback){
5057                 options.callback.call(options.scope || this, [], options, false);
5058             }
5059             return;
5060         }
5061         // if data returned failure - throw an exception.
5062         if (o.success === false) {
5063             this.fireEvent("loadexception", this, o, options, this.reader.jsonData);
5064             return;
5065         }
5066         var r = o.records, t = o.totalRecords || r.length;
5067         if(!options || options.add !== true){
5068             if(this.pruneModifiedRecords){
5069                 this.modified = [];
5070             }
5071             for(var i = 0, len = r.length; i < len; i++){
5072                 r[i].join(this);
5073             }
5074             if(this.snapshot){
5075                 this.data = this.snapshot;
5076                 delete this.snapshot;
5077             }
5078             this.data.clear();
5079             this.data.addAll(r);
5080             this.totalLength = t;
5081             this.applySort();
5082             this.fireEvent("datachanged", this);
5083         }else{
5084             this.totalLength = Math.max(t, this.data.length+r.length);
5085             this.add(r);
5086         }
5087         this.fireEvent("load", this, r, options);
5088         if(options.callback){
5089             options.callback.call(options.scope || this, r, options, true);
5090         }
5091     },
5092
5093     /**
5094      * Loads data from a passed data block. A Reader which understands the format of the data
5095      * must have been configured in the constructor.
5096      * @param {Object} data The data block from which to read the Records.  The format of the data expected
5097      * is dependent on the type of Reader that is configured and should correspond to that Reader's readRecords parameter.
5098      * @param {Boolean} append (Optional) True to append the new Records rather than replace the existing cache.
5099      */
5100     loadData : function(o, append){
5101         var r = this.reader.readRecords(o);
5102         this.loadRecords(r, {add: append}, true);
5103     },
5104
5105     /**
5106      * Gets the number of cached records.
5107      * <p>
5108      * <em>If using paging, this may not be the total size of the dataset. If the data object
5109      * used by the Reader contains the dataset size, then the getTotalCount() function returns
5110      * the data set size</em>
5111      */
5112     getCount : function(){
5113         return this.data.length || 0;
5114     },
5115
5116     /**
5117      * Gets the total number of records in the dataset as returned by the server.
5118      * <p>
5119      * <em>If using paging, for this to be accurate, the data object used by the Reader must contain
5120      * the dataset size</em>
5121      */
5122     getTotalCount : function(){
5123         return this.totalLength || 0;
5124     },
5125
5126     /**
5127      * Returns the sort state of the Store as an object with two properties:
5128      * <pre><code>
5129  field {String} The name of the field by which the Records are sorted
5130  direction {String} The sort order, "ASC" or "DESC"
5131      * </code></pre>
5132      */
5133     getSortState : function(){
5134         return this.sortInfo;
5135     },
5136
5137     // private
5138     applySort : function(){
5139         if(this.sortInfo && !this.remoteSort){
5140             var s = this.sortInfo, f = s.field;
5141             var st = this.fields.get(f).sortType;
5142             var fn = function(r1, r2){
5143                 var v1 = st(r1.data[f]), v2 = st(r2.data[f]);
5144                 return v1 > v2 ? 1 : (v1 < v2 ? -1 : 0);
5145             };
5146             this.data.sort(s.direction, fn);
5147             if(this.snapshot && this.snapshot != this.data){
5148                 this.snapshot.sort(s.direction, fn);
5149             }
5150         }
5151     },
5152
5153     /**
5154      * Sets the default sort column and order to be used by the next load operation.
5155      * @param {String} fieldName The name of the field to sort by.
5156      * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
5157      */
5158     setDefaultSort : function(field, dir){
5159         this.sortInfo = {field: field, direction: dir ? dir.toUpperCase() : "ASC"};
5160     },
5161
5162     /**
5163      * Sort the Records.
5164      * If remote sorting is used, the sort is performed on the server, and the cache is
5165      * reloaded. If local sorting is used, the cache is sorted internally.
5166      * @param {String} fieldName The name of the field to sort by.
5167      * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
5168      */
5169     sort : function(fieldName, dir){
5170         var f = this.fields.get(fieldName);
5171         if(!dir){
5172             this.sortToggle[f.name] = this.sortToggle[f.name] || f.sortDir;
5173             
5174             if(this.multiSort || (this.sortInfo && this.sortInfo.field == f.name) ){ // toggle sort dir
5175                 dir = (this.sortToggle[f.name] || "ASC").toggle("ASC", "DESC");
5176             }else{
5177                 dir = f.sortDir;
5178             }
5179         }
5180         this.sortToggle[f.name] = dir;
5181         this.sortInfo = {field: f.name, direction: dir};
5182         if(!this.remoteSort){
5183             this.applySort();
5184             this.fireEvent("datachanged", this);
5185         }else{
5186             this.load(this.lastOptions);
5187         }
5188     },
5189
5190     /**
5191      * Calls the specified function for each of the Records in the cache.
5192      * @param {Function} fn The function to call. The Record is passed as the first parameter.
5193      * Returning <em>false</em> aborts and exits the iteration.
5194      * @param {Object} scope (optional) The scope in which to call the function (defaults to the Record).
5195      */
5196     each : function(fn, scope){
5197         this.data.each(fn, scope);
5198     },
5199
5200     /**
5201      * Gets all records modified since the last commit.  Modified records are persisted across load operations
5202      * (e.g., during paging).
5203      * @return {Roo.data.Record[]} An array of Records containing outstanding modifications.
5204      */
5205     getModifiedRecords : function(){
5206         return this.modified;
5207     },
5208
5209     // private
5210     createFilterFn : function(property, value, anyMatch){
5211         if(!value.exec){ // not a regex
5212             value = String(value);
5213             if(value.length == 0){
5214                 return false;
5215             }
5216             value = new RegExp((anyMatch === true ? '' : '^') + Roo.escapeRe(value), "i");
5217         }
5218         return function(r){
5219             return value.test(r.data[property]);
5220         };
5221     },
5222
5223     /**
5224      * Sums the value of <i>property</i> for each record between start and end and returns the result.
5225      * @param {String} property A field on your records
5226      * @param {Number} start The record index to start at (defaults to 0)
5227      * @param {Number} end The last record index to include (defaults to length - 1)
5228      * @return {Number} The sum
5229      */
5230     sum : function(property, start, end){
5231         var rs = this.data.items, v = 0;
5232         start = start || 0;
5233         end = (end || end === 0) ? end : rs.length-1;
5234
5235         for(var i = start; i <= end; i++){
5236             v += (rs[i].data[property] || 0);
5237         }
5238         return v;
5239     },
5240
5241     /**
5242      * Filter the records by a specified property.
5243      * @param {String} field A field on your records
5244      * @param {String/RegExp} value Either a string that the field
5245      * should start with or a RegExp to test against the field
5246      * @param {Boolean} anyMatch True to match any part not just the beginning
5247      */
5248     filter : function(property, value, anyMatch){
5249         var fn = this.createFilterFn(property, value, anyMatch);
5250         return fn ? this.filterBy(fn) : this.clearFilter();
5251     },
5252
5253     /**
5254      * Filter by a function. The specified function will be called with each
5255      * record in this data source. If the function returns true the record is included,
5256      * otherwise it is filtered.
5257      * @param {Function} fn The function to be called, it will receive 2 args (record, id)
5258      * @param {Object} scope (optional) The scope of the function (defaults to this)
5259      */
5260     filterBy : function(fn, scope){
5261         this.snapshot = this.snapshot || this.data;
5262         this.data = this.queryBy(fn, scope||this);
5263         this.fireEvent("datachanged", this);
5264     },
5265
5266     /**
5267      * Query the records by a specified property.
5268      * @param {String} field A field on your records
5269      * @param {String/RegExp} value Either a string that the field
5270      * should start with or a RegExp to test against the field
5271      * @param {Boolean} anyMatch True to match any part not just the beginning
5272      * @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
5273      */
5274     query : function(property, value, anyMatch){
5275         var fn = this.createFilterFn(property, value, anyMatch);
5276         return fn ? this.queryBy(fn) : this.data.clone();
5277     },
5278
5279     /**
5280      * Query by a function. The specified function will be called with each
5281      * record in this data source. If the function returns true the record is included
5282      * in the results.
5283      * @param {Function} fn The function to be called, it will receive 2 args (record, id)
5284      * @param {Object} scope (optional) The scope of the function (defaults to this)
5285       @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
5286      **/
5287     queryBy : function(fn, scope){
5288         var data = this.snapshot || this.data;
5289         return data.filterBy(fn, scope||this);
5290     },
5291
5292     /**
5293      * Collects unique values for a particular dataIndex from this store.
5294      * @param {String} dataIndex The property to collect
5295      * @param {Boolean} allowNull (optional) Pass true to allow null, undefined or empty string values
5296      * @param {Boolean} bypassFilter (optional) Pass true to collect from all records, even ones which are filtered
5297      * @return {Array} An array of the unique values
5298      **/
5299     collect : function(dataIndex, allowNull, bypassFilter){
5300         var d = (bypassFilter === true && this.snapshot) ?
5301                 this.snapshot.items : this.data.items;
5302         var v, sv, r = [], l = {};
5303         for(var i = 0, len = d.length; i < len; i++){
5304             v = d[i].data[dataIndex];
5305             sv = String(v);
5306             if((allowNull || !Roo.isEmpty(v)) && !l[sv]){
5307                 l[sv] = true;
5308                 r[r.length] = v;
5309             }
5310         }
5311         return r;
5312     },
5313
5314     /**
5315      * Revert to a view of the Record cache with no filtering applied.
5316      * @param {Boolean} suppressEvent If true the filter is cleared silently without notifying listeners
5317      */
5318     clearFilter : function(suppressEvent){
5319         if(this.snapshot && this.snapshot != this.data){
5320             this.data = this.snapshot;
5321             delete this.snapshot;
5322             if(suppressEvent !== true){
5323                 this.fireEvent("datachanged", this);
5324             }
5325         }
5326     },
5327
5328     // private
5329     afterEdit : function(record){
5330         if(this.modified.indexOf(record) == -1){
5331             this.modified.push(record);
5332         }
5333         this.fireEvent("update", this, record, Roo.data.Record.EDIT);
5334     },
5335
5336     // private
5337     afterReject : function(record){
5338         this.modified.remove(record);
5339         this.fireEvent("update", this, record, Roo.data.Record.REJECT);
5340     },
5341
5342     // private
5343     afterCommit : function(record){
5344         this.modified.remove(record);
5345         this.fireEvent("update", this, record, Roo.data.Record.COMMIT);
5346     },
5347
5348     /**
5349      * Commit all Records with outstanding changes. To handle updates for changes, subscribe to the
5350      * Store's "update" event, and perform updating when the third parameter is Roo.data.Record.COMMIT.
5351      */
5352     commitChanges : function(){
5353         var m = this.modified.slice(0);
5354         this.modified = [];
5355         for(var i = 0, len = m.length; i < len; i++){
5356             m[i].commit();
5357         }
5358     },
5359
5360     /**
5361      * Cancel outstanding changes on all changed records.
5362      */
5363     rejectChanges : function(){
5364         var m = this.modified.slice(0);
5365         this.modified = [];
5366         for(var i = 0, len = m.length; i < len; i++){
5367             m[i].reject();
5368         }
5369     },
5370
5371     onMetaChange : function(meta, rtype, o){
5372         this.recordType = rtype;
5373         this.fields = rtype.prototype.fields;
5374         delete this.snapshot;
5375         this.sortInfo = meta.sortInfo || this.sortInfo;
5376         this.modified = [];
5377         this.fireEvent('metachange', this, this.reader.meta);
5378     }
5379 });/*
5380  * Based on:
5381  * Ext JS Library 1.1.1
5382  * Copyright(c) 2006-2007, Ext JS, LLC.
5383  *
5384  * Originally Released Under LGPL - original licence link has changed is not relivant.
5385  *
5386  * Fork - LGPL
5387  * <script type="text/javascript">
5388  */
5389
5390 /**
5391  * @class Roo.data.SimpleStore
5392  * @extends Roo.data.Store
5393  * Small helper class to make creating Stores from Array data easier.
5394  * @cfg {Number} id The array index of the record id. Leave blank to auto generate ids.
5395  * @cfg {Array} fields An array of field definition objects, or field name strings.
5396  * @cfg {Array} data The multi-dimensional array of data
5397  * @constructor
5398  * @param {Object} config
5399  */
5400 Roo.data.SimpleStore = function(config){
5401     Roo.data.SimpleStore.superclass.constructor.call(this, {
5402         isLocal : true,
5403         reader: new Roo.data.ArrayReader({
5404                 id: config.id
5405             },
5406             Roo.data.Record.create(config.fields)
5407         ),
5408         proxy : new Roo.data.MemoryProxy(config.data)
5409     });
5410     this.load();
5411 };
5412 Roo.extend(Roo.data.SimpleStore, Roo.data.Store);/*
5413  * Based on:
5414  * Ext JS Library 1.1.1
5415  * Copyright(c) 2006-2007, Ext JS, LLC.
5416  *
5417  * Originally Released Under LGPL - original licence link has changed is not relivant.
5418  *
5419  * Fork - LGPL
5420  * <script type="text/javascript">
5421  */
5422
5423 /**
5424 /**
5425  * @extends Roo.data.Store
5426  * @class Roo.data.JsonStore
5427  * Small helper class to make creating Stores for JSON data easier. <br/>
5428 <pre><code>
5429 var store = new Roo.data.JsonStore({
5430     url: 'get-images.php',
5431     root: 'images',
5432     fields: ['name', 'url', {name:'size', type: 'float'}, {name:'lastmod', type:'date'}]
5433 });
5434 </code></pre>
5435  * <b>Note: Although they are not listed, this class inherits all of the config options of Store,
5436  * JsonReader and HttpProxy (unless inline data is provided).</b>
5437  * @cfg {Array} fields An array of field definition objects, or field name strings.
5438  * @constructor
5439  * @param {Object} config
5440  */
5441 Roo.data.JsonStore = function(c){
5442     Roo.data.JsonStore.superclass.constructor.call(this, Roo.apply(c, {
5443         proxy: !c.data ? new Roo.data.HttpProxy({url: c.url}) : undefined,
5444         reader: new Roo.data.JsonReader(c, c.fields)
5445     }));
5446 };
5447 Roo.extend(Roo.data.JsonStore, Roo.data.Store);/*
5448  * Based on:
5449  * Ext JS Library 1.1.1
5450  * Copyright(c) 2006-2007, Ext JS, LLC.
5451  *
5452  * Originally Released Under LGPL - original licence link has changed is not relivant.
5453  *
5454  * Fork - LGPL
5455  * <script type="text/javascript">
5456  */
5457
5458  
5459 Roo.data.Field = function(config){
5460     if(typeof config == "string"){
5461         config = {name: config};
5462     }
5463     Roo.apply(this, config);
5464     
5465     if(!this.type){
5466         this.type = "auto";
5467     }
5468     
5469     var st = Roo.data.SortTypes;
5470     // named sortTypes are supported, here we look them up
5471     if(typeof this.sortType == "string"){
5472         this.sortType = st[this.sortType];
5473     }
5474     
5475     // set default sortType for strings and dates
5476     if(!this.sortType){
5477         switch(this.type){
5478             case "string":
5479                 this.sortType = st.asUCString;
5480                 break;
5481             case "date":
5482                 this.sortType = st.asDate;
5483                 break;
5484             default:
5485                 this.sortType = st.none;
5486         }
5487     }
5488
5489     // define once
5490     var stripRe = /[\$,%]/g;
5491
5492     // prebuilt conversion function for this field, instead of
5493     // switching every time we're reading a value
5494     if(!this.convert){
5495         var cv, dateFormat = this.dateFormat;
5496         switch(this.type){
5497             case "":
5498             case "auto":
5499             case undefined:
5500                 cv = function(v){ return v; };
5501                 break;
5502             case "string":
5503                 cv = function(v){ return (v === undefined || v === null) ? '' : String(v); };
5504                 break;
5505             case "int":
5506                 cv = function(v){
5507                     return v !== undefined && v !== null && v !== '' ?
5508                            parseInt(String(v).replace(stripRe, ""), 10) : '';
5509                     };
5510                 break;
5511             case "float":
5512                 cv = function(v){
5513                     return v !== undefined && v !== null && v !== '' ?
5514                            parseFloat(String(v).replace(stripRe, ""), 10) : ''; 
5515                     };
5516                 break;
5517             case "bool":
5518             case "boolean":
5519                 cv = function(v){ return v === true || v === "true" || v == 1; };
5520                 break;
5521             case "date":
5522                 cv = function(v){
5523                     if(!v){
5524                         return '';
5525                     }
5526                     if(v instanceof Date){
5527                         return v;
5528                     }
5529                     if(dateFormat){
5530                         if(dateFormat == "timestamp"){
5531                             return new Date(v*1000);
5532                         }
5533                         return Date.parseDate(v, dateFormat);
5534                     }
5535                     var parsed = Date.parse(v);
5536                     return parsed ? new Date(parsed) : null;
5537                 };
5538              break;
5539             
5540         }
5541         this.convert = cv;
5542     }
5543 };
5544
5545 Roo.data.Field.prototype = {
5546     dateFormat: null,
5547     defaultValue: "",
5548     mapping: null,
5549     sortType : null,
5550     sortDir : "ASC"
5551 };/*
5552  * Based on:
5553  * Ext JS Library 1.1.1
5554  * Copyright(c) 2006-2007, Ext JS, LLC.
5555  *
5556  * Originally Released Under LGPL - original licence link has changed is not relivant.
5557  *
5558  * Fork - LGPL
5559  * <script type="text/javascript">
5560  */
5561  
5562 // Base class for reading structured data from a data source.  This class is intended to be
5563 // extended (see ArrayReader, JsonReader and XmlReader) and should not be created directly.
5564
5565 /**
5566  * @class Roo.data.DataReader
5567  * Base class for reading structured data from a data source.  This class is intended to be
5568  * extended (see {Roo.data.ArrayReader}, {Roo.data.JsonReader} and {Roo.data.XmlReader}) and should not be created directly.
5569  */
5570
5571 Roo.data.DataReader = function(meta, recordType){
5572     
5573     this.meta = meta;
5574     
5575     this.recordType = recordType instanceof Array ? 
5576         Roo.data.Record.create(recordType) : recordType;
5577 };
5578
5579 Roo.data.DataReader.prototype = {
5580      /**
5581      * Create an empty record
5582      * @param {Object} data (optional) - overlay some values
5583      * @return {Roo.data.Record} record created.
5584      */
5585     newRow :  function(d) {
5586         var da =  {};
5587         this.recordType.prototype.fields.each(function(c) {
5588             switch( c.type) {
5589                 case 'int' : da[c.name] = 0; break;
5590                 case 'date' : da[c.name] = new Date(); break;
5591                 case 'float' : da[c.name] = 0.0; break;
5592                 case 'boolean' : da[c.name] = false; break;
5593                 default : da[c.name] = ""; break;
5594             }
5595             
5596         });
5597         return new this.recordType(Roo.apply(da, d));
5598     }
5599     
5600 };/*
5601  * Based on:
5602  * Ext JS Library 1.1.1
5603  * Copyright(c) 2006-2007, Ext JS, LLC.
5604  *
5605  * Originally Released Under LGPL - original licence link has changed is not relivant.
5606  *
5607  * Fork - LGPL
5608  * <script type="text/javascript">
5609  */
5610
5611 /**
5612  * @class Roo.data.DataProxy
5613  * @extends Roo.data.Observable
5614  * This class is an abstract base class for implementations which provide retrieval of
5615  * unformatted data objects.<br>
5616  * <p>
5617  * DataProxy implementations are usually used in conjunction with an implementation of Roo.data.DataReader
5618  * (of the appropriate type which knows how to parse the data object) to provide a block of
5619  * {@link Roo.data.Records} to an {@link Roo.data.Store}.<br>
5620  * <p>
5621  * Custom implementations must implement the load method as described in
5622  * {@link Roo.data.HttpProxy#load}.
5623  */
5624 Roo.data.DataProxy = function(){
5625     this.addEvents({
5626         /**
5627          * @event beforeload
5628          * Fires before a network request is made to retrieve a data object.
5629          * @param {Object} This DataProxy object.
5630          * @param {Object} params The params parameter to the load function.
5631          */
5632         beforeload : true,
5633         /**
5634          * @event load
5635          * Fires before the load method's callback is called.
5636          * @param {Object} This DataProxy object.
5637          * @param {Object} o The data object.
5638          * @param {Object} arg The callback argument object passed to the load function.
5639          */
5640         load : true,
5641         /**
5642          * @event loadexception
5643          * Fires if an Exception occurs during data retrieval.
5644          * @param {Object} This DataProxy object.
5645          * @param {Object} o The data object.
5646          * @param {Object} arg The callback argument object passed to the load function.
5647          * @param {Object} e The Exception.
5648          */
5649         loadexception : true
5650     });
5651     Roo.data.DataProxy.superclass.constructor.call(this);
5652 };
5653
5654 Roo.extend(Roo.data.DataProxy, Roo.util.Observable);
5655
5656     /**
5657      * @cfg {void} listeners (Not available) Constructor blocks listeners from being set
5658      */
5659 /*
5660  * Based on:
5661  * Ext JS Library 1.1.1
5662  * Copyright(c) 2006-2007, Ext JS, LLC.
5663  *
5664  * Originally Released Under LGPL - original licence link has changed is not relivant.
5665  *
5666  * Fork - LGPL
5667  * <script type="text/javascript">
5668  */
5669 /**
5670  * @class Roo.data.MemoryProxy
5671  * An implementation of Roo.data.DataProxy that simply passes the data specified in its constructor
5672  * to the Reader when its load method is called.
5673  * @constructor
5674  * @param {Object} data The data object which the Reader uses to construct a block of Roo.data.Records.
5675  */
5676 Roo.data.MemoryProxy = function(data){
5677     if (data.data) {
5678         data = data.data;
5679     }
5680     Roo.data.MemoryProxy.superclass.constructor.call(this);
5681     this.data = data;
5682 };
5683
5684 Roo.extend(Roo.data.MemoryProxy, Roo.data.DataProxy, {
5685     /**
5686      * Load data from the requested source (in this case an in-memory
5687      * data object passed to the constructor), read the data object into
5688      * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
5689      * process that block using the passed callback.
5690      * @param {Object} params This parameter is not used by the MemoryProxy class.
5691      * @param {Roo.data.DataReader} reader The Reader object which converts the data
5692      * object into a block of Roo.data.Records.
5693      * @param {Function} callback The function into which to pass the block of Roo.data.records.
5694      * The function must be passed <ul>
5695      * <li>The Record block object</li>
5696      * <li>The "arg" argument from the load function</li>
5697      * <li>A boolean success indicator</li>
5698      * </ul>
5699      * @param {Object} scope The scope in which to call the callback
5700      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
5701      */
5702     load : function(params, reader, callback, scope, arg){
5703         params = params || {};
5704         var result;
5705         try {
5706             result = reader.readRecords(this.data);
5707         }catch(e){
5708             this.fireEvent("loadexception", this, arg, null, e);
5709             callback.call(scope, null, arg, false);
5710             return;
5711         }
5712         callback.call(scope, result, arg, true);
5713     },
5714     
5715     // private
5716     update : function(params, records){
5717         
5718     }
5719 });/*
5720  * Based on:
5721  * Ext JS Library 1.1.1
5722  * Copyright(c) 2006-2007, Ext JS, LLC.
5723  *
5724  * Originally Released Under LGPL - original licence link has changed is not relivant.
5725  *
5726  * Fork - LGPL
5727  * <script type="text/javascript">
5728  */
5729 /**
5730  * @class Roo.data.HttpProxy
5731  * @extends Roo.data.DataProxy
5732  * An implementation of {@link Roo.data.DataProxy} that reads a data object from an {@link Roo.data.Connection} object
5733  * configured to reference a certain URL.<br><br>
5734  * <p>
5735  * <em>Note that this class cannot be used to retrieve data from a domain other than the domain
5736  * from which the running page was served.<br><br>
5737  * <p>
5738  * For cross-domain access to remote data, use an {@link Roo.data.ScriptTagProxy}.</em><br><br>
5739  * <p>
5740  * Be aware that to enable the browser to parse an XML document, the server must set
5741  * the Content-Type header in the HTTP response to "text/xml".
5742  * @constructor
5743  * @param {Object} conn Connection config options to add to each request (e.g. {url: 'foo.php'} or
5744  * an {@link Roo.data.Connection} object.  If a Connection config is passed, the singleton {@link Roo.Ajax} object
5745  * will be used to make the request.
5746  */
5747 Roo.data.HttpProxy = function(conn){
5748     Roo.data.HttpProxy.superclass.constructor.call(this);
5749     // is conn a conn config or a real conn?
5750     this.conn = conn;
5751     this.useAjax = !conn || !conn.events;
5752   
5753 };
5754
5755 Roo.extend(Roo.data.HttpProxy, Roo.data.DataProxy, {
5756     // thse are take from connection...
5757     
5758     /**
5759      * @cfg {String} url (Optional) The default URL to be used for requests to the server. (defaults to undefined)
5760      */
5761     /**
5762      * @cfg {Object} extraParams (Optional) An object containing properties which are used as
5763      * extra parameters to each request made by this object. (defaults to undefined)
5764      */
5765     /**
5766      * @cfg {Object} defaultHeaders (Optional) An object containing request headers which are added
5767      *  to each request made by this object. (defaults to undefined)
5768      */
5769     /**
5770      * @cfg {String} method (Optional) The default HTTP method to be used for requests. (defaults to undefined; if not set but parms are present will use POST, otherwise GET)
5771      */
5772     /**
5773      * @cfg {Number} timeout (Optional) The timeout in milliseconds to be used for requests. (defaults to 30000)
5774      */
5775      /**
5776      * @cfg {Boolean} autoAbort (Optional) Whether this request should abort any pending requests. (defaults to false)
5777      * @type Boolean
5778      */
5779   
5780
5781     /**
5782      * @cfg {Boolean} disableCaching (Optional) True to add a unique cache-buster param to GET requests. (defaults to true)
5783      * @type Boolean
5784      */
5785     /**
5786      * Return the {@link Roo.data.Connection} object being used by this Proxy.
5787      * @return {Connection} The Connection object. This object may be used to subscribe to events on
5788      * a finer-grained basis than the DataProxy events.
5789      */
5790     getConnection : function(){
5791         return this.useAjax ? Roo.Ajax : this.conn;
5792     },
5793
5794     /**
5795      * Load data from the configured {@link Roo.data.Connection}, read the data object into
5796      * a block of Roo.data.Records using the passed {@link Roo.data.DataReader} implementation, and
5797      * process that block using the passed callback.
5798      * @param {Object} params An object containing properties which are to be used as HTTP parameters
5799      * for the request to the remote server.
5800      * @param {Roo.data.DataReader} reader The Reader object which converts the data
5801      * object into a block of Roo.data.Records.
5802      * @param {Function} callback The function into which to pass the block of Roo.data.Records.
5803      * The function must be passed <ul>
5804      * <li>The Record block object</li>
5805      * <li>The "arg" argument from the load function</li>
5806      * <li>A boolean success indicator</li>
5807      * </ul>
5808      * @param {Object} scope The scope in which to call the callback
5809      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
5810      */
5811     load : function(params, reader, callback, scope, arg){
5812         if(this.fireEvent("beforeload", this, params) !== false){
5813             var  o = {
5814                 params : params || {},
5815                 request: {
5816                     callback : callback,
5817                     scope : scope,
5818                     arg : arg
5819                 },
5820                 reader: reader,
5821                 callback : this.loadResponse,
5822                 scope: this
5823             };
5824             if(this.useAjax){
5825                 Roo.applyIf(o, this.conn);
5826                 if(this.activeRequest){
5827                     Roo.Ajax.abort(this.activeRequest);
5828                 }
5829                 this.activeRequest = Roo.Ajax.request(o);
5830             }else{
5831                 this.conn.request(o);
5832             }
5833         }else{
5834             callback.call(scope||this, null, arg, false);
5835         }
5836     },
5837
5838     // private
5839     loadResponse : function(o, success, response){
5840         delete this.activeRequest;
5841         if(!success){
5842             this.fireEvent("loadexception", this, o, response);
5843             o.request.callback.call(o.request.scope, null, o.request.arg, false);
5844             return;
5845         }
5846         var result;
5847         try {
5848             result = o.reader.read(response);
5849         }catch(e){
5850             this.fireEvent("loadexception", this, o, response, e);
5851             o.request.callback.call(o.request.scope, null, o.request.arg, false);
5852             return;
5853         }
5854         
5855         this.fireEvent("load", this, o, o.request.arg);
5856         o.request.callback.call(o.request.scope, result, o.request.arg, true);
5857     },
5858
5859     // private
5860     update : function(dataSet){
5861
5862     },
5863
5864     // private
5865     updateResponse : function(dataSet){
5866
5867     }
5868 });/*
5869  * Based on:
5870  * Ext JS Library 1.1.1
5871  * Copyright(c) 2006-2007, Ext JS, LLC.
5872  *
5873  * Originally Released Under LGPL - original licence link has changed is not relivant.
5874  *
5875  * Fork - LGPL
5876  * <script type="text/javascript">
5877  */
5878
5879 /**
5880  * @class Roo.data.ScriptTagProxy
5881  * An implementation of Roo.data.DataProxy that reads a data object from a URL which may be in a domain
5882  * other than the originating domain of the running page.<br><br>
5883  * <p>
5884  * <em>Note that if you are retrieving data from a page that is in a domain that is NOT the same as the originating domain
5885  * of the running page, you must use this class, rather than DataProxy.</em><br><br>
5886  * <p>
5887  * The content passed back from a server resource requested by a ScriptTagProxy is executable JavaScript
5888  * source code that is used as the source inside a &lt;script> tag.<br><br>
5889  * <p>
5890  * In order for the browser to process the returned data, the server must wrap the data object
5891  * with a call to a callback function, the name of which is passed as a parameter by the ScriptTagProxy.
5892  * Below is a Java example for a servlet which returns data for either a ScriptTagProxy, or an HttpProxy
5893  * depending on whether the callback name was passed:
5894  * <p>
5895  * <pre><code>
5896 boolean scriptTag = false;
5897 String cb = request.getParameter("callback");
5898 if (cb != null) {
5899     scriptTag = true;
5900     response.setContentType("text/javascript");
5901 } else {
5902     response.setContentType("application/x-json");
5903 }
5904 Writer out = response.getWriter();
5905 if (scriptTag) {
5906     out.write(cb + "(");
5907 }
5908 out.print(dataBlock.toJsonString());
5909 if (scriptTag) {
5910     out.write(");");
5911 }
5912 </pre></code>
5913  *
5914  * @constructor
5915  * @param {Object} config A configuration object.
5916  */
5917 Roo.data.ScriptTagProxy = function(config){
5918     Roo.data.ScriptTagProxy.superclass.constructor.call(this);
5919     Roo.apply(this, config);
5920     this.head = document.getElementsByTagName("head")[0];
5921 };
5922
5923 Roo.data.ScriptTagProxy.TRANS_ID = 1000;
5924
5925 Roo.extend(Roo.data.ScriptTagProxy, Roo.data.DataProxy, {
5926     /**
5927      * @cfg {String} url The URL from which to request the data object.
5928      */
5929     /**
5930      * @cfg {Number} timeout (Optional) The number of milliseconds to wait for a response. Defaults to 30 seconds.
5931      */
5932     timeout : 30000,
5933     /**
5934      * @cfg {String} callbackParam (Optional) The name of the parameter to pass to the server which tells
5935      * the server the name of the callback function set up by the load call to process the returned data object.
5936      * Defaults to "callback".<p>The server-side processing must read this parameter value, and generate
5937      * javascript output which calls this named function passing the data object as its only parameter.
5938      */
5939     callbackParam : "callback",
5940     /**
5941      *  @cfg {Boolean} nocache (Optional) Defaults to true. Disable cacheing by adding a unique parameter
5942      * name to the request.
5943      */
5944     nocache : true,
5945
5946     /**
5947      * Load data from the configured URL, read the data object into
5948      * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
5949      * process that block using the passed callback.
5950      * @param {Object} params An object containing properties which are to be used as HTTP parameters
5951      * for the request to the remote server.
5952      * @param {Roo.data.DataReader} reader The Reader object which converts the data
5953      * object into a block of Roo.data.Records.
5954      * @param {Function} callback The function into which to pass the block of Roo.data.Records.
5955      * The function must be passed <ul>
5956      * <li>The Record block object</li>
5957      * <li>The "arg" argument from the load function</li>
5958      * <li>A boolean success indicator</li>
5959      * </ul>
5960      * @param {Object} scope The scope in which to call the callback
5961      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
5962      */
5963     load : function(params, reader, callback, scope, arg){
5964         if(this.fireEvent("beforeload", this, params) !== false){
5965
5966             var p = Roo.urlEncode(Roo.apply(params, this.extraParams));
5967
5968             var url = this.url;
5969             url += (url.indexOf("?") != -1 ? "&" : "?") + p;
5970             if(this.nocache){
5971                 url += "&_dc=" + (new Date().getTime());
5972             }
5973             var transId = ++Roo.data.ScriptTagProxy.TRANS_ID;
5974             var trans = {
5975                 id : transId,
5976                 cb : "stcCallback"+transId,
5977                 scriptId : "stcScript"+transId,
5978                 params : params,
5979                 arg : arg,
5980                 url : url,
5981                 callback : callback,
5982                 scope : scope,
5983                 reader : reader
5984             };
5985             var conn = this;
5986
5987             window[trans.cb] = function(o){
5988                 conn.handleResponse(o, trans);
5989             };
5990
5991             url += String.format("&{0}={1}", this.callbackParam, trans.cb);
5992
5993             if(this.autoAbort !== false){
5994                 this.abort();
5995             }
5996
5997             trans.timeoutId = this.handleFailure.defer(this.timeout, this, [trans]);
5998
5999             var script = document.createElement("script");
6000             script.setAttribute("src", url);
6001             script.setAttribute("type", "text/javascript");
6002             script.setAttribute("id", trans.scriptId);
6003             this.head.appendChild(script);
6004
6005             this.trans = trans;
6006         }else{
6007             callback.call(scope||this, null, arg, false);
6008         }
6009     },
6010
6011     // private
6012     isLoading : function(){
6013         return this.trans ? true : false;
6014     },
6015
6016     /**
6017      * Abort the current server request.
6018      */
6019     abort : function(){
6020         if(this.isLoading()){
6021             this.destroyTrans(this.trans);
6022         }
6023     },
6024
6025     // private
6026     destroyTrans : function(trans, isLoaded){
6027         this.head.removeChild(document.getElementById(trans.scriptId));
6028         clearTimeout(trans.timeoutId);
6029         if(isLoaded){
6030             window[trans.cb] = undefined;
6031             try{
6032                 delete window[trans.cb];
6033             }catch(e){}
6034         }else{
6035             // if hasn't been loaded, wait for load to remove it to prevent script error
6036             window[trans.cb] = function(){
6037                 window[trans.cb] = undefined;
6038                 try{
6039                     delete window[trans.cb];
6040                 }catch(e){}
6041             };
6042         }
6043     },
6044
6045     // private
6046     handleResponse : function(o, trans){
6047         this.trans = false;
6048         this.destroyTrans(trans, true);
6049         var result;
6050         try {
6051             result = trans.reader.readRecords(o);
6052         }catch(e){
6053             this.fireEvent("loadexception", this, o, trans.arg, e);
6054             trans.callback.call(trans.scope||window, null, trans.arg, false);
6055             return;
6056         }
6057         this.fireEvent("load", this, o, trans.arg);
6058         trans.callback.call(trans.scope||window, result, trans.arg, true);
6059     },
6060
6061     // private
6062     handleFailure : function(trans){
6063         this.trans = false;
6064         this.destroyTrans(trans, false);
6065         this.fireEvent("loadexception", this, null, trans.arg);
6066         trans.callback.call(trans.scope||window, null, trans.arg, false);
6067     }
6068 });/*
6069  * Based on:
6070  * Ext JS Library 1.1.1
6071  * Copyright(c) 2006-2007, Ext JS, LLC.
6072  *
6073  * Originally Released Under LGPL - original licence link has changed is not relivant.
6074  *
6075  * Fork - LGPL
6076  * <script type="text/javascript">
6077  */
6078
6079 /**
6080  * @class Roo.data.JsonReader
6081  * @extends Roo.data.DataReader
6082  * Data reader class to create an Array of Roo.data.Record objects from a JSON response
6083  * based on mappings in a provided Roo.data.Record constructor.
6084  * 
6085  * The default behaviour of a store is to send ?_requestMeta=1, unless the class has recieved 'metaData' property
6086  * in the reply previously. 
6087  * 
6088  * <p>
6089  * Example code:
6090  * <pre><code>
6091 var RecordDef = Roo.data.Record.create([
6092     {name: 'name', mapping: 'name'},     // "mapping" property not needed if it's the same as "name"
6093     {name: 'occupation'}                 // This field will use "occupation" as the mapping.
6094 ]);
6095 var myReader = new Roo.data.JsonReader({
6096     totalProperty: "results",    // The property which contains the total dataset size (optional)
6097     root: "rows",                // The property which contains an Array of row objects
6098     id: "id"                     // The property within each row object that provides an ID for the record (optional)
6099 }, RecordDef);
6100 </code></pre>
6101  * <p>
6102  * This would consume a JSON file like this:
6103  * <pre><code>
6104 { 'results': 2, 'rows': [
6105     { 'id': 1, 'name': 'Bill', occupation: 'Gardener' },
6106     { 'id': 2, 'name': 'Ben', occupation: 'Horticulturalist' } ]
6107 }
6108 </code></pre>
6109  * @cfg {String} totalProperty Name of the property from which to retrieve the total number of records
6110  * in the dataset. This is only needed if the whole dataset is not passed in one go, but is being
6111  * paged from the remote server.
6112  * @cfg {String} successProperty Name of the property from which to retrieve the success attribute used by forms.
6113  * @cfg {String} root name of the property which contains the Array of row objects.
6114  * @cfg {String} id Name of the property within a row object that contains a record identifier value.
6115  * @constructor
6116  * Create a new JsonReader
6117  * @param {Object} meta Metadata configuration options
6118  * @param {Object} recordType Either an Array of field definition objects,
6119  * or an {@link Roo.data.Record} object created using {@link Roo.data.Record#create}.
6120  */
6121 Roo.data.JsonReader = function(meta, recordType){
6122     
6123     meta = meta || {};
6124     // set some defaults:
6125     Roo.applyIf(meta, {
6126         totalProperty: 'total',
6127         successProperty : 'success',
6128         root : 'data',
6129         id : 'id'
6130     });
6131     
6132     Roo.data.JsonReader.superclass.constructor.call(this, meta, recordType||meta.fields);
6133 };
6134 Roo.extend(Roo.data.JsonReader, Roo.data.DataReader, {
6135     
6136     /**
6137      * @prop {Boolean} metaFromRemote  - if the meta data was loaded from the remote source.
6138      * Used by Store query builder to append _requestMeta to params.
6139      * 
6140      */
6141     metaFromRemote : false,
6142     /**
6143      * This method is only used by a DataProxy which has retrieved data from a remote server.
6144      * @param {Object} response The XHR object which contains the JSON data in its responseText.
6145      * @return {Object} data A data block which is used by an Roo.data.Store object as
6146      * a cache of Roo.data.Records.
6147      */
6148     read : function(response){
6149         var json = response.responseText;
6150        
6151         var o = /* eval:var:o */ eval("("+json+")");
6152         if(!o) {
6153             throw {message: "JsonReader.read: Json object not found"};
6154         }
6155         
6156         if(o.metaData){
6157             
6158             delete this.ef;
6159             this.metaFromRemote = true;
6160             this.meta = o.metaData;
6161             this.recordType = Roo.data.Record.create(o.metaData.fields);
6162             this.onMetaChange(this.meta, this.recordType, o);
6163         }
6164         return this.readRecords(o);
6165     },
6166
6167     // private function a store will implement
6168     onMetaChange : function(meta, recordType, o){
6169
6170     },
6171
6172     /**
6173          * @ignore
6174          */
6175     simpleAccess: function(obj, subsc) {
6176         return obj[subsc];
6177     },
6178
6179         /**
6180          * @ignore
6181          */
6182     getJsonAccessor: function(){
6183         var re = /[\[\.]/;
6184         return function(expr) {
6185             try {
6186                 return(re.test(expr))
6187                     ? new Function("obj", "return obj." + expr)
6188                     : function(obj){
6189                         return obj[expr];
6190                     };
6191             } catch(e){}
6192             return Roo.emptyFn;
6193         };
6194     }(),
6195
6196     /**
6197      * Create a data block containing Roo.data.Records from an XML document.
6198      * @param {Object} o An object which contains an Array of row objects in the property specified
6199      * in the config as 'root, and optionally a property, specified in the config as 'totalProperty'
6200      * which contains the total size of the dataset.
6201      * @return {Object} data A data block which is used by an Roo.data.Store object as
6202      * a cache of Roo.data.Records.
6203      */
6204     readRecords : function(o){
6205         /**
6206          * After any data loads, the raw JSON data is available for further custom processing.
6207          * @type Object
6208          */
6209         this.jsonData = o;
6210         var s = this.meta, Record = this.recordType,
6211             f = Record.prototype.fields, fi = f.items, fl = f.length;
6212
6213 //      Generate extraction functions for the totalProperty, the root, the id, and for each field
6214         if (!this.ef) {
6215             if(s.totalProperty) {
6216                     this.getTotal = this.getJsonAccessor(s.totalProperty);
6217                 }
6218                 if(s.successProperty) {
6219                     this.getSuccess = this.getJsonAccessor(s.successProperty);
6220                 }
6221                 this.getRoot = s.root ? this.getJsonAccessor(s.root) : function(p){return p;};
6222                 if (s.id) {
6223                         var g = this.getJsonAccessor(s.id);
6224                         this.getId = function(rec) {
6225                                 var r = g(rec);
6226                                 return (r === undefined || r === "") ? null : r;
6227                         };
6228                 } else {
6229                         this.getId = function(){return null;};
6230                 }
6231             this.ef = [];
6232             for(var jj = 0; jj < fl; jj++){
6233                 f = fi[jj];
6234                 var map = (f.mapping !== undefined && f.mapping !== null) ? f.mapping : f.name;
6235                 this.ef[jj] = this.getJsonAccessor(map);
6236             }
6237         }
6238
6239         var root = this.getRoot(o), c = root.length, totalRecords = c, success = true;
6240         if(s.totalProperty){
6241             var vt = parseInt(this.getTotal(o), 10);
6242             if(!isNaN(vt)){
6243                 totalRecords = vt;
6244             }
6245         }
6246         if(s.successProperty){
6247             var vs = this.getSuccess(o);
6248             if(vs === false || vs === 'false'){
6249                 success = false;
6250             }
6251         }
6252         var records = [];
6253             for(var i = 0; i < c; i++){
6254                     var n = root[i];
6255                 var values = {};
6256                 var id = this.getId(n);
6257                 for(var j = 0; j < fl; j++){
6258                     f = fi[j];
6259                 var v = this.ef[j](n);
6260                 if (!f.convert) {
6261                     Roo.log('missing convert for ' + f.name);
6262                     Roo.log(f);
6263                     continue;
6264                 }
6265                 values[f.name] = f.convert((v !== undefined) ? v : f.defaultValue);
6266                 }
6267                 var record = new Record(values, id);
6268                 record.json = n;
6269                 records[i] = record;
6270             }
6271             return {
6272                 success : success,
6273                 records : records,
6274                 totalRecords : totalRecords
6275             };
6276     }
6277 });/*
6278  * Based on:
6279  * Ext JS Library 1.1.1
6280  * Copyright(c) 2006-2007, Ext JS, LLC.
6281  *
6282  * Originally Released Under LGPL - original licence link has changed is not relivant.
6283  *
6284  * Fork - LGPL
6285  * <script type="text/javascript">
6286  */
6287
6288 /**
6289  * @class Roo.data.XmlReader
6290  * @extends Roo.data.DataReader
6291  * Data reader class to create an Array of {@link Roo.data.Record} objects from an XML document
6292  * based on mappings in a provided Roo.data.Record constructor.<br><br>
6293  * <p>
6294  * <em>Note that in order for the browser to parse a returned XML document, the Content-Type
6295  * header in the HTTP response must be set to "text/xml".</em>
6296  * <p>
6297  * Example code:
6298  * <pre><code>
6299 var RecordDef = Roo.data.Record.create([
6300    {name: 'name', mapping: 'name'},     // "mapping" property not needed if it's the same as "name"
6301    {name: 'occupation'}                 // This field will use "occupation" as the mapping.
6302 ]);
6303 var myReader = new Roo.data.XmlReader({
6304    totalRecords: "results", // The element which contains the total dataset size (optional)
6305    record: "row",           // The repeated element which contains row information
6306    id: "id"                 // The element within the row that provides an ID for the record (optional)
6307 }, RecordDef);
6308 </code></pre>
6309  * <p>
6310  * This would consume an XML file like this:
6311  * <pre><code>
6312 &lt;?xml?>
6313 &lt;dataset>
6314  &lt;results>2&lt;/results>
6315  &lt;row>
6316    &lt;id>1&lt;/id>
6317    &lt;name>Bill&lt;/name>
6318    &lt;occupation>Gardener&lt;/occupation>
6319  &lt;/row>
6320  &lt;row>
6321    &lt;id>2&lt;/id>
6322    &lt;name>Ben&lt;/name>
6323    &lt;occupation>Horticulturalist&lt;/occupation>
6324  &lt;/row>
6325 &lt;/dataset>
6326 </code></pre>
6327  * @cfg {String} totalRecords The DomQuery path from which to retrieve the total number of records
6328  * in the dataset. This is only needed if the whole dataset is not passed in one go, but is being
6329  * paged from the remote server.
6330  * @cfg {String} record The DomQuery path to the repeated element which contains record information.
6331  * @cfg {String} success The DomQuery path to the success attribute used by forms.
6332  * @cfg {String} id The DomQuery path relative from the record element to the element that contains
6333  * a record identifier value.
6334  * @constructor
6335  * Create a new XmlReader
6336  * @param {Object} meta Metadata configuration options
6337  * @param {Mixed} recordType The definition of the data record type to produce.  This can be either a valid
6338  * Record subclass created with {@link Roo.data.Record#create}, or an array of objects with which to call
6339  * Roo.data.Record.create.  See the {@link Roo.data.Record} class for more details.
6340  */
6341 Roo.data.XmlReader = function(meta, recordType){
6342     meta = meta || {};
6343     Roo.data.XmlReader.superclass.constructor.call(this, meta, recordType||meta.fields);
6344 };
6345 Roo.extend(Roo.data.XmlReader, Roo.data.DataReader, {
6346     /**
6347      * This method is only used by a DataProxy which has retrieved data from a remote server.
6348          * @param {Object} response The XHR object which contains the parsed XML document.  The response is expected
6349          * to contain a method called 'responseXML' that returns an XML document object.
6350      * @return {Object} records A data block which is used by an {@link Roo.data.Store} as
6351      * a cache of Roo.data.Records.
6352      */
6353     read : function(response){
6354         var doc = response.responseXML;
6355         if(!doc) {
6356             throw {message: "XmlReader.read: XML Document not available"};
6357         }
6358         return this.readRecords(doc);
6359     },
6360
6361     /**
6362      * Create a data block containing Roo.data.Records from an XML document.
6363          * @param {Object} doc A parsed XML document.
6364      * @return {Object} records A data block which is used by an {@link Roo.data.Store} as
6365      * a cache of Roo.data.Records.
6366      */
6367     readRecords : function(doc){
6368         /**
6369          * After any data loads/reads, the raw XML Document is available for further custom processing.
6370          * @type XMLDocument
6371          */
6372         this.xmlData = doc;
6373         var root = doc.documentElement || doc;
6374         var q = Roo.DomQuery;
6375         var recordType = this.recordType, fields = recordType.prototype.fields;
6376         var sid = this.meta.id;
6377         var totalRecords = 0, success = true;
6378         if(this.meta.totalRecords){
6379             totalRecords = q.selectNumber(this.meta.totalRecords, root, 0);
6380         }
6381         
6382         if(this.meta.success){
6383             var sv = q.selectValue(this.meta.success, root, true);
6384             success = sv !== false && sv !== 'false';
6385         }
6386         var records = [];
6387         var ns = q.select(this.meta.record, root);
6388         for(var i = 0, len = ns.length; i < len; i++) {
6389                 var n = ns[i];
6390                 var values = {};
6391                 var id = sid ? q.selectValue(sid, n) : undefined;
6392                 for(var j = 0, jlen = fields.length; j < jlen; j++){
6393                     var f = fields.items[j];
6394                 var v = q.selectValue(f.mapping || f.name, n, f.defaultValue);
6395                     v = f.convert(v);
6396                     values[f.name] = v;
6397                 }
6398                 var record = new recordType(values, id);
6399                 record.node = n;
6400                 records[records.length] = record;
6401             }
6402
6403             return {
6404                 success : success,
6405                 records : records,
6406                 totalRecords : totalRecords || records.length
6407             };
6408     }
6409 });/*
6410  * Based on:
6411  * Ext JS Library 1.1.1
6412  * Copyright(c) 2006-2007, Ext JS, LLC.
6413  *
6414  * Originally Released Under LGPL - original licence link has changed is not relivant.
6415  *
6416  * Fork - LGPL
6417  * <script type="text/javascript">
6418  */
6419
6420 /**
6421  * @class Roo.data.ArrayReader
6422  * @extends Roo.data.DataReader
6423  * Data reader class to create an Array of Roo.data.Record objects from an Array.
6424  * Each element of that Array represents a row of data fields. The
6425  * fields are pulled into a Record object using as a subscript, the <em>mapping</em> property
6426  * of the field definition if it exists, or the field's ordinal position in the definition.<br>
6427  * <p>
6428  * Example code:.
6429  * <pre><code>
6430 var RecordDef = Roo.data.Record.create([
6431     {name: 'name', mapping: 1},         // "mapping" only needed if an "id" field is present which
6432     {name: 'occupation', mapping: 2}    // precludes using the ordinal position as the index.
6433 ]);
6434 var myReader = new Roo.data.ArrayReader({
6435     id: 0                     // The subscript within row Array that provides an ID for the Record (optional)
6436 }, RecordDef);
6437 </code></pre>
6438  * <p>
6439  * This would consume an Array like this:
6440  * <pre><code>
6441 [ [1, 'Bill', 'Gardener'], [2, 'Ben', 'Horticulturalist'] ]
6442   </code></pre>
6443  * @cfg {String} id (optional) The subscript within row Array that provides an ID for the Record
6444  * @constructor
6445  * Create a new JsonReader
6446  * @param {Object} meta Metadata configuration options.
6447  * @param {Object} recordType Either an Array of field definition objects
6448  * as specified to {@link Roo.data.Record#create},
6449  * or an {@link Roo.data.Record} object
6450  * created using {@link Roo.data.Record#create}.
6451  */
6452 Roo.data.ArrayReader = function(meta, recordType){
6453     Roo.data.ArrayReader.superclass.constructor.call(this, meta, recordType);
6454 };
6455
6456 Roo.extend(Roo.data.ArrayReader, Roo.data.JsonReader, {
6457     /**
6458      * Create a data block containing Roo.data.Records from an XML document.
6459      * @param {Object} o An Array of row objects which represents the dataset.
6460      * @return {Object} data A data block which is used by an Roo.data.Store object as
6461      * a cache of Roo.data.Records.
6462      */
6463     readRecords : function(o){
6464         var sid = this.meta ? this.meta.id : null;
6465         var recordType = this.recordType, fields = recordType.prototype.fields;
6466         var records = [];
6467         var root = o;
6468             for(var i = 0; i < root.length; i++){
6469                     var n = root[i];
6470                 var values = {};
6471                 var id = ((sid || sid === 0) && n[sid] !== undefined && n[sid] !== "" ? n[sid] : null);
6472                 for(var j = 0, jlen = fields.length; j < jlen; j++){
6473                 var f = fields.items[j];
6474                 var k = f.mapping !== undefined && f.mapping !== null ? f.mapping : j;
6475                 var v = n[k] !== undefined ? n[k] : f.defaultValue;
6476                 v = f.convert(v);
6477                 values[f.name] = v;
6478             }
6479                 var record = new recordType(values, id);
6480                 record.json = n;
6481                 records[records.length] = record;
6482             }
6483             return {
6484                 records : records,
6485                 totalRecords : records.length
6486             };
6487     }
6488 });/*
6489  * Based on:
6490  * Ext JS Library 1.1.1
6491  * Copyright(c) 2006-2007, Ext JS, LLC.
6492  *
6493  * Originally Released Under LGPL - original licence link has changed is not relivant.
6494  *
6495  * Fork - LGPL
6496  * <script type="text/javascript">
6497  */
6498
6499
6500 /**
6501  * @class Roo.data.Tree
6502  * @extends Roo.util.Observable
6503  * Represents a tree data structure and bubbles all the events for its nodes. The nodes
6504  * in the tree have most standard DOM functionality.
6505  * @constructor
6506  * @param {Node} root (optional) The root node
6507  */
6508 Roo.data.Tree = function(root){
6509    this.nodeHash = {};
6510    /**
6511     * The root node for this tree
6512     * @type Node
6513     */
6514    this.root = null;
6515    if(root){
6516        this.setRootNode(root);
6517    }
6518    this.addEvents({
6519        /**
6520         * @event append
6521         * Fires when a new child node is appended to a node in this tree.
6522         * @param {Tree} tree The owner tree
6523         * @param {Node} parent The parent node
6524         * @param {Node} node The newly appended node
6525         * @param {Number} index The index of the newly appended node
6526         */
6527        "append" : true,
6528        /**
6529         * @event remove
6530         * Fires when a child node is removed from a node in this tree.
6531         * @param {Tree} tree The owner tree
6532         * @param {Node} parent The parent node
6533         * @param {Node} node The child node removed
6534         */
6535        "remove" : true,
6536        /**
6537         * @event move
6538         * Fires when a node is moved to a new location in the tree
6539         * @param {Tree} tree The owner tree
6540         * @param {Node} node The node moved
6541         * @param {Node} oldParent The old parent of this node
6542         * @param {Node} newParent The new parent of this node
6543         * @param {Number} index The index it was moved to
6544         */
6545        "move" : true,
6546        /**
6547         * @event insert
6548         * Fires when a new child node is inserted in a node in this tree.
6549         * @param {Tree} tree The owner tree
6550         * @param {Node} parent The parent node
6551         * @param {Node} node The child node inserted
6552         * @param {Node} refNode The child node the node was inserted before
6553         */
6554        "insert" : true,
6555        /**
6556         * @event beforeappend
6557         * Fires before a new child is appended to a node in this tree, return false to cancel the append.
6558         * @param {Tree} tree The owner tree
6559         * @param {Node} parent The parent node
6560         * @param {Node} node The child node to be appended
6561         */
6562        "beforeappend" : true,
6563        /**
6564         * @event beforeremove
6565         * Fires before a child is removed from a node in this tree, return false to cancel the remove.
6566         * @param {Tree} tree The owner tree
6567         * @param {Node} parent The parent node
6568         * @param {Node} node The child node to be removed
6569         */
6570        "beforeremove" : true,
6571        /**
6572         * @event beforemove
6573         * Fires before a node is moved to a new location in the tree. Return false to cancel the move.
6574         * @param {Tree} tree The owner tree
6575         * @param {Node} node The node being moved
6576         * @param {Node} oldParent The parent of the node
6577         * @param {Node} newParent The new parent the node is moving to
6578         * @param {Number} index The index it is being moved to
6579         */
6580        "beforemove" : true,
6581        /**
6582         * @event beforeinsert
6583         * Fires before a new child is inserted in a node in this tree, return false to cancel the insert.
6584         * @param {Tree} tree The owner tree
6585         * @param {Node} parent The parent node
6586         * @param {Node} node The child node to be inserted
6587         * @param {Node} refNode The child node the node is being inserted before
6588         */
6589        "beforeinsert" : true
6590    });
6591
6592     Roo.data.Tree.superclass.constructor.call(this);
6593 };
6594
6595 Roo.extend(Roo.data.Tree, Roo.util.Observable, {
6596     pathSeparator: "/",
6597
6598     proxyNodeEvent : function(){
6599         return this.fireEvent.apply(this, arguments);
6600     },
6601
6602     /**
6603      * Returns the root node for this tree.
6604      * @return {Node}
6605      */
6606     getRootNode : function(){
6607         return this.root;
6608     },
6609
6610     /**
6611      * Sets the root node for this tree.
6612      * @param {Node} node
6613      * @return {Node}
6614      */
6615     setRootNode : function(node){
6616         this.root = node;
6617         node.ownerTree = this;
6618         node.isRoot = true;
6619         this.registerNode(node);
6620         return node;
6621     },
6622
6623     /**
6624      * Gets a node in this tree by its id.
6625      * @param {String} id
6626      * @return {Node}
6627      */
6628     getNodeById : function(id){
6629         return this.nodeHash[id];
6630     },
6631
6632     registerNode : function(node){
6633         this.nodeHash[node.id] = node;
6634     },
6635
6636     unregisterNode : function(node){
6637         delete this.nodeHash[node.id];
6638     },
6639
6640     toString : function(){
6641         return "[Tree"+(this.id?" "+this.id:"")+"]";
6642     }
6643 });
6644
6645 /**
6646  * @class Roo.data.Node
6647  * @extends Roo.util.Observable
6648  * @cfg {Boolean} leaf true if this node is a leaf and does not have children
6649  * @cfg {String} id The id for this node. If one is not specified, one is generated.
6650  * @constructor
6651  * @param {Object} attributes The attributes/config for the node
6652  */
6653 Roo.data.Node = function(attributes){
6654     /**
6655      * The attributes supplied for the node. You can use this property to access any custom attributes you supplied.
6656      * @type {Object}
6657      */
6658     this.attributes = attributes || {};
6659     this.leaf = this.attributes.leaf;
6660     /**
6661      * The node id. @type String
6662      */
6663     this.id = this.attributes.id;
6664     if(!this.id){
6665         this.id = Roo.id(null, "ynode-");
6666         this.attributes.id = this.id;
6667     }
6668     /**
6669      * All child nodes of this node. @type Array
6670      */
6671     this.childNodes = [];
6672     if(!this.childNodes.indexOf){ // indexOf is a must
6673         this.childNodes.indexOf = function(o){
6674             for(var i = 0, len = this.length; i < len; i++){
6675                 if(this[i] == o) {
6676                     return i;
6677                 }
6678             }
6679             return -1;
6680         };
6681     }
6682     /**
6683      * The parent node for this node. @type Node
6684      */
6685     this.parentNode = null;
6686     /**
6687      * The first direct child node of this node, or null if this node has no child nodes. @type Node
6688      */
6689     this.firstChild = null;
6690     /**
6691      * The last direct child node of this node, or null if this node has no child nodes. @type Node
6692      */
6693     this.lastChild = null;
6694     /**
6695      * The node immediately preceding this node in the tree, or null if there is no sibling node. @type Node
6696      */
6697     this.previousSibling = null;
6698     /**
6699      * The node immediately following this node in the tree, or null if there is no sibling node. @type Node
6700      */
6701     this.nextSibling = null;
6702
6703     this.addEvents({
6704        /**
6705         * @event append
6706         * Fires when a new child node is appended
6707         * @param {Tree} tree The owner tree
6708         * @param {Node} this This node
6709         * @param {Node} node The newly appended node
6710         * @param {Number} index The index of the newly appended node
6711         */
6712        "append" : true,
6713        /**
6714         * @event remove
6715         * Fires when a child node is removed
6716         * @param {Tree} tree The owner tree
6717         * @param {Node} this This node
6718         * @param {Node} node The removed node
6719         */
6720        "remove" : true,
6721        /**
6722         * @event move
6723         * Fires when this node is moved to a new location in the tree
6724         * @param {Tree} tree The owner tree
6725         * @param {Node} this This node
6726         * @param {Node} oldParent The old parent of this node
6727         * @param {Node} newParent The new parent of this node
6728         * @param {Number} index The index it was moved to
6729         */
6730        "move" : true,
6731        /**
6732         * @event insert
6733         * Fires when a new child node is inserted.
6734         * @param {Tree} tree The owner tree
6735         * @param {Node} this This node
6736         * @param {Node} node The child node inserted
6737         * @param {Node} refNode The child node the node was inserted before
6738         */
6739        "insert" : true,
6740        /**
6741         * @event beforeappend
6742         * Fires before a new child is appended, return false to cancel the append.
6743         * @param {Tree} tree The owner tree
6744         * @param {Node} this This node
6745         * @param {Node} node The child node to be appended
6746         */
6747        "beforeappend" : true,
6748        /**
6749         * @event beforeremove
6750         * Fires before a child is removed, return false to cancel the remove.
6751         * @param {Tree} tree The owner tree
6752         * @param {Node} this This node
6753         * @param {Node} node The child node to be removed
6754         */
6755        "beforeremove" : true,
6756        /**
6757         * @event beforemove
6758         * Fires before this node is moved to a new location in the tree. Return false to cancel the move.
6759         * @param {Tree} tree The owner tree
6760         * @param {Node} this This node
6761         * @param {Node} oldParent The parent of this node
6762         * @param {Node} newParent The new parent this node is moving to
6763         * @param {Number} index The index it is being moved to
6764         */
6765        "beforemove" : true,
6766        /**
6767         * @event beforeinsert
6768         * Fires before a new child is inserted, return false to cancel the insert.
6769         * @param {Tree} tree The owner tree
6770         * @param {Node} this This node
6771         * @param {Node} node The child node to be inserted
6772         * @param {Node} refNode The child node the node is being inserted before
6773         */
6774        "beforeinsert" : true
6775    });
6776     this.listeners = this.attributes.listeners;
6777     Roo.data.Node.superclass.constructor.call(this);
6778 };
6779
6780 Roo.extend(Roo.data.Node, Roo.util.Observable, {
6781     fireEvent : function(evtName){
6782         // first do standard event for this node
6783         if(Roo.data.Node.superclass.fireEvent.apply(this, arguments) === false){
6784             return false;
6785         }
6786         // then bubble it up to the tree if the event wasn't cancelled
6787         var ot = this.getOwnerTree();
6788         if(ot){
6789             if(ot.proxyNodeEvent.apply(ot, arguments) === false){
6790                 return false;
6791             }
6792         }
6793         return true;
6794     },
6795
6796     /**
6797      * Returns true if this node is a leaf
6798      * @return {Boolean}
6799      */
6800     isLeaf : function(){
6801         return this.leaf === true;
6802     },
6803
6804     // private
6805     setFirstChild : function(node){
6806         this.firstChild = node;
6807     },
6808
6809     //private
6810     setLastChild : function(node){
6811         this.lastChild = node;
6812     },
6813
6814
6815     /**
6816      * Returns true if this node is the last child of its parent
6817      * @return {Boolean}
6818      */
6819     isLast : function(){
6820        return (!this.parentNode ? true : this.parentNode.lastChild == this);
6821     },
6822
6823     /**
6824      * Returns true if this node is the first child of its parent
6825      * @return {Boolean}
6826      */
6827     isFirst : function(){
6828        return (!this.parentNode ? true : this.parentNode.firstChild == this);
6829     },
6830
6831     hasChildNodes : function(){
6832         return !this.isLeaf() && this.childNodes.length > 0;
6833     },
6834
6835     /**
6836      * Insert node(s) as the last child node of this node.
6837      * @param {Node/Array} node The node or Array of nodes to append
6838      * @return {Node} The appended node if single append, or null if an array was passed
6839      */
6840     appendChild : function(node){
6841         var multi = false;
6842         if(node instanceof Array){
6843             multi = node;
6844         }else if(arguments.length > 1){
6845             multi = arguments;
6846         }
6847         // if passed an array or multiple args do them one by one
6848         if(multi){
6849             for(var i = 0, len = multi.length; i < len; i++) {
6850                 this.appendChild(multi[i]);
6851             }
6852         }else{
6853             if(this.fireEvent("beforeappend", this.ownerTree, this, node) === false){
6854                 return false;
6855             }
6856             var index = this.childNodes.length;
6857             var oldParent = node.parentNode;
6858             // it's a move, make sure we move it cleanly
6859             if(oldParent){
6860                 if(node.fireEvent("beforemove", node.getOwnerTree(), node, oldParent, this, index) === false){
6861                     return false;
6862                 }
6863                 oldParent.removeChild(node);
6864             }
6865             index = this.childNodes.length;
6866             if(index == 0){
6867                 this.setFirstChild(node);
6868             }
6869             this.childNodes.push(node);
6870             node.parentNode = this;
6871             var ps = this.childNodes[index-1];
6872             if(ps){
6873                 node.previousSibling = ps;
6874                 ps.nextSibling = node;
6875             }else{
6876                 node.previousSibling = null;
6877             }
6878             node.nextSibling = null;
6879             this.setLastChild(node);
6880             node.setOwnerTree(this.getOwnerTree());
6881             this.fireEvent("append", this.ownerTree, this, node, index);
6882             if(oldParent){
6883                 node.fireEvent("move", this.ownerTree, node, oldParent, this, index);
6884             }
6885             return node;
6886         }
6887     },
6888
6889     /**
6890      * Removes a child node from this node.
6891      * @param {Node} node The node to remove
6892      * @return {Node} The removed node
6893      */
6894     removeChild : function(node){
6895         var index = this.childNodes.indexOf(node);
6896         if(index == -1){
6897             return false;
6898         }
6899         if(this.fireEvent("beforeremove", this.ownerTree, this, node) === false){
6900             return false;
6901         }
6902
6903         // remove it from childNodes collection
6904         this.childNodes.splice(index, 1);
6905
6906         // update siblings
6907         if(node.previousSibling){
6908             node.previousSibling.nextSibling = node.nextSibling;
6909         }
6910         if(node.nextSibling){
6911             node.nextSibling.previousSibling = node.previousSibling;
6912         }
6913
6914         // update child refs
6915         if(this.firstChild == node){
6916             this.setFirstChild(node.nextSibling);
6917         }
6918         if(this.lastChild == node){
6919             this.setLastChild(node.previousSibling);
6920         }
6921
6922         node.setOwnerTree(null);
6923         // clear any references from the node
6924         node.parentNode = null;
6925         node.previousSibling = null;
6926         node.nextSibling = null;
6927         this.fireEvent("remove", this.ownerTree, this, node);
6928         return node;
6929     },
6930
6931     /**
6932      * Inserts the first node before the second node in this nodes childNodes collection.
6933      * @param {Node} node The node to insert
6934      * @param {Node} refNode The node to insert before (if null the node is appended)
6935      * @return {Node} The inserted node
6936      */
6937     insertBefore : function(node, refNode){
6938         if(!refNode){ // like standard Dom, refNode can be null for append
6939             return this.appendChild(node);
6940         }
6941         // nothing to do
6942         if(node == refNode){
6943             return false;
6944         }
6945
6946         if(this.fireEvent("beforeinsert", this.ownerTree, this, node, refNode) === false){
6947             return false;
6948         }
6949         var index = this.childNodes.indexOf(refNode);
6950         var oldParent = node.parentNode;
6951         var refIndex = index;
6952
6953         // when moving internally, indexes will change after remove
6954         if(oldParent == this && this.childNodes.indexOf(node) < index){
6955             refIndex--;
6956         }
6957
6958         // it's a move, make sure we move it cleanly
6959         if(oldParent){
6960             if(node.fireEvent("beforemove", node.getOwnerTree(), node, oldParent, this, index, refNode) === false){
6961                 return false;
6962             }
6963             oldParent.removeChild(node);
6964         }
6965         if(refIndex == 0){
6966             this.setFirstChild(node);
6967         }
6968         this.childNodes.splice(refIndex, 0, node);
6969         node.parentNode = this;
6970         var ps = this.childNodes[refIndex-1];
6971         if(ps){
6972             node.previousSibling = ps;
6973             ps.nextSibling = node;
6974         }else{
6975             node.previousSibling = null;
6976         }
6977         node.nextSibling = refNode;
6978         refNode.previousSibling = node;
6979         node.setOwnerTree(this.getOwnerTree());
6980         this.fireEvent("insert", this.ownerTree, this, node, refNode);
6981         if(oldParent){
6982             node.fireEvent("move", this.ownerTree, node, oldParent, this, refIndex, refNode);
6983         }
6984         return node;
6985     },
6986
6987     /**
6988      * Returns the child node at the specified index.
6989      * @param {Number} index
6990      * @return {Node}
6991      */
6992     item : function(index){
6993         return this.childNodes[index];
6994     },
6995
6996     /**
6997      * Replaces one child node in this node with another.
6998      * @param {Node} newChild The replacement node
6999      * @param {Node} oldChild The node to replace
7000      * @return {Node} The replaced node
7001      */
7002     replaceChild : function(newChild, oldChild){
7003         this.insertBefore(newChild, oldChild);
7004         this.removeChild(oldChild);
7005         return oldChild;
7006     },
7007
7008     /**
7009      * Returns the index of a child node
7010      * @param {Node} node
7011      * @return {Number} The index of the node or -1 if it was not found
7012      */
7013     indexOf : function(child){
7014         return this.childNodes.indexOf(child);
7015     },
7016
7017     /**
7018      * Returns the tree this node is in.
7019      * @return {Tree}
7020      */
7021     getOwnerTree : function(){
7022         // if it doesn't have one, look for one
7023         if(!this.ownerTree){
7024             var p = this;
7025             while(p){
7026                 if(p.ownerTree){
7027                     this.ownerTree = p.ownerTree;
7028                     break;
7029                 }
7030                 p = p.parentNode;
7031             }
7032         }
7033         return this.ownerTree;
7034     },
7035
7036     /**
7037      * Returns depth of this node (the root node has a depth of 0)
7038      * @return {Number}
7039      */
7040     getDepth : function(){
7041         var depth = 0;
7042         var p = this;
7043         while(p.parentNode){
7044             ++depth;
7045             p = p.parentNode;
7046         }
7047         return depth;
7048     },
7049
7050     // private
7051     setOwnerTree : function(tree){
7052         // if it's move, we need to update everyone
7053         if(tree != this.ownerTree){
7054             if(this.ownerTree){
7055                 this.ownerTree.unregisterNode(this);
7056             }
7057             this.ownerTree = tree;
7058             var cs = this.childNodes;
7059             for(var i = 0, len = cs.length; i < len; i++) {
7060                 cs[i].setOwnerTree(tree);
7061             }
7062             if(tree){
7063                 tree.registerNode(this);
7064             }
7065         }
7066     },
7067
7068     /**
7069      * Returns the path for this node. The path can be used to expand or select this node programmatically.
7070      * @param {String} attr (optional) The attr to use for the path (defaults to the node's id)
7071      * @return {String} The path
7072      */
7073     getPath : function(attr){
7074         attr = attr || "id";
7075         var p = this.parentNode;
7076         var b = [this.attributes[attr]];
7077         while(p){
7078             b.unshift(p.attributes[attr]);
7079             p = p.parentNode;
7080         }
7081         var sep = this.getOwnerTree().pathSeparator;
7082         return sep + b.join(sep);
7083     },
7084
7085     /**
7086      * Bubbles up the tree from this node, calling the specified function with each node. The scope (<i>this</i>) of
7087      * function call will be the scope provided or the current node. The arguments to the function
7088      * will be the args provided or the current node. If the function returns false at any point,
7089      * the bubble is stopped.
7090      * @param {Function} fn The function to call
7091      * @param {Object} scope (optional) The scope of the function (defaults to current node)
7092      * @param {Array} args (optional) The args to call the function with (default to passing the current node)
7093      */
7094     bubble : function(fn, scope, args){
7095         var p = this;
7096         while(p){
7097             if(fn.call(scope || p, args || p) === false){
7098                 break;
7099             }
7100             p = p.parentNode;
7101         }
7102     },
7103
7104     /**
7105      * Cascades down the tree from this node, calling the specified function with each node. The scope (<i>this</i>) of
7106      * function call will be the scope provided or the current node. The arguments to the function
7107      * will be the args provided or the current node. If the function returns false at any point,
7108      * the cascade is stopped on that branch.
7109      * @param {Function} fn The function to call
7110      * @param {Object} scope (optional) The scope of the function (defaults to current node)
7111      * @param {Array} args (optional) The args to call the function with (default to passing the current node)
7112      */
7113     cascade : function(fn, scope, args){
7114         if(fn.call(scope || this, args || this) !== false){
7115             var cs = this.childNodes;
7116             for(var i = 0, len = cs.length; i < len; i++) {
7117                 cs[i].cascade(fn, scope, args);
7118             }
7119         }
7120     },
7121
7122     /**
7123      * Interates the child nodes of this node, calling the specified function with each node. The scope (<i>this</i>) of
7124      * function call will be the scope provided or the current node. The arguments to the function
7125      * will be the args provided or the current node. If the function returns false at any point,
7126      * the iteration stops.
7127      * @param {Function} fn The function to call
7128      * @param {Object} scope (optional) The scope of the function (defaults to current node)
7129      * @param {Array} args (optional) The args to call the function with (default to passing the current node)
7130      */
7131     eachChild : function(fn, scope, args){
7132         var cs = this.childNodes;
7133         for(var i = 0, len = cs.length; i < len; i++) {
7134                 if(fn.call(scope || this, args || cs[i]) === false){
7135                     break;
7136                 }
7137         }
7138     },
7139
7140     /**
7141      * Finds the first child that has the attribute with the specified value.
7142      * @param {String} attribute The attribute name
7143      * @param {Mixed} value The value to search for
7144      * @return {Node} The found child or null if none was found
7145      */
7146     findChild : function(attribute, value){
7147         var cs = this.childNodes;
7148         for(var i = 0, len = cs.length; i < len; i++) {
7149                 if(cs[i].attributes[attribute] == value){
7150                     return cs[i];
7151                 }
7152         }
7153         return null;
7154     },
7155
7156     /**
7157      * Finds the first child by a custom function. The child matches if the function passed
7158      * returns true.
7159      * @param {Function} fn
7160      * @param {Object} scope (optional)
7161      * @return {Node} The found child or null if none was found
7162      */
7163     findChildBy : function(fn, scope){
7164         var cs = this.childNodes;
7165         for(var i = 0, len = cs.length; i < len; i++) {
7166                 if(fn.call(scope||cs[i], cs[i]) === true){
7167                     return cs[i];
7168                 }
7169         }
7170         return null;
7171     },
7172
7173     /**
7174      * Sorts this nodes children using the supplied sort function
7175      * @param {Function} fn
7176      * @param {Object} scope (optional)
7177      */
7178     sort : function(fn, scope){
7179         var cs = this.childNodes;
7180         var len = cs.length;
7181         if(len > 0){
7182             var sortFn = scope ? function(){fn.apply(scope, arguments);} : fn;
7183             cs.sort(sortFn);
7184             for(var i = 0; i < len; i++){
7185                 var n = cs[i];
7186                 n.previousSibling = cs[i-1];
7187                 n.nextSibling = cs[i+1];
7188                 if(i == 0){
7189                     this.setFirstChild(n);
7190                 }
7191                 if(i == len-1){
7192                     this.setLastChild(n);
7193                 }
7194             }
7195         }
7196     },
7197
7198     /**
7199      * Returns true if this node is an ancestor (at any point) of the passed node.
7200      * @param {Node} node
7201      * @return {Boolean}
7202      */
7203     contains : function(node){
7204         return node.isAncestor(this);
7205     },
7206
7207     /**
7208      * Returns true if the passed node is an ancestor (at any point) of this node.
7209      * @param {Node} node
7210      * @return {Boolean}
7211      */
7212     isAncestor : function(node){
7213         var p = this.parentNode;
7214         while(p){
7215             if(p == node){
7216                 return true;
7217             }
7218             p = p.parentNode;
7219         }
7220         return false;
7221     },
7222
7223     toString : function(){
7224         return "[Node"+(this.id?" "+this.id:"")+"]";
7225     }
7226 });/*
7227  * Based on:
7228  * Ext JS Library 1.1.1
7229  * Copyright(c) 2006-2007, Ext JS, LLC.
7230  *
7231  * Originally Released Under LGPL - original licence link has changed is not relivant.
7232  *
7233  * Fork - LGPL
7234  * <script type="text/javascript">
7235  */
7236  
7237
7238 /**
7239  * @class Roo.ComponentMgr
7240  * Provides a common registry of all components on a page so that they can be easily accessed by component id (see {@link Roo.getCmp}).
7241  * @singleton
7242  */
7243 Roo.ComponentMgr = function(){
7244     var all = new Roo.util.MixedCollection();
7245
7246     return {
7247         /**
7248          * Registers a component.
7249          * @param {Roo.Component} c The component
7250          */
7251         register : function(c){
7252             all.add(c);
7253         },
7254
7255         /**
7256          * Unregisters a component.
7257          * @param {Roo.Component} c The component
7258          */
7259         unregister : function(c){
7260             all.remove(c);
7261         },
7262
7263         /**
7264          * Returns a component by id
7265          * @param {String} id The component id
7266          */
7267         get : function(id){
7268             return all.get(id);
7269         },
7270
7271         /**
7272          * Registers a function that will be called when a specified component is added to ComponentMgr
7273          * @param {String} id The component id
7274          * @param {Funtction} fn The callback function
7275          * @param {Object} scope The scope of the callback
7276          */
7277         onAvailable : function(id, fn, scope){
7278             all.on("add", function(index, o){
7279                 if(o.id == id){
7280                     fn.call(scope || o, o);
7281                     all.un("add", fn, scope);
7282                 }
7283             });
7284         }
7285     };
7286 }();/*
7287  * Based on:
7288  * Ext JS Library 1.1.1
7289  * Copyright(c) 2006-2007, Ext JS, LLC.
7290  *
7291  * Originally Released Under LGPL - original licence link has changed is not relivant.
7292  *
7293  * Fork - LGPL
7294  * <script type="text/javascript">
7295  */
7296  
7297 /**
7298  * @class Roo.Component
7299  * @extends Roo.util.Observable
7300  * Base class for all major Roo components.  All subclasses of Component can automatically participate in the standard
7301  * Roo component lifecycle of creation, rendering and destruction.  They also have automatic support for basic hide/show
7302  * and enable/disable behavior.  Component allows any subclass to be lazy-rendered into any {@link Roo.Container} and
7303  * to be automatically registered with the {@link Roo.ComponentMgr} so that it can be referenced at any time via {@link Roo.getCmp}.
7304  * All visual components (widgets) that require rendering into a layout should subclass Component.
7305  * @constructor
7306  * @param {Roo.Element/String/Object} config The configuration options.  If an element is passed, it is set as the internal
7307  * element and its id used as the component id.  If a string is passed, it is assumed to be the id of an existing element
7308  * and is used as the component id.  Otherwise, it is assumed to be a standard config object and is applied to the component.
7309  */
7310 Roo.Component = function(config){
7311     config = config || {};
7312     if(config.tagName || config.dom || typeof config == "string"){ // element object
7313         config = {el: config, id: config.id || config};
7314     }
7315     this.initialConfig = config;
7316
7317     Roo.apply(this, config);
7318     this.addEvents({
7319         /**
7320          * @event disable
7321          * Fires after the component is disabled.
7322              * @param {Roo.Component} this
7323              */
7324         disable : true,
7325         /**
7326          * @event enable
7327          * Fires after the component is enabled.
7328              * @param {Roo.Component} this
7329              */
7330         enable : true,
7331         /**
7332          * @event beforeshow
7333          * Fires before the component is shown.  Return false to stop the show.
7334              * @param {Roo.Component} this
7335              */
7336         beforeshow : true,
7337         /**
7338          * @event show
7339          * Fires after the component is shown.
7340              * @param {Roo.Component} this
7341              */
7342         show : true,
7343         /**
7344          * @event beforehide
7345          * Fires before the component is hidden. Return false to stop the hide.
7346              * @param {Roo.Component} this
7347              */
7348         beforehide : true,
7349         /**
7350          * @event hide
7351          * Fires after the component is hidden.
7352              * @param {Roo.Component} this
7353              */
7354         hide : true,
7355         /**
7356          * @event beforerender
7357          * Fires before the component is rendered. Return false to stop the render.
7358              * @param {Roo.Component} this
7359              */
7360         beforerender : true,
7361         /**
7362          * @event render
7363          * Fires after the component is rendered.
7364              * @param {Roo.Component} this
7365              */
7366         render : true,
7367         /**
7368          * @event beforedestroy
7369          * Fires before the component is destroyed. Return false to stop the destroy.
7370              * @param {Roo.Component} this
7371              */
7372         beforedestroy : true,
7373         /**
7374          * @event destroy
7375          * Fires after the component is destroyed.
7376              * @param {Roo.Component} this
7377              */
7378         destroy : true
7379     });
7380     if(!this.id){
7381         this.id = "ext-comp-" + (++Roo.Component.AUTO_ID);
7382     }
7383     Roo.ComponentMgr.register(this);
7384     Roo.Component.superclass.constructor.call(this);
7385     this.initComponent();
7386     if(this.renderTo){ // not supported by all components yet. use at your own risk!
7387         this.render(this.renderTo);
7388         delete this.renderTo;
7389     }
7390 };
7391
7392 // private
7393 Roo.Component.AUTO_ID = 1000;
7394
7395 Roo.extend(Roo.Component, Roo.util.Observable, {
7396     /**
7397      * @property {Boolean} hidden
7398      * true if this component is hidden. Read-only.
7399      */
7400     hidden : false,
7401     /**
7402      * true if this component is disabled. Read-only.
7403      */
7404     disabled : false,
7405     /**
7406      * true if this component has been rendered. Read-only.
7407      */
7408     rendered : false,
7409     
7410     /** @cfg {String} disableClass
7411      * CSS class added to the component when it is disabled (defaults to "x-item-disabled").
7412      */
7413     disabledClass : "x-item-disabled",
7414         /** @cfg {Boolean} allowDomMove
7415          * Whether the component can move the Dom node when rendering (defaults to true).
7416          */
7417     allowDomMove : true,
7418     /** @cfg {String} hideMode
7419      * How this component should hidden. Supported values are
7420      * "visibility" (css visibility), "offsets" (negative offset position) and
7421      * "display" (css display) - defaults to "display".
7422      */
7423     hideMode: 'display',
7424
7425     // private
7426     ctype : "Roo.Component",
7427
7428     /** @cfg {String} actionMode 
7429      * which property holds the element that used for  hide() / show() / disable() / enable()
7430      * default is 'el' 
7431      */
7432     actionMode : "el",
7433
7434     // private
7435     getActionEl : function(){
7436         return this[this.actionMode];
7437     },
7438
7439     initComponent : Roo.emptyFn,
7440     /**
7441      * If this is a lazy rendering component, render it to its container element.
7442      * @param {String/HTMLElement/Element} container (optional) The element this component should be rendered into. If it is being applied to existing markup, this should be left off.
7443      */
7444     render : function(container, position){
7445         if(!this.rendered && this.fireEvent("beforerender", this) !== false){
7446             if(!container && this.el){
7447                 this.el = Roo.get(this.el);
7448                 container = this.el.dom.parentNode;
7449                 this.allowDomMove = false;
7450             }
7451             this.container = Roo.get(container);
7452             this.rendered = true;
7453             if(position !== undefined){
7454                 if(typeof position == 'number'){
7455                     position = this.container.dom.childNodes[position];
7456                 }else{
7457                     position = Roo.getDom(position);
7458                 }
7459             }
7460             this.onRender(this.container, position || null);
7461             if(this.cls){
7462                 this.el.addClass(this.cls);
7463                 delete this.cls;
7464             }
7465             if(this.style){
7466                 this.el.applyStyles(this.style);
7467                 delete this.style;
7468             }
7469             this.fireEvent("render", this);
7470             this.afterRender(this.container);
7471             if(this.hidden){
7472                 this.hide();
7473             }
7474             if(this.disabled){
7475                 this.disable();
7476             }
7477         }
7478         return this;
7479     },
7480
7481     // private
7482     // default function is not really useful
7483     onRender : function(ct, position){
7484         if(this.el){
7485             this.el = Roo.get(this.el);
7486             if(this.allowDomMove !== false){
7487                 ct.dom.insertBefore(this.el.dom, position);
7488             }
7489         }
7490     },
7491
7492     // private
7493     getAutoCreate : function(){
7494         var cfg = typeof this.autoCreate == "object" ?
7495                       this.autoCreate : Roo.apply({}, this.defaultAutoCreate);
7496         if(this.id && !cfg.id){
7497             cfg.id = this.id;
7498         }
7499         return cfg;
7500     },
7501
7502     // private
7503     afterRender : Roo.emptyFn,
7504
7505     /**
7506      * Destroys this component by purging any event listeners, removing the component's element from the DOM,
7507      * removing the component from its {@link Roo.Container} (if applicable) and unregistering it from {@link Roo.ComponentMgr}.
7508      */
7509     destroy : function(){
7510         if(this.fireEvent("beforedestroy", this) !== false){
7511             this.purgeListeners();
7512             this.beforeDestroy();
7513             if(this.rendered){
7514                 this.el.removeAllListeners();
7515                 this.el.remove();
7516                 if(this.actionMode == "container"){
7517                     this.container.remove();
7518                 }
7519             }
7520             this.onDestroy();
7521             Roo.ComponentMgr.unregister(this);
7522             this.fireEvent("destroy", this);
7523         }
7524     },
7525
7526         // private
7527     beforeDestroy : function(){
7528
7529     },
7530
7531         // private
7532         onDestroy : function(){
7533
7534     },
7535
7536     /**
7537      * Returns the underlying {@link Roo.Element}.
7538      * @return {Roo.Element} The element
7539      */
7540     getEl : function(){
7541         return this.el;
7542     },
7543
7544     /**
7545      * Returns the id of this component.
7546      * @return {String}
7547      */
7548     getId : function(){
7549         return this.id;
7550     },
7551
7552     /**
7553      * Try to focus this component.
7554      * @param {Boolean} selectText True to also select the text in this component (if applicable)
7555      * @return {Roo.Component} this
7556      */
7557     focus : function(selectText){
7558         if(this.rendered){
7559             this.el.focus();
7560             if(selectText === true){
7561                 this.el.dom.select();
7562             }
7563         }
7564         return this;
7565     },
7566
7567     // private
7568     blur : function(){
7569         if(this.rendered){
7570             this.el.blur();
7571         }
7572         return this;
7573     },
7574
7575     /**
7576      * Disable this component.
7577      * @return {Roo.Component} this
7578      */
7579     disable : function(){
7580         if(this.rendered){
7581             this.onDisable();
7582         }
7583         this.disabled = true;
7584         this.fireEvent("disable", this);
7585         return this;
7586     },
7587
7588         // private
7589     onDisable : function(){
7590         this.getActionEl().addClass(this.disabledClass);
7591         this.el.dom.disabled = true;
7592     },
7593
7594     /**
7595      * Enable this component.
7596      * @return {Roo.Component} this
7597      */
7598     enable : function(){
7599         if(this.rendered){
7600             this.onEnable();
7601         }
7602         this.disabled = false;
7603         this.fireEvent("enable", this);
7604         return this;
7605     },
7606
7607         // private
7608     onEnable : function(){
7609         this.getActionEl().removeClass(this.disabledClass);
7610         this.el.dom.disabled = false;
7611     },
7612
7613     /**
7614      * Convenience function for setting disabled/enabled by boolean.
7615      * @param {Boolean} disabled
7616      */
7617     setDisabled : function(disabled){
7618         this[disabled ? "disable" : "enable"]();
7619     },
7620
7621     /**
7622      * Show this component.
7623      * @return {Roo.Component} this
7624      */
7625     show: function(){
7626         if(this.fireEvent("beforeshow", this) !== false){
7627             this.hidden = false;
7628             if(this.rendered){
7629                 this.onShow();
7630             }
7631             this.fireEvent("show", this);
7632         }
7633         return this;
7634     },
7635
7636     // private
7637     onShow : function(){
7638         var ae = this.getActionEl();
7639         if(this.hideMode == 'visibility'){
7640             ae.dom.style.visibility = "visible";
7641         }else if(this.hideMode == 'offsets'){
7642             ae.removeClass('x-hidden');
7643         }else{
7644             ae.dom.style.display = "";
7645         }
7646     },
7647
7648     /**
7649      * Hide this component.
7650      * @return {Roo.Component} this
7651      */
7652     hide: function(){
7653         if(this.fireEvent("beforehide", this) !== false){
7654             this.hidden = true;
7655             if(this.rendered){
7656                 this.onHide();
7657             }
7658             this.fireEvent("hide", this);
7659         }
7660         return this;
7661     },
7662
7663     // private
7664     onHide : function(){
7665         var ae = this.getActionEl();
7666         if(this.hideMode == 'visibility'){
7667             ae.dom.style.visibility = "hidden";
7668         }else if(this.hideMode == 'offsets'){
7669             ae.addClass('x-hidden');
7670         }else{
7671             ae.dom.style.display = "none";
7672         }
7673     },
7674
7675     /**
7676      * Convenience function to hide or show this component by boolean.
7677      * @param {Boolean} visible True to show, false to hide
7678      * @return {Roo.Component} this
7679      */
7680     setVisible: function(visible){
7681         if(visible) {
7682             this.show();
7683         }else{
7684             this.hide();
7685         }
7686         return this;
7687     },
7688
7689     /**
7690      * Returns true if this component is visible.
7691      */
7692     isVisible : function(){
7693         return this.getActionEl().isVisible();
7694     },
7695
7696     cloneConfig : function(overrides){
7697         overrides = overrides || {};
7698         var id = overrides.id || Roo.id();
7699         var cfg = Roo.applyIf(overrides, this.initialConfig);
7700         cfg.id = id; // prevent dup id
7701         return new this.constructor(cfg);
7702     }
7703 });/*
7704  * Based on:
7705  * Ext JS Library 1.1.1
7706  * Copyright(c) 2006-2007, Ext JS, LLC.
7707  *
7708  * Originally Released Under LGPL - original licence link has changed is not relivant.
7709  *
7710  * Fork - LGPL
7711  * <script type="text/javascript">
7712  */
7713  (function(){ 
7714 /**
7715  * @class Roo.Layer
7716  * @extends Roo.Element
7717  * An extended {@link Roo.Element} object that supports a shadow and shim, constrain to viewport and
7718  * automatic maintaining of shadow/shim positions.
7719  * @cfg {Boolean} shim False to disable the iframe shim in browsers which need one (defaults to true)
7720  * @cfg {String/Boolean} shadow True to create a shadow element with default class "x-layer-shadow", or
7721  * you can pass a string with a CSS class name. False turns off the shadow.
7722  * @cfg {Object} dh DomHelper object config to create element with (defaults to {tag: "div", cls: "x-layer"}).
7723  * @cfg {Boolean} constrain False to disable constrain to viewport (defaults to true)
7724  * @cfg {String} cls CSS class to add to the element
7725  * @cfg {Number} zindex Starting z-index (defaults to 11000)
7726  * @cfg {Number} shadowOffset Number of pixels to offset the shadow (defaults to 3)
7727  * @constructor
7728  * @param {Object} config An object with config options.
7729  * @param {String/HTMLElement} existingEl (optional) Uses an existing DOM element. If the element is not found it creates it.
7730  */
7731
7732 Roo.Layer = function(config, existingEl){
7733     config = config || {};
7734     var dh = Roo.DomHelper;
7735     var cp = config.parentEl, pel = cp ? Roo.getDom(cp) : document.body;
7736     if(existingEl){
7737         this.dom = Roo.getDom(existingEl);
7738     }
7739     if(!this.dom){
7740         var o = config.dh || {tag: "div", cls: "x-layer"};
7741         this.dom = dh.append(pel, o);
7742     }
7743     if(config.cls){
7744         this.addClass(config.cls);
7745     }
7746     this.constrain = config.constrain !== false;
7747     this.visibilityMode = Roo.Element.VISIBILITY;
7748     if(config.id){
7749         this.id = this.dom.id = config.id;
7750     }else{
7751         this.id = Roo.id(this.dom);
7752     }
7753     this.zindex = config.zindex || this.getZIndex();
7754     this.position("absolute", this.zindex);
7755     if(config.shadow){
7756         this.shadowOffset = config.shadowOffset || 4;
7757         this.shadow = new Roo.Shadow({
7758             offset : this.shadowOffset,
7759             mode : config.shadow
7760         });
7761     }else{
7762         this.shadowOffset = 0;
7763     }
7764     this.useShim = config.shim !== false && Roo.useShims;
7765     this.useDisplay = config.useDisplay;
7766     this.hide();
7767 };
7768
7769 var supr = Roo.Element.prototype;
7770
7771 // shims are shared among layer to keep from having 100 iframes
7772 var shims = [];
7773
7774 Roo.extend(Roo.Layer, Roo.Element, {
7775
7776     getZIndex : function(){
7777         return this.zindex || parseInt(this.getStyle("z-index"), 10) || 11000;
7778     },
7779
7780     getShim : function(){
7781         if(!this.useShim){
7782             return null;
7783         }
7784         if(this.shim){
7785             return this.shim;
7786         }
7787         var shim = shims.shift();
7788         if(!shim){
7789             shim = this.createShim();
7790             shim.enableDisplayMode('block');
7791             shim.dom.style.display = 'none';
7792             shim.dom.style.visibility = 'visible';
7793         }
7794         var pn = this.dom.parentNode;
7795         if(shim.dom.parentNode != pn){
7796             pn.insertBefore(shim.dom, this.dom);
7797         }
7798         shim.setStyle('z-index', this.getZIndex()-2);
7799         this.shim = shim;
7800         return shim;
7801     },
7802
7803     hideShim : function(){
7804         if(this.shim){
7805             this.shim.setDisplayed(false);
7806             shims.push(this.shim);
7807             delete this.shim;
7808         }
7809     },
7810
7811     disableShadow : function(){
7812         if(this.shadow){
7813             this.shadowDisabled = true;
7814             this.shadow.hide();
7815             this.lastShadowOffset = this.shadowOffset;
7816             this.shadowOffset = 0;
7817         }
7818     },
7819
7820     enableShadow : function(show){
7821         if(this.shadow){
7822             this.shadowDisabled = false;
7823             this.shadowOffset = this.lastShadowOffset;
7824             delete this.lastShadowOffset;
7825             if(show){
7826                 this.sync(true);
7827             }
7828         }
7829     },
7830
7831     // private
7832     // this code can execute repeatedly in milliseconds (i.e. during a drag) so
7833     // code size was sacrificed for effeciency (e.g. no getBox/setBox, no XY calls)
7834     sync : function(doShow){
7835         var sw = this.shadow;
7836         if(!this.updating && this.isVisible() && (sw || this.useShim)){
7837             var sh = this.getShim();
7838
7839             var w = this.getWidth(),
7840                 h = this.getHeight();
7841
7842             var l = this.getLeft(true),
7843                 t = this.getTop(true);
7844
7845             if(sw && !this.shadowDisabled){
7846                 if(doShow && !sw.isVisible()){
7847                     sw.show(this);
7848                 }else{
7849                     sw.realign(l, t, w, h);
7850                 }
7851                 if(sh){
7852                     if(doShow){
7853                        sh.show();
7854                     }
7855                     // fit the shim behind the shadow, so it is shimmed too
7856                     var a = sw.adjusts, s = sh.dom.style;
7857                     s.left = (Math.min(l, l+a.l))+"px";
7858                     s.top = (Math.min(t, t+a.t))+"px";
7859                     s.width = (w+a.w)+"px";
7860                     s.height = (h+a.h)+"px";
7861                 }
7862             }else if(sh){
7863                 if(doShow){
7864                    sh.show();
7865                 }
7866                 sh.setSize(w, h);
7867                 sh.setLeftTop(l, t);
7868             }
7869             
7870         }
7871     },
7872
7873     // private
7874     destroy : function(){
7875         this.hideShim();
7876         if(this.shadow){
7877             this.shadow.hide();
7878         }
7879         this.removeAllListeners();
7880         var pn = this.dom.parentNode;
7881         if(pn){
7882             pn.removeChild(this.dom);
7883         }
7884         Roo.Element.uncache(this.id);
7885     },
7886
7887     remove : function(){
7888         this.destroy();
7889     },
7890
7891     // private
7892     beginUpdate : function(){
7893         this.updating = true;
7894     },
7895
7896     // private
7897     endUpdate : function(){
7898         this.updating = false;
7899         this.sync(true);
7900     },
7901
7902     // private
7903     hideUnders : function(negOffset){
7904         if(this.shadow){
7905             this.shadow.hide();
7906         }
7907         this.hideShim();
7908     },
7909
7910     // private
7911     constrainXY : function(){
7912         if(this.constrain){
7913             var vw = Roo.lib.Dom.getViewWidth(),
7914                 vh = Roo.lib.Dom.getViewHeight();
7915             var s = Roo.get(document).getScroll();
7916
7917             var xy = this.getXY();
7918             var x = xy[0], y = xy[1];   
7919             var w = this.dom.offsetWidth+this.shadowOffset, h = this.dom.offsetHeight+this.shadowOffset;
7920             // only move it if it needs it
7921             var moved = false;
7922             // first validate right/bottom
7923             if((x + w) > vw+s.left){
7924                 x = vw - w - this.shadowOffset;
7925                 moved = true;
7926             }
7927             if((y + h) > vh+s.top){
7928                 y = vh - h - this.shadowOffset;
7929                 moved = true;
7930             }
7931             // then make sure top/left isn't negative
7932             if(x < s.left){
7933                 x = s.left;
7934                 moved = true;
7935             }
7936             if(y < s.top){
7937                 y = s.top;
7938                 moved = true;
7939             }
7940             if(moved){
7941                 if(this.avoidY){
7942                     var ay = this.avoidY;
7943                     if(y <= ay && (y+h) >= ay){
7944                         y = ay-h-5;   
7945                     }
7946                 }
7947                 xy = [x, y];
7948                 this.storeXY(xy);
7949                 supr.setXY.call(this, xy);
7950                 this.sync();
7951             }
7952         }
7953     },
7954
7955     isVisible : function(){
7956         return this.visible;    
7957     },
7958
7959     // private
7960     showAction : function(){
7961         this.visible = true; // track visibility to prevent getStyle calls
7962         if(this.useDisplay === true){
7963             this.setDisplayed("");
7964         }else if(this.lastXY){
7965             supr.setXY.call(this, this.lastXY);
7966         }else if(this.lastLT){
7967             supr.setLeftTop.call(this, this.lastLT[0], this.lastLT[1]);
7968         }
7969     },
7970
7971     // private
7972     hideAction : function(){
7973         this.visible = false;
7974         if(this.useDisplay === true){
7975             this.setDisplayed(false);
7976         }else{
7977             this.setLeftTop(-10000,-10000);
7978         }
7979     },
7980
7981     // overridden Element method
7982     setVisible : function(v, a, d, c, e){
7983         if(v){
7984             this.showAction();
7985         }
7986         if(a && v){
7987             var cb = function(){
7988                 this.sync(true);
7989                 if(c){
7990                     c();
7991                 }
7992             }.createDelegate(this);
7993             supr.setVisible.call(this, true, true, d, cb, e);
7994         }else{
7995             if(!v){
7996                 this.hideUnders(true);
7997             }
7998             var cb = c;
7999             if(a){
8000                 cb = function(){
8001                     this.hideAction();
8002                     if(c){
8003                         c();
8004                     }
8005                 }.createDelegate(this);
8006             }
8007             supr.setVisible.call(this, v, a, d, cb, e);
8008             if(v){
8009                 this.sync(true);
8010             }else if(!a){
8011                 this.hideAction();
8012             }
8013         }
8014     },
8015
8016     storeXY : function(xy){
8017         delete this.lastLT;
8018         this.lastXY = xy;
8019     },
8020
8021     storeLeftTop : function(left, top){
8022         delete this.lastXY;
8023         this.lastLT = [left, top];
8024     },
8025
8026     // private
8027     beforeFx : function(){
8028         this.beforeAction();
8029         return Roo.Layer.superclass.beforeFx.apply(this, arguments);
8030     },
8031
8032     // private
8033     afterFx : function(){
8034         Roo.Layer.superclass.afterFx.apply(this, arguments);
8035         this.sync(this.isVisible());
8036     },
8037
8038     // private
8039     beforeAction : function(){
8040         if(!this.updating && this.shadow){
8041             this.shadow.hide();
8042         }
8043     },
8044
8045     // overridden Element method
8046     setLeft : function(left){
8047         this.storeLeftTop(left, this.getTop(true));
8048         supr.setLeft.apply(this, arguments);
8049         this.sync();
8050     },
8051
8052     setTop : function(top){
8053         this.storeLeftTop(this.getLeft(true), top);
8054         supr.setTop.apply(this, arguments);
8055         this.sync();
8056     },
8057
8058     setLeftTop : function(left, top){
8059         this.storeLeftTop(left, top);
8060         supr.setLeftTop.apply(this, arguments);
8061         this.sync();
8062     },
8063
8064     setXY : function(xy, a, d, c, e){
8065         this.fixDisplay();
8066         this.beforeAction();
8067         this.storeXY(xy);
8068         var cb = this.createCB(c);
8069         supr.setXY.call(this, xy, a, d, cb, e);
8070         if(!a){
8071             cb();
8072         }
8073     },
8074
8075     // private
8076     createCB : function(c){
8077         var el = this;
8078         return function(){
8079             el.constrainXY();
8080             el.sync(true);
8081             if(c){
8082                 c();
8083             }
8084         };
8085     },
8086
8087     // overridden Element method
8088     setX : function(x, a, d, c, e){
8089         this.setXY([x, this.getY()], a, d, c, e);
8090     },
8091
8092     // overridden Element method
8093     setY : function(y, a, d, c, e){
8094         this.setXY([this.getX(), y], a, d, c, e);
8095     },
8096
8097     // overridden Element method
8098     setSize : function(w, h, a, d, c, e){
8099         this.beforeAction();
8100         var cb = this.createCB(c);
8101         supr.setSize.call(this, w, h, a, d, cb, e);
8102         if(!a){
8103             cb();
8104         }
8105     },
8106
8107     // overridden Element method
8108     setWidth : function(w, a, d, c, e){
8109         this.beforeAction();
8110         var cb = this.createCB(c);
8111         supr.setWidth.call(this, w, a, d, cb, e);
8112         if(!a){
8113             cb();
8114         }
8115     },
8116
8117     // overridden Element method
8118     setHeight : function(h, a, d, c, e){
8119         this.beforeAction();
8120         var cb = this.createCB(c);
8121         supr.setHeight.call(this, h, a, d, cb, e);
8122         if(!a){
8123             cb();
8124         }
8125     },
8126
8127     // overridden Element method
8128     setBounds : function(x, y, w, h, a, d, c, e){
8129         this.beforeAction();
8130         var cb = this.createCB(c);
8131         if(!a){
8132             this.storeXY([x, y]);
8133             supr.setXY.call(this, [x, y]);
8134             supr.setSize.call(this, w, h, a, d, cb, e);
8135             cb();
8136         }else{
8137             supr.setBounds.call(this, x, y, w, h, a, d, cb, e);
8138         }
8139         return this;
8140     },
8141     
8142     /**
8143      * Sets the z-index of this layer and adjusts any shadow and shim z-indexes. The layer z-index is automatically
8144      * incremented by two more than the value passed in so that it always shows above any shadow or shim (the shadow
8145      * element, if any, will be assigned z-index + 1, and the shim element, if any, will be assigned the unmodified z-index).
8146      * @param {Number} zindex The new z-index to set
8147      * @return {this} The Layer
8148      */
8149     setZIndex : function(zindex){
8150         this.zindex = zindex;
8151         this.setStyle("z-index", zindex + 2);
8152         if(this.shadow){
8153             this.shadow.setZIndex(zindex + 1);
8154         }
8155         if(this.shim){
8156             this.shim.setStyle("z-index", zindex);
8157         }
8158     }
8159 });
8160 })();/*
8161  * Based on:
8162  * Ext JS Library 1.1.1
8163  * Copyright(c) 2006-2007, Ext JS, LLC.
8164  *
8165  * Originally Released Under LGPL - original licence link has changed is not relivant.
8166  *
8167  * Fork - LGPL
8168  * <script type="text/javascript">
8169  */
8170
8171
8172 /**
8173  * @class Roo.Shadow
8174  * Simple class that can provide a shadow effect for any element.  Note that the element MUST be absolutely positioned,
8175  * and the shadow does not provide any shimming.  This should be used only in simple cases -- for more advanced
8176  * functionality that can also provide the same shadow effect, see the {@link Roo.Layer} class.
8177  * @constructor
8178  * Create a new Shadow
8179  * @param {Object} config The config object
8180  */
8181 Roo.Shadow = function(config){
8182     Roo.apply(this, config);
8183     if(typeof this.mode != "string"){
8184         this.mode = this.defaultMode;
8185     }
8186     var o = this.offset, a = {h: 0};
8187     var rad = Math.floor(this.offset/2);
8188     switch(this.mode.toLowerCase()){ // all this hideous nonsense calculates the various offsets for shadows
8189         case "drop":
8190             a.w = 0;
8191             a.l = a.t = o;
8192             a.t -= 1;
8193             if(Roo.isIE){
8194                 a.l -= this.offset + rad;
8195                 a.t -= this.offset + rad;
8196                 a.w -= rad;
8197                 a.h -= rad;
8198                 a.t += 1;
8199             }
8200         break;
8201         case "sides":
8202             a.w = (o*2);
8203             a.l = -o;
8204             a.t = o-1;
8205             if(Roo.isIE){
8206                 a.l -= (this.offset - rad);
8207                 a.t -= this.offset + rad;
8208                 a.l += 1;
8209                 a.w -= (this.offset - rad)*2;
8210                 a.w -= rad + 1;
8211                 a.h -= 1;
8212             }
8213         break;
8214         case "frame":
8215             a.w = a.h = (o*2);
8216             a.l = a.t = -o;
8217             a.t += 1;
8218             a.h -= 2;
8219             if(Roo.isIE){
8220                 a.l -= (this.offset - rad);
8221                 a.t -= (this.offset - rad);
8222                 a.l += 1;
8223                 a.w -= (this.offset + rad + 1);
8224                 a.h -= (this.offset + rad);
8225                 a.h += 1;
8226             }
8227         break;
8228     };
8229
8230     this.adjusts = a;
8231 };
8232
8233 Roo.Shadow.prototype = {
8234     /**
8235      * @cfg {String} mode
8236      * The shadow display mode.  Supports the following options:<br />
8237      * sides: Shadow displays on both sides and bottom only<br />
8238      * frame: Shadow displays equally on all four sides<br />
8239      * drop: Traditional bottom-right drop shadow (default)
8240      */
8241     /**
8242      * @cfg {String} offset
8243      * The number of pixels to offset the shadow from the element (defaults to 4)
8244      */
8245     offset: 4,
8246
8247     // private
8248     defaultMode: "drop",
8249
8250     /**
8251      * Displays the shadow under the target element
8252      * @param {String/HTMLElement/Element} targetEl The id or element under which the shadow should display
8253      */
8254     show : function(target){
8255         target = Roo.get(target);
8256         if(!this.el){
8257             this.el = Roo.Shadow.Pool.pull();
8258             if(this.el.dom.nextSibling != target.dom){
8259                 this.el.insertBefore(target);
8260             }
8261         }
8262         this.el.setStyle("z-index", this.zIndex || parseInt(target.getStyle("z-index"), 10)-1);
8263         if(Roo.isIE){
8264             this.el.dom.style.filter="progid:DXImageTransform.Microsoft.alpha(opacity=50) progid:DXImageTransform.Microsoft.Blur(pixelradius="+(this.offset)+")";
8265         }
8266         this.realign(
8267             target.getLeft(true),
8268             target.getTop(true),
8269             target.getWidth(),
8270             target.getHeight()
8271         );
8272         this.el.dom.style.display = "block";
8273     },
8274
8275     /**
8276      * Returns true if the shadow is visible, else false
8277      */
8278     isVisible : function(){
8279         return this.el ? true : false;  
8280     },
8281
8282     /**
8283      * Direct alignment when values are already available. Show must be called at least once before
8284      * calling this method to ensure it is initialized.
8285      * @param {Number} left The target element left position
8286      * @param {Number} top The target element top position
8287      * @param {Number} width The target element width
8288      * @param {Number} height The target element height
8289      */
8290     realign : function(l, t, w, h){
8291         if(!this.el){
8292             return;
8293         }
8294         var a = this.adjusts, d = this.el.dom, s = d.style;
8295         var iea = 0;
8296         s.left = (l+a.l)+"px";
8297         s.top = (t+a.t)+"px";
8298         var sw = (w+a.w), sh = (h+a.h), sws = sw +"px", shs = sh + "px";
8299  
8300         if(s.width != sws || s.height != shs){
8301             s.width = sws;
8302             s.height = shs;
8303             if(!Roo.isIE){
8304                 var cn = d.childNodes;
8305                 var sww = Math.max(0, (sw-12))+"px";
8306                 cn[0].childNodes[1].style.width = sww;
8307                 cn[1].childNodes[1].style.width = sww;
8308                 cn[2].childNodes[1].style.width = sww;
8309                 cn[1].style.height = Math.max(0, (sh-12))+"px";
8310             }
8311         }
8312     },
8313
8314     /**
8315      * Hides this shadow
8316      */
8317     hide : function(){
8318         if(this.el){
8319             this.el.dom.style.display = "none";
8320             Roo.Shadow.Pool.push(this.el);
8321             delete this.el;
8322         }
8323     },
8324
8325     /**
8326      * Adjust the z-index of this shadow
8327      * @param {Number} zindex The new z-index
8328      */
8329     setZIndex : function(z){
8330         this.zIndex = z;
8331         if(this.el){
8332             this.el.setStyle("z-index", z);
8333         }
8334     }
8335 };
8336
8337 // Private utility class that manages the internal Shadow cache
8338 Roo.Shadow.Pool = function(){
8339     var p = [];
8340     var markup = Roo.isIE ?
8341                  '<div class="x-ie-shadow"></div>' :
8342                  '<div class="x-shadow"><div class="xst"><div class="xstl"></div><div class="xstc"></div><div class="xstr"></div></div><div class="xsc"><div class="xsml"></div><div class="xsmc"></div><div class="xsmr"></div></div><div class="xsb"><div class="xsbl"></div><div class="xsbc"></div><div class="xsbr"></div></div></div>';
8343     return {
8344         pull : function(){
8345             var sh = p.shift();
8346             if(!sh){
8347                 sh = Roo.get(Roo.DomHelper.insertHtml("beforeBegin", document.body.firstChild, markup));
8348                 sh.autoBoxAdjust = false;
8349             }
8350             return sh;
8351         },
8352
8353         push : function(sh){
8354             p.push(sh);
8355         }
8356     };
8357 }();/*
8358  * Based on:
8359  * Ext JS Library 1.1.1
8360  * Copyright(c) 2006-2007, Ext JS, LLC.
8361  *
8362  * Originally Released Under LGPL - original licence link has changed is not relivant.
8363  *
8364  * Fork - LGPL
8365  * <script type="text/javascript">
8366  */
8367
8368 /**
8369  * @class Roo.BoxComponent
8370  * @extends Roo.Component
8371  * Base class for any visual {@link Roo.Component} that uses a box container.  BoxComponent provides automatic box
8372  * model adjustments for sizing and positioning and will work correctly withnin the Component rendering model.  All
8373  * container classes should subclass BoxComponent so that they will work consistently when nested within other Ext
8374  * layout containers.
8375  * @constructor
8376  * @param {Roo.Element/String/Object} config The configuration options.
8377  */
8378 Roo.BoxComponent = function(config){
8379     Roo.Component.call(this, config);
8380     this.addEvents({
8381         /**
8382          * @event resize
8383          * Fires after the component is resized.
8384              * @param {Roo.Component} this
8385              * @param {Number} adjWidth The box-adjusted width that was set
8386              * @param {Number} adjHeight The box-adjusted height that was set
8387              * @param {Number} rawWidth The width that was originally specified
8388              * @param {Number} rawHeight The height that was originally specified
8389              */
8390         resize : true,
8391         /**
8392          * @event move
8393          * Fires after the component is moved.
8394              * @param {Roo.Component} this
8395              * @param {Number} x The new x position
8396              * @param {Number} y The new y position
8397              */
8398         move : true
8399     });
8400 };
8401
8402 Roo.extend(Roo.BoxComponent, Roo.Component, {
8403     // private, set in afterRender to signify that the component has been rendered
8404     boxReady : false,
8405     // private, used to defer height settings to subclasses
8406     deferHeight: false,
8407     /** @cfg {Number} width
8408      * width (optional) size of component
8409      */
8410      /** @cfg {Number} height
8411      * height (optional) size of component
8412      */
8413      
8414     /**
8415      * Sets the width and height of the component.  This method fires the resize event.  This method can accept
8416      * either width and height as separate numeric arguments, or you can pass a size object like {width:10, height:20}.
8417      * @param {Number/Object} width The new width to set, or a size object in the format {width, height}
8418      * @param {Number} height The new height to set (not required if a size object is passed as the first arg)
8419      * @return {Roo.BoxComponent} this
8420      */
8421     setSize : function(w, h){
8422         // support for standard size objects
8423         if(typeof w == 'object'){
8424             h = w.height;
8425             w = w.width;
8426         }
8427         // not rendered
8428         if(!this.boxReady){
8429             this.width = w;
8430             this.height = h;
8431             return this;
8432         }
8433
8434         // prevent recalcs when not needed
8435         if(this.lastSize && this.lastSize.width == w && this.lastSize.height == h){
8436             return this;
8437         }
8438         this.lastSize = {width: w, height: h};
8439
8440         var adj = this.adjustSize(w, h);
8441         var aw = adj.width, ah = adj.height;
8442         if(aw !== undefined || ah !== undefined){ // this code is nasty but performs better with floaters
8443             var rz = this.getResizeEl();
8444             if(!this.deferHeight && aw !== undefined && ah !== undefined){
8445                 rz.setSize(aw, ah);
8446             }else if(!this.deferHeight && ah !== undefined){
8447                 rz.setHeight(ah);
8448             }else if(aw !== undefined){
8449                 rz.setWidth(aw);
8450             }
8451             this.onResize(aw, ah, w, h);
8452             this.fireEvent('resize', this, aw, ah, w, h);
8453         }
8454         return this;
8455     },
8456
8457     /**
8458      * Gets the current size of the component's underlying element.
8459      * @return {Object} An object containing the element's size {width: (element width), height: (element height)}
8460      */
8461     getSize : function(){
8462         return this.el.getSize();
8463     },
8464
8465     /**
8466      * Gets the current XY position of the component's underlying element.
8467      * @param {Boolean} local (optional) If true the element's left and top are returned instead of page XY (defaults to false)
8468      * @return {Array} The XY position of the element (e.g., [100, 200])
8469      */
8470     getPosition : function(local){
8471         if(local === true){
8472             return [this.el.getLeft(true), this.el.getTop(true)];
8473         }
8474         return this.xy || this.el.getXY();
8475     },
8476
8477     /**
8478      * Gets the current box measurements of the component's underlying element.
8479      * @param {Boolean} local (optional) If true the element's left and top are returned instead of page XY (defaults to false)
8480      * @returns {Object} box An object in the format {x, y, width, height}
8481      */
8482     getBox : function(local){
8483         var s = this.el.getSize();
8484         if(local){
8485             s.x = this.el.getLeft(true);
8486             s.y = this.el.getTop(true);
8487         }else{
8488             var xy = this.xy || this.el.getXY();
8489             s.x = xy[0];
8490             s.y = xy[1];
8491         }
8492         return s;
8493     },
8494
8495     /**
8496      * Sets the current box measurements of the component's underlying element.
8497      * @param {Object} box An object in the format {x, y, width, height}
8498      * @returns {Roo.BoxComponent} this
8499      */
8500     updateBox : function(box){
8501         this.setSize(box.width, box.height);
8502         this.setPagePosition(box.x, box.y);
8503         return this;
8504     },
8505
8506     // protected
8507     getResizeEl : function(){
8508         return this.resizeEl || this.el;
8509     },
8510
8511     // protected
8512     getPositionEl : function(){
8513         return this.positionEl || this.el;
8514     },
8515
8516     /**
8517      * Sets the left and top of the component.  To set the page XY position instead, use {@link #setPagePosition}.
8518      * This method fires the move event.
8519      * @param {Number} left The new left
8520      * @param {Number} top The new top
8521      * @returns {Roo.BoxComponent} this
8522      */
8523     setPosition : function(x, y){
8524         this.x = x;
8525         this.y = y;
8526         if(!this.boxReady){
8527             return this;
8528         }
8529         var adj = this.adjustPosition(x, y);
8530         var ax = adj.x, ay = adj.y;
8531
8532         var el = this.getPositionEl();
8533         if(ax !== undefined || ay !== undefined){
8534             if(ax !== undefined && ay !== undefined){
8535                 el.setLeftTop(ax, ay);
8536             }else if(ax !== undefined){
8537                 el.setLeft(ax);
8538             }else if(ay !== undefined){
8539                 el.setTop(ay);
8540             }
8541             this.onPosition(ax, ay);
8542             this.fireEvent('move', this, ax, ay);
8543         }
8544         return this;
8545     },
8546
8547     /**
8548      * Sets the page XY position of the component.  To set the left and top instead, use {@link #setPosition}.
8549      * This method fires the move event.
8550      * @param {Number} x The new x position
8551      * @param {Number} y The new y position
8552      * @returns {Roo.BoxComponent} this
8553      */
8554     setPagePosition : function(x, y){
8555         this.pageX = x;
8556         this.pageY = y;
8557         if(!this.boxReady){
8558             return;
8559         }
8560         if(x === undefined || y === undefined){ // cannot translate undefined points
8561             return;
8562         }
8563         var p = this.el.translatePoints(x, y);
8564         this.setPosition(p.left, p.top);
8565         return this;
8566     },
8567
8568     // private
8569     onRender : function(ct, position){
8570         Roo.BoxComponent.superclass.onRender.call(this, ct, position);
8571         if(this.resizeEl){
8572             this.resizeEl = Roo.get(this.resizeEl);
8573         }
8574         if(this.positionEl){
8575             this.positionEl = Roo.get(this.positionEl);
8576         }
8577     },
8578
8579     // private
8580     afterRender : function(){
8581         Roo.BoxComponent.superclass.afterRender.call(this);
8582         this.boxReady = true;
8583         this.setSize(this.width, this.height);
8584         if(this.x || this.y){
8585             this.setPosition(this.x, this.y);
8586         }
8587         if(this.pageX || this.pageY){
8588             this.setPagePosition(this.pageX, this.pageY);
8589         }
8590     },
8591
8592     /**
8593      * Force the component's size to recalculate based on the underlying element's current height and width.
8594      * @returns {Roo.BoxComponent} this
8595      */
8596     syncSize : function(){
8597         delete this.lastSize;
8598         this.setSize(this.el.getWidth(), this.el.getHeight());
8599         return this;
8600     },
8601
8602     /**
8603      * Called after the component is resized, this method is empty by default but can be implemented by any
8604      * subclass that needs to perform custom logic after a resize occurs.
8605      * @param {Number} adjWidth The box-adjusted width that was set
8606      * @param {Number} adjHeight The box-adjusted height that was set
8607      * @param {Number} rawWidth The width that was originally specified
8608      * @param {Number} rawHeight The height that was originally specified
8609      */
8610     onResize : function(adjWidth, adjHeight, rawWidth, rawHeight){
8611
8612     },
8613
8614     /**
8615      * Called after the component is moved, this method is empty by default but can be implemented by any
8616      * subclass that needs to perform custom logic after a move occurs.
8617      * @param {Number} x The new x position
8618      * @param {Number} y The new y position
8619      */
8620     onPosition : function(x, y){
8621
8622     },
8623
8624     // private
8625     adjustSize : function(w, h){
8626         if(this.autoWidth){
8627             w = 'auto';
8628         }
8629         if(this.autoHeight){
8630             h = 'auto';
8631         }
8632         return {width : w, height: h};
8633     },
8634
8635     // private
8636     adjustPosition : function(x, y){
8637         return {x : x, y: y};
8638     }
8639 });/*
8640  * Based on:
8641  * Ext JS Library 1.1.1
8642  * Copyright(c) 2006-2007, Ext JS, LLC.
8643  *
8644  * Originally Released Under LGPL - original licence link has changed is not relivant.
8645  *
8646  * Fork - LGPL
8647  * <script type="text/javascript">
8648  */
8649
8650
8651 /**
8652  * @class Roo.SplitBar
8653  * @extends Roo.util.Observable
8654  * Creates draggable splitter bar functionality from two elements (element to be dragged and element to be resized).
8655  * <br><br>
8656  * Usage:
8657  * <pre><code>
8658 var split = new Roo.SplitBar("elementToDrag", "elementToSize",
8659                    Roo.SplitBar.HORIZONTAL, Roo.SplitBar.LEFT);
8660 split.setAdapter(new Roo.SplitBar.AbsoluteLayoutAdapter("container"));
8661 split.minSize = 100;
8662 split.maxSize = 600;
8663 split.animate = true;
8664 split.on('moved', splitterMoved);
8665 </code></pre>
8666  * @constructor
8667  * Create a new SplitBar
8668  * @param {String/HTMLElement/Roo.Element} dragElement The element to be dragged and act as the SplitBar. 
8669  * @param {String/HTMLElement/Roo.Element} resizingElement The element to be resized based on where the SplitBar element is dragged 
8670  * @param {Number} orientation (optional) Either Roo.SplitBar.HORIZONTAL or Roo.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
8671  * @param {Number} placement (optional) Either Roo.SplitBar.LEFT or Roo.SplitBar.RIGHT for horizontal or  
8672                         Roo.SplitBar.TOP or Roo.SplitBar.BOTTOM for vertical. (By default, this is determined automatically by the initial
8673                         position of the SplitBar).
8674  */
8675 Roo.SplitBar = function(dragElement, resizingElement, orientation, placement, existingProxy){
8676     
8677     /** @private */
8678     this.el = Roo.get(dragElement, true);
8679     this.el.dom.unselectable = "on";
8680     /** @private */
8681     this.resizingEl = Roo.get(resizingElement, true);
8682
8683     /**
8684      * @private
8685      * The orientation of the split. Either Roo.SplitBar.HORIZONTAL or Roo.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
8686      * Note: If this is changed after creating the SplitBar, the placement property must be manually updated
8687      * @type Number
8688      */
8689     this.orientation = orientation || Roo.SplitBar.HORIZONTAL;
8690     
8691     /**
8692      * The minimum size of the resizing element. (Defaults to 0)
8693      * @type Number
8694      */
8695     this.minSize = 0;
8696     
8697     /**
8698      * The maximum size of the resizing element. (Defaults to 2000)
8699      * @type Number
8700      */
8701     this.maxSize = 2000;
8702     
8703     /**
8704      * Whether to animate the transition to the new size
8705      * @type Boolean
8706      */
8707     this.animate = false;
8708     
8709     /**
8710      * Whether to create a transparent shim that overlays the page when dragging, enables dragging across iframes.
8711      * @type Boolean
8712      */
8713     this.useShim = false;
8714     
8715     /** @private */
8716     this.shim = null;
8717     
8718     if(!existingProxy){
8719         /** @private */
8720         this.proxy = Roo.SplitBar.createProxy(this.orientation);
8721     }else{
8722         this.proxy = Roo.get(existingProxy).dom;
8723     }
8724     /** @private */
8725     this.dd = new Roo.dd.DDProxy(this.el.dom.id, "XSplitBars", {dragElId : this.proxy.id});
8726     
8727     /** @private */
8728     this.dd.b4StartDrag = this.onStartProxyDrag.createDelegate(this);
8729     
8730     /** @private */
8731     this.dd.endDrag = this.onEndProxyDrag.createDelegate(this);
8732     
8733     /** @private */
8734     this.dragSpecs = {};
8735     
8736     /**
8737      * @private The adapter to use to positon and resize elements
8738      */
8739     this.adapter = new Roo.SplitBar.BasicLayoutAdapter();
8740     this.adapter.init(this);
8741     
8742     if(this.orientation == Roo.SplitBar.HORIZONTAL){
8743         /** @private */
8744         this.placement = placement || (this.el.getX() > this.resizingEl.getX() ? Roo.SplitBar.LEFT : Roo.SplitBar.RIGHT);
8745         this.el.addClass("x-splitbar-h");
8746     }else{
8747         /** @private */
8748         this.placement = placement || (this.el.getY() > this.resizingEl.getY() ? Roo.SplitBar.TOP : Roo.SplitBar.BOTTOM);
8749         this.el.addClass("x-splitbar-v");
8750     }
8751     
8752     this.addEvents({
8753         /**
8754          * @event resize
8755          * Fires when the splitter is moved (alias for {@link #event-moved})
8756          * @param {Roo.SplitBar} this
8757          * @param {Number} newSize the new width or height
8758          */
8759         "resize" : true,
8760         /**
8761          * @event moved
8762          * Fires when the splitter is moved
8763          * @param {Roo.SplitBar} this
8764          * @param {Number} newSize the new width or height
8765          */
8766         "moved" : true,
8767         /**
8768          * @event beforeresize
8769          * Fires before the splitter is dragged
8770          * @param {Roo.SplitBar} this
8771          */
8772         "beforeresize" : true,
8773
8774         "beforeapply" : true
8775     });
8776
8777     Roo.util.Observable.call(this);
8778 };
8779
8780 Roo.extend(Roo.SplitBar, Roo.util.Observable, {
8781     onStartProxyDrag : function(x, y){
8782         this.fireEvent("beforeresize", this);
8783         if(!this.overlay){
8784             var o = Roo.DomHelper.insertFirst(document.body,  {cls: "x-drag-overlay", html: "&#160;"}, true);
8785             o.unselectable();
8786             o.enableDisplayMode("block");
8787             // all splitbars share the same overlay
8788             Roo.SplitBar.prototype.overlay = o;
8789         }
8790         this.overlay.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
8791         this.overlay.show();
8792         Roo.get(this.proxy).setDisplayed("block");
8793         var size = this.adapter.getElementSize(this);
8794         this.activeMinSize = this.getMinimumSize();;
8795         this.activeMaxSize = this.getMaximumSize();;
8796         var c1 = size - this.activeMinSize;
8797         var c2 = Math.max(this.activeMaxSize - size, 0);
8798         if(this.orientation == Roo.SplitBar.HORIZONTAL){
8799             this.dd.resetConstraints();
8800             this.dd.setXConstraint(
8801                 this.placement == Roo.SplitBar.LEFT ? c1 : c2, 
8802                 this.placement == Roo.SplitBar.LEFT ? c2 : c1
8803             );
8804             this.dd.setYConstraint(0, 0);
8805         }else{
8806             this.dd.resetConstraints();
8807             this.dd.setXConstraint(0, 0);
8808             this.dd.setYConstraint(
8809                 this.placement == Roo.SplitBar.TOP ? c1 : c2, 
8810                 this.placement == Roo.SplitBar.TOP ? c2 : c1
8811             );
8812          }
8813         this.dragSpecs.startSize = size;
8814         this.dragSpecs.startPoint = [x, y];
8815         Roo.dd.DDProxy.prototype.b4StartDrag.call(this.dd, x, y);
8816     },
8817     
8818     /** 
8819      * @private Called after the drag operation by the DDProxy
8820      */
8821     onEndProxyDrag : function(e){
8822         Roo.get(this.proxy).setDisplayed(false);
8823         var endPoint = Roo.lib.Event.getXY(e);
8824         if(this.overlay){
8825             this.overlay.hide();
8826         }
8827         var newSize;
8828         if(this.orientation == Roo.SplitBar.HORIZONTAL){
8829             newSize = this.dragSpecs.startSize + 
8830                 (this.placement == Roo.SplitBar.LEFT ?
8831                     endPoint[0] - this.dragSpecs.startPoint[0] :
8832                     this.dragSpecs.startPoint[0] - endPoint[0]
8833                 );
8834         }else{
8835             newSize = this.dragSpecs.startSize + 
8836                 (this.placement == Roo.SplitBar.TOP ?
8837                     endPoint[1] - this.dragSpecs.startPoint[1] :
8838                     this.dragSpecs.startPoint[1] - endPoint[1]
8839                 );
8840         }
8841         newSize = Math.min(Math.max(newSize, this.activeMinSize), this.activeMaxSize);
8842         if(newSize != this.dragSpecs.startSize){
8843             if(this.fireEvent('beforeapply', this, newSize) !== false){
8844                 this.adapter.setElementSize(this, newSize);
8845                 this.fireEvent("moved", this, newSize);
8846                 this.fireEvent("resize", this, newSize);
8847             }
8848         }
8849     },
8850     
8851     /**
8852      * Get the adapter this SplitBar uses
8853      * @return The adapter object
8854      */
8855     getAdapter : function(){
8856         return this.adapter;
8857     },
8858     
8859     /**
8860      * Set the adapter this SplitBar uses
8861      * @param {Object} adapter A SplitBar adapter object
8862      */
8863     setAdapter : function(adapter){
8864         this.adapter = adapter;
8865         this.adapter.init(this);
8866     },
8867     
8868     /**
8869      * Gets the minimum size for the resizing element
8870      * @return {Number} The minimum size
8871      */
8872     getMinimumSize : function(){
8873         return this.minSize;
8874     },
8875     
8876     /**
8877      * Sets the minimum size for the resizing element
8878      * @param {Number} minSize The minimum size
8879      */
8880     setMinimumSize : function(minSize){
8881         this.minSize = minSize;
8882     },
8883     
8884     /**
8885      * Gets the maximum size for the resizing element
8886      * @return {Number} The maximum size
8887      */
8888     getMaximumSize : function(){
8889         return this.maxSize;
8890     },
8891     
8892     /**
8893      * Sets the maximum size for the resizing element
8894      * @param {Number} maxSize The maximum size
8895      */
8896     setMaximumSize : function(maxSize){
8897         this.maxSize = maxSize;
8898     },
8899     
8900     /**
8901      * Sets the initialize size for the resizing element
8902      * @param {Number} size The initial size
8903      */
8904     setCurrentSize : function(size){
8905         var oldAnimate = this.animate;
8906         this.animate = false;
8907         this.adapter.setElementSize(this, size);
8908         this.animate = oldAnimate;
8909     },
8910     
8911     /**
8912      * Destroy this splitbar. 
8913      * @param {Boolean} removeEl True to remove the element
8914      */
8915     destroy : function(removeEl){
8916         if(this.shim){
8917             this.shim.remove();
8918         }
8919         this.dd.unreg();
8920         this.proxy.parentNode.removeChild(this.proxy);
8921         if(removeEl){
8922             this.el.remove();
8923         }
8924     }
8925 });
8926
8927 /**
8928  * @private static Create our own proxy element element. So it will be the same same size on all browsers, we won't use borders. Instead we use a background color.
8929  */
8930 Roo.SplitBar.createProxy = function(dir){
8931     var proxy = new Roo.Element(document.createElement("div"));
8932     proxy.unselectable();
8933     var cls = 'x-splitbar-proxy';
8934     proxy.addClass(cls + ' ' + (dir == Roo.SplitBar.HORIZONTAL ? cls +'-h' : cls + '-v'));
8935     document.body.appendChild(proxy.dom);
8936     return proxy.dom;
8937 };
8938
8939 /** 
8940  * @class Roo.SplitBar.BasicLayoutAdapter
8941  * Default Adapter. It assumes the splitter and resizing element are not positioned
8942  * elements and only gets/sets the width of the element. Generally used for table based layouts.
8943  */
8944 Roo.SplitBar.BasicLayoutAdapter = function(){
8945 };
8946
8947 Roo.SplitBar.BasicLayoutAdapter.prototype = {
8948     // do nothing for now
8949     init : function(s){
8950     
8951     },
8952     /**
8953      * Called before drag operations to get the current size of the resizing element. 
8954      * @param {Roo.SplitBar} s The SplitBar using this adapter
8955      */
8956      getElementSize : function(s){
8957         if(s.orientation == Roo.SplitBar.HORIZONTAL){
8958             return s.resizingEl.getWidth();
8959         }else{
8960             return s.resizingEl.getHeight();
8961         }
8962     },
8963     
8964     /**
8965      * Called after drag operations to set the size of the resizing element.
8966      * @param {Roo.SplitBar} s The SplitBar using this adapter
8967      * @param {Number} newSize The new size to set
8968      * @param {Function} onComplete A function to be invoked when resizing is complete
8969      */
8970     setElementSize : function(s, newSize, onComplete){
8971         if(s.orientation == Roo.SplitBar.HORIZONTAL){
8972             if(!s.animate){
8973                 s.resizingEl.setWidth(newSize);
8974                 if(onComplete){
8975                     onComplete(s, newSize);
8976                 }
8977             }else{
8978                 s.resizingEl.setWidth(newSize, true, .1, onComplete, 'easeOut');
8979             }
8980         }else{
8981             
8982             if(!s.animate){
8983                 s.resizingEl.setHeight(newSize);
8984                 if(onComplete){
8985                     onComplete(s, newSize);
8986                 }
8987             }else{
8988                 s.resizingEl.setHeight(newSize, true, .1, onComplete, 'easeOut');
8989             }
8990         }
8991     }
8992 };
8993
8994 /** 
8995  *@class Roo.SplitBar.AbsoluteLayoutAdapter
8996  * @extends Roo.SplitBar.BasicLayoutAdapter
8997  * Adapter that  moves the splitter element to align with the resized sizing element. 
8998  * Used with an absolute positioned SplitBar.
8999  * @param {String/HTMLElement/Roo.Element} container The container that wraps around the absolute positioned content. If it's
9000  * document.body, make sure you assign an id to the body element.
9001  */
9002 Roo.SplitBar.AbsoluteLayoutAdapter = function(container){
9003     this.basic = new Roo.SplitBar.BasicLayoutAdapter();
9004     this.container = Roo.get(container);
9005 };
9006
9007 Roo.SplitBar.AbsoluteLayoutAdapter.prototype = {
9008     init : function(s){
9009         this.basic.init(s);
9010     },
9011     
9012     getElementSize : function(s){
9013         return this.basic.getElementSize(s);
9014     },
9015     
9016     setElementSize : function(s, newSize, onComplete){
9017         this.basic.setElementSize(s, newSize, this.moveSplitter.createDelegate(this, [s]));
9018     },
9019     
9020     moveSplitter : function(s){
9021         var yes = Roo.SplitBar;
9022         switch(s.placement){
9023             case yes.LEFT:
9024                 s.el.setX(s.resizingEl.getRight());
9025                 break;
9026             case yes.RIGHT:
9027                 s.el.setStyle("right", (this.container.getWidth() - s.resizingEl.getLeft()) + "px");
9028                 break;
9029             case yes.TOP:
9030                 s.el.setY(s.resizingEl.getBottom());
9031                 break;
9032             case yes.BOTTOM:
9033                 s.el.setY(s.resizingEl.getTop() - s.el.getHeight());
9034                 break;
9035         }
9036     }
9037 };
9038
9039 /**
9040  * Orientation constant - Create a vertical SplitBar
9041  * @static
9042  * @type Number
9043  */
9044 Roo.SplitBar.VERTICAL = 1;
9045
9046 /**
9047  * Orientation constant - Create a horizontal SplitBar
9048  * @static
9049  * @type Number
9050  */
9051 Roo.SplitBar.HORIZONTAL = 2;
9052
9053 /**
9054  * Placement constant - The resizing element is to the left of the splitter element
9055  * @static
9056  * @type Number
9057  */
9058 Roo.SplitBar.LEFT = 1;
9059
9060 /**
9061  * Placement constant - The resizing element is to the right of the splitter element
9062  * @static
9063  * @type Number
9064  */
9065 Roo.SplitBar.RIGHT = 2;
9066
9067 /**
9068  * Placement constant - The resizing element is positioned above the splitter element
9069  * @static
9070  * @type Number
9071  */
9072 Roo.SplitBar.TOP = 3;
9073
9074 /**
9075  * Placement constant - The resizing element is positioned under splitter element
9076  * @static
9077  * @type Number
9078  */
9079 Roo.SplitBar.BOTTOM = 4;
9080 /*
9081  * Based on:
9082  * Ext JS Library 1.1.1
9083  * Copyright(c) 2006-2007, Ext JS, LLC.
9084  *
9085  * Originally Released Under LGPL - original licence link has changed is not relivant.
9086  *
9087  * Fork - LGPL
9088  * <script type="text/javascript">
9089  */
9090
9091 /**
9092  * @class Roo.View
9093  * @extends Roo.util.Observable
9094  * Create a "View" for an element based on a data model or UpdateManager and the supplied DomHelper template. 
9095  * This class also supports single and multi selection modes. <br>
9096  * Create a data model bound view:
9097  <pre><code>
9098  var store = new Roo.data.Store(...);
9099
9100  var view = new Roo.View({
9101     el : "my-element",
9102     tpl : '&lt;div id="{0}"&gt;{2} - {1}&lt;/div&gt;', // auto create template
9103  
9104     singleSelect: true,
9105     selectedClass: "ydataview-selected",
9106     store: store
9107  });
9108
9109  // listen for node click?
9110  view.on("click", function(vw, index, node, e){
9111  alert('Node "' + node.id + '" at index: ' + index + " was clicked.");
9112  });
9113
9114  // load XML data
9115  dataModel.load("foobar.xml");
9116  </code></pre>
9117  For an example of creating a JSON/UpdateManager view, see {@link Roo.JsonView}.
9118  * <br><br>
9119  * <b>Note: The root of your template must be a single node. Table/row implementations may work but are not supported due to
9120  * IE"s limited insertion support with tables and Opera"s faulty event bubbling.</b>
9121  * 
9122  * Note: old style constructor is still suported (container, template, config)
9123  * 
9124  * @constructor
9125  * Create a new View
9126  * @param {Object} config The config object
9127  * 
9128  */
9129 Roo.View = function(config, depreciated_tpl, depreciated_config){
9130     
9131     if (typeof(depreciated_tpl) == 'undefined') {
9132         // new way.. - universal constructor.
9133         Roo.apply(this, config);
9134         this.el  = Roo.get(this.el);
9135     } else {
9136         // old format..
9137         this.el  = Roo.get(config);
9138         this.tpl = depreciated_tpl;
9139         Roo.apply(this, depreciated_config);
9140     }
9141      
9142     
9143     if(typeof(this.tpl) == "string"){
9144         this.tpl = new Roo.Template(this.tpl);
9145     } else {
9146         // support xtype ctors..
9147         this.tpl = new Roo.factory(this.tpl, Roo);
9148     }
9149     
9150     
9151     this.tpl.compile();
9152    
9153
9154      
9155     /** @private */
9156     this.addEvents({
9157         /**
9158          * @event beforeclick
9159          * Fires before a click is processed. Returns false to cancel the default action.
9160          * @param {Roo.View} this
9161          * @param {Number} index The index of the target node
9162          * @param {HTMLElement} node The target node
9163          * @param {Roo.EventObject} e The raw event object
9164          */
9165             "beforeclick" : true,
9166         /**
9167          * @event click
9168          * Fires when a template node is clicked.
9169          * @param {Roo.View} this
9170          * @param {Number} index The index of the target node
9171          * @param {HTMLElement} node The target node
9172          * @param {Roo.EventObject} e The raw event object
9173          */
9174             "click" : true,
9175         /**
9176          * @event dblclick
9177          * Fires when a template node is double clicked.
9178          * @param {Roo.View} this
9179          * @param {Number} index The index of the target node
9180          * @param {HTMLElement} node The target node
9181          * @param {Roo.EventObject} e The raw event object
9182          */
9183             "dblclick" : true,
9184         /**
9185          * @event contextmenu
9186          * Fires when a template node is right clicked.
9187          * @param {Roo.View} this
9188          * @param {Number} index The index of the target node
9189          * @param {HTMLElement} node The target node
9190          * @param {Roo.EventObject} e The raw event object
9191          */
9192             "contextmenu" : true,
9193         /**
9194          * @event selectionchange
9195          * Fires when the selected nodes change.
9196          * @param {Roo.View} this
9197          * @param {Array} selections Array of the selected nodes
9198          */
9199             "selectionchange" : true,
9200     
9201         /**
9202          * @event beforeselect
9203          * Fires before a selection is made. If any handlers return false, the selection is cancelled.
9204          * @param {Roo.View} this
9205          * @param {HTMLElement} node The node to be selected
9206          * @param {Array} selections Array of currently selected nodes
9207          */
9208             "beforeselect" : true,
9209         /**
9210          * @event preparedata
9211          * Fires on every row to render, to allow you to change the data.
9212          * @param {Roo.View} this
9213          * @param {Object} data to be rendered (change this)
9214          * @param {Number} row being rendered
9215          * @param {Roo.data.Record} record being rendered.
9216          */
9217           "preparedata" : true
9218         });
9219
9220     this.el.on({
9221         "click": this.onClick,
9222         "dblclick": this.onDblClick,
9223         "contextmenu": this.onContextMenu,
9224         scope:this
9225     });
9226
9227     this.selections = [];
9228     this.nodes = [];
9229     this.cmp = new Roo.CompositeElementLite([]);
9230     if(this.store){
9231         this.store = Roo.factory(this.store, Roo.data);
9232         this.setStore(this.store, true);
9233     }
9234     Roo.View.superclass.constructor.call(this);
9235 };
9236
9237 Roo.extend(Roo.View, Roo.util.Observable, {
9238     
9239      /**
9240      * @cfg {Roo.data.Store} store Data store to load data from.
9241      */
9242     store : false,
9243     
9244     /**
9245      * @cfg {String|Roo.Element} el The container element.
9246      */
9247     el : '',
9248     
9249     /**
9250      * @cfg {String|Roo.Template} tpl The template used by this View 
9251      */
9252     tpl : false,
9253     
9254     /**
9255      * @cfg {String} selectedClass The css class to add to selected nodes
9256      */
9257     selectedClass : "x-view-selected",
9258      /**
9259      * @cfg {String} emptyText The empty text to show when nothing is loaded.
9260      */
9261     emptyText : "",
9262     /**
9263      * @cfg {Boolean} multiSelect Allow multiple selection
9264      */
9265     
9266     multiSelect : false,
9267     /**
9268      * @cfg {Boolean} singleSelect Allow single selection
9269      */
9270     singleSelect:  false,
9271     
9272     /**
9273      * Returns the element this view is bound to.
9274      * @return {Roo.Element}
9275      */
9276     getEl : function(){
9277         return this.el;
9278     },
9279
9280     /**
9281      * Refreshes the view.
9282      */
9283     refresh : function(){
9284         var t = this.tpl;
9285         this.clearSelections();
9286         this.el.update("");
9287         var html = [];
9288         var records = this.store.getRange();
9289         if(records.length < 1){
9290             this.el.update(this.emptyText);
9291             return;
9292         }
9293         for(var i = 0, len = records.length; i < len; i++){
9294             var data = this.prepareData(records[i].data, i, records[i]);
9295             this.fireEvent("preparedata", this, data, i, records[i]);
9296             html[html.length] = t.apply(data);
9297         }
9298         this.el.update(html.join(""));
9299         this.nodes = this.el.dom.childNodes;
9300         this.updateIndexes(0);
9301     },
9302
9303     /**
9304      * Function to override to reformat the data that is sent to
9305      * the template for each node.
9306      * @param {Array/Object} data The raw data (array of colData for a data model bound view or
9307      * a JSON object for an UpdateManager bound view).
9308      */
9309     prepareData : function(data){
9310         return data;
9311     },
9312
9313     onUpdate : function(ds, record){
9314         this.clearSelections();
9315         var index = this.store.indexOf(record);
9316         var n = this.nodes[index];
9317         this.tpl.insertBefore(n, this.prepareData(record.data));
9318         n.parentNode.removeChild(n);
9319         this.updateIndexes(index, index);
9320     },
9321
9322     onAdd : function(ds, records, index){
9323         this.clearSelections();
9324         if(this.nodes.length == 0){
9325             this.refresh();
9326             return;
9327         }
9328         var n = this.nodes[index];
9329         for(var i = 0, len = records.length; i < len; i++){
9330             var d = this.prepareData(records[i].data);
9331             if(n){
9332                 this.tpl.insertBefore(n, d);
9333             }else{
9334                 this.tpl.append(this.el, d);
9335             }
9336         }
9337         this.updateIndexes(index);
9338     },
9339
9340     onRemove : function(ds, record, index){
9341         this.clearSelections();
9342         this.el.dom.removeChild(this.nodes[index]);
9343         this.updateIndexes(index);
9344     },
9345
9346     /**
9347      * Refresh an individual node.
9348      * @param {Number} index
9349      */
9350     refreshNode : function(index){
9351         this.onUpdate(this.store, this.store.getAt(index));
9352     },
9353
9354     updateIndexes : function(startIndex, endIndex){
9355         var ns = this.nodes;
9356         startIndex = startIndex || 0;
9357         endIndex = endIndex || ns.length - 1;
9358         for(var i = startIndex; i <= endIndex; i++){
9359             ns[i].nodeIndex = i;
9360         }
9361     },
9362
9363     /**
9364      * Changes the data store this view uses and refresh the view.
9365      * @param {Store} store
9366      */
9367     setStore : function(store, initial){
9368         if(!initial && this.store){
9369             this.store.un("datachanged", this.refresh);
9370             this.store.un("add", this.onAdd);
9371             this.store.un("remove", this.onRemove);
9372             this.store.un("update", this.onUpdate);
9373             this.store.un("clear", this.refresh);
9374         }
9375         if(store){
9376           
9377             store.on("datachanged", this.refresh, this);
9378             store.on("add", this.onAdd, this);
9379             store.on("remove", this.onRemove, this);
9380             store.on("update", this.onUpdate, this);
9381             store.on("clear", this.refresh, this);
9382         }
9383         
9384         if(store){
9385             this.refresh();
9386         }
9387     },
9388
9389     /**
9390      * Returns the template node the passed child belongs to or null if it doesn't belong to one.
9391      * @param {HTMLElement} node
9392      * @return {HTMLElement} The template node
9393      */
9394     findItemFromChild : function(node){
9395         var el = this.el.dom;
9396         if(!node || node.parentNode == el){
9397                     return node;
9398             }
9399             var p = node.parentNode;
9400             while(p && p != el){
9401             if(p.parentNode == el){
9402                 return p;
9403             }
9404             p = p.parentNode;
9405         }
9406             return null;
9407     },
9408
9409     /** @ignore */
9410     onClick : function(e){
9411         var item = this.findItemFromChild(e.getTarget());
9412         if(item){
9413             var index = this.indexOf(item);
9414             if(this.onItemClick(item, index, e) !== false){
9415                 this.fireEvent("click", this, index, item, e);
9416             }
9417         }else{
9418             this.clearSelections();
9419         }
9420     },
9421
9422     /** @ignore */
9423     onContextMenu : function(e){
9424         var item = this.findItemFromChild(e.getTarget());
9425         if(item){
9426             this.fireEvent("contextmenu", this, this.indexOf(item), item, e);
9427         }
9428     },
9429
9430     /** @ignore */
9431     onDblClick : function(e){
9432         var item = this.findItemFromChild(e.getTarget());
9433         if(item){
9434             this.fireEvent("dblclick", this, this.indexOf(item), item, e);
9435         }
9436     },
9437
9438     onItemClick : function(item, index, e){
9439         if(this.fireEvent("beforeclick", this, index, item, e) === false){
9440             return false;
9441         }
9442         if(this.multiSelect || this.singleSelect){
9443             if(this.multiSelect && e.shiftKey && this.lastSelection){
9444                 this.select(this.getNodes(this.indexOf(this.lastSelection), index), false);
9445             }else{
9446                 this.select(item, this.multiSelect && e.ctrlKey);
9447                 this.lastSelection = item;
9448             }
9449             e.preventDefault();
9450         }
9451         return true;
9452     },
9453
9454     /**
9455      * Get the number of selected nodes.
9456      * @return {Number}
9457      */
9458     getSelectionCount : function(){
9459         return this.selections.length;
9460     },
9461
9462     /**
9463      * Get the currently selected nodes.
9464      * @return {Array} An array of HTMLElements
9465      */
9466     getSelectedNodes : function(){
9467         return this.selections;
9468     },
9469
9470     /**
9471      * Get the indexes of the selected nodes.
9472      * @return {Array}
9473      */
9474     getSelectedIndexes : function(){
9475         var indexes = [], s = this.selections;
9476         for(var i = 0, len = s.length; i < len; i++){
9477             indexes.push(s[i].nodeIndex);
9478         }
9479         return indexes;
9480     },
9481
9482     /**
9483      * Clear all selections
9484      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange event
9485      */
9486     clearSelections : function(suppressEvent){
9487         if(this.nodes && (this.multiSelect || this.singleSelect) && this.selections.length > 0){
9488             this.cmp.elements = this.selections;
9489             this.cmp.removeClass(this.selectedClass);
9490             this.selections = [];
9491             if(!suppressEvent){
9492                 this.fireEvent("selectionchange", this, this.selections);
9493             }
9494         }
9495     },
9496
9497     /**
9498      * Returns true if the passed node is selected
9499      * @param {HTMLElement/Number} node The node or node index
9500      * @return {Boolean}
9501      */
9502     isSelected : function(node){
9503         var s = this.selections;
9504         if(s.length < 1){
9505             return false;
9506         }
9507         node = this.getNode(node);
9508         return s.indexOf(node) !== -1;
9509     },
9510
9511     /**
9512      * Selects nodes.
9513      * @param {Array/HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node, id of a template node or an array of any of those to select
9514      * @param {Boolean} keepExisting (optional) true to keep existing selections
9515      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
9516      */
9517     select : function(nodeInfo, keepExisting, suppressEvent){
9518         if(nodeInfo instanceof Array){
9519             if(!keepExisting){
9520                 this.clearSelections(true);
9521             }
9522             for(var i = 0, len = nodeInfo.length; i < len; i++){
9523                 this.select(nodeInfo[i], true, true);
9524             }
9525         } else{
9526             var node = this.getNode(nodeInfo);
9527             if(node && !this.isSelected(node)){
9528                 if(!keepExisting){
9529                     this.clearSelections(true);
9530                 }
9531                 if(this.fireEvent("beforeselect", this, node, this.selections) !== false){
9532                     Roo.fly(node).addClass(this.selectedClass);
9533                     this.selections.push(node);
9534                     if(!suppressEvent){
9535                         this.fireEvent("selectionchange", this, this.selections);
9536                     }
9537                 }
9538             }
9539         }
9540     },
9541
9542     /**
9543      * Gets a template node.
9544      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
9545      * @return {HTMLElement} The node or null if it wasn't found
9546      */
9547     getNode : function(nodeInfo){
9548         if(typeof nodeInfo == "string"){
9549             return document.getElementById(nodeInfo);
9550         }else if(typeof nodeInfo == "number"){
9551             return this.nodes[nodeInfo];
9552         }
9553         return nodeInfo;
9554     },
9555
9556     /**
9557      * Gets a range template nodes.
9558      * @param {Number} startIndex
9559      * @param {Number} endIndex
9560      * @return {Array} An array of nodes
9561      */
9562     getNodes : function(start, end){
9563         var ns = this.nodes;
9564         start = start || 0;
9565         end = typeof end == "undefined" ? ns.length - 1 : end;
9566         var nodes = [];
9567         if(start <= end){
9568             for(var i = start; i <= end; i++){
9569                 nodes.push(ns[i]);
9570             }
9571         } else{
9572             for(var i = start; i >= end; i--){
9573                 nodes.push(ns[i]);
9574             }
9575         }
9576         return nodes;
9577     },
9578
9579     /**
9580      * Finds the index of the passed node
9581      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
9582      * @return {Number} The index of the node or -1
9583      */
9584     indexOf : function(node){
9585         node = this.getNode(node);
9586         if(typeof node.nodeIndex == "number"){
9587             return node.nodeIndex;
9588         }
9589         var ns = this.nodes;
9590         for(var i = 0, len = ns.length; i < len; i++){
9591             if(ns[i] == node){
9592                 return i;
9593             }
9594         }
9595         return -1;
9596     }
9597 });
9598 /*
9599  * Based on:
9600  * Ext JS Library 1.1.1
9601  * Copyright(c) 2006-2007, Ext JS, LLC.
9602  *
9603  * Originally Released Under LGPL - original licence link has changed is not relivant.
9604  *
9605  * Fork - LGPL
9606  * <script type="text/javascript">
9607  */
9608
9609 /**
9610  * @class Roo.JsonView
9611  * @extends Roo.View
9612  * Shortcut class to create a JSON + {@link Roo.UpdateManager} template view. Usage:
9613 <pre><code>
9614 var view = new Roo.JsonView({
9615     container: "my-element",
9616     tpl: '&lt;div id="{id}"&gt;{foo} - {bar}&lt;/div&gt;', // auto create template
9617     multiSelect: true, 
9618     jsonRoot: "data" 
9619 });
9620
9621 // listen for node click?
9622 view.on("click", function(vw, index, node, e){
9623     alert('Node "' + node.id + '" at index: ' + index + " was clicked.");
9624 });
9625
9626 // direct load of JSON data
9627 view.load("foobar.php");
9628
9629 // Example from my blog list
9630 var tpl = new Roo.Template(
9631     '&lt;div class="entry"&gt;' +
9632     '&lt;a class="entry-title" href="{link}"&gt;{title}&lt;/a&gt;' +
9633     "&lt;h4&gt;{date} by {author} | {comments} Comments&lt;/h4&gt;{description}" +
9634     "&lt;/div&gt;&lt;hr /&gt;"
9635 );
9636
9637 var moreView = new Roo.JsonView({
9638     container :  "entry-list", 
9639     template : tpl,
9640     jsonRoot: "posts"
9641 });
9642 moreView.on("beforerender", this.sortEntries, this);
9643 moreView.load({
9644     url: "/blog/get-posts.php",
9645     params: "allposts=true",
9646     text: "Loading Blog Entries..."
9647 });
9648 </code></pre>
9649
9650 * Note: old code is supported with arguments : (container, template, config)
9651
9652
9653  * @constructor
9654  * Create a new JsonView
9655  * 
9656  * @param {Object} config The config object
9657  * 
9658  */
9659 Roo.JsonView = function(config, depreciated_tpl, depreciated_config){
9660     
9661     
9662     Roo.JsonView.superclass.constructor.call(this, config, depreciated_tpl, depreciated_config);
9663
9664     var um = this.el.getUpdateManager();
9665     um.setRenderer(this);
9666     um.on("update", this.onLoad, this);
9667     um.on("failure", this.onLoadException, this);
9668
9669     /**
9670      * @event beforerender
9671      * Fires before rendering of the downloaded JSON data.
9672      * @param {Roo.JsonView} this
9673      * @param {Object} data The JSON data loaded
9674      */
9675     /**
9676      * @event load
9677      * Fires when data is loaded.
9678      * @param {Roo.JsonView} this
9679      * @param {Object} data The JSON data loaded
9680      * @param {Object} response The raw Connect response object
9681      */
9682     /**
9683      * @event loadexception
9684      * Fires when loading fails.
9685      * @param {Roo.JsonView} this
9686      * @param {Object} response The raw Connect response object
9687      */
9688     this.addEvents({
9689         'beforerender' : true,
9690         'load' : true,
9691         'loadexception' : true
9692     });
9693 };
9694 Roo.extend(Roo.JsonView, Roo.View, {
9695     /**
9696      * @type {String} The root property in the loaded JSON object that contains the data
9697      */
9698     jsonRoot : "",
9699
9700     /**
9701      * Refreshes the view.
9702      */
9703     refresh : function(){
9704         this.clearSelections();
9705         this.el.update("");
9706         var html = [];
9707         var o = this.jsonData;
9708         if(o && o.length > 0){
9709             for(var i = 0, len = o.length; i < len; i++){
9710                 var data = this.prepareData(o[i], i, o);
9711                 html[html.length] = this.tpl.apply(data);
9712             }
9713         }else{
9714             html.push(this.emptyText);
9715         }
9716         this.el.update(html.join(""));
9717         this.nodes = this.el.dom.childNodes;
9718         this.updateIndexes(0);
9719     },
9720
9721     /**
9722      * Performs an async HTTP request, and loads the JSON from the response. If <i>params</i> are specified it uses POST, otherwise it uses GET.
9723      * @param {Object/String/Function} url The URL for this request, or a function to call to get the URL, or a config object containing any of the following options:
9724      <pre><code>
9725      view.load({
9726          url: "your-url.php",
9727          params: {param1: "foo", param2: "bar"}, // or a URL encoded string
9728          callback: yourFunction,
9729          scope: yourObject, //(optional scope)
9730          discardUrl: false,
9731          nocache: false,
9732          text: "Loading...",
9733          timeout: 30,
9734          scripts: false
9735      });
9736      </code></pre>
9737      * The only required property is <i>url</i>. The optional properties <i>nocache</i>, <i>text</i> and <i>scripts</i>
9738      * are respectively shorthand for <i>disableCaching</i>, <i>indicatorText</i>, and <i>loadScripts</i> and are used to set their associated property on this UpdateManager instance.
9739      * @param {String/Object} params (optional) The parameters to pass, as either a URL encoded string "param1=1&amp;param2=2" or an object {param1: 1, param2: 2}
9740      * @param {Function} callback (optional) Callback when transaction is complete - called with signature (oElement, bSuccess)
9741      * @param {Boolean} discardUrl (optional) By default when you execute an update the defaultUrl is changed to the last used URL. If true, it will not store the URL.
9742      */
9743     load : function(){
9744         var um = this.el.getUpdateManager();
9745         um.update.apply(um, arguments);
9746     },
9747
9748     render : function(el, response){
9749         this.clearSelections();
9750         this.el.update("");
9751         var o;
9752         try{
9753             o = Roo.util.JSON.decode(response.responseText);
9754             if(this.jsonRoot){
9755                 
9756                 o = o[this.jsonRoot];
9757             }
9758         } catch(e){
9759         }
9760         /**
9761          * The current JSON data or null
9762          */
9763         this.jsonData = o;
9764         this.beforeRender();
9765         this.refresh();
9766     },
9767
9768 /**
9769  * Get the number of records in the current JSON dataset
9770  * @return {Number}
9771  */
9772     getCount : function(){
9773         return this.jsonData ? this.jsonData.length : 0;
9774     },
9775
9776 /**
9777  * Returns the JSON object for the specified node(s)
9778  * @param {HTMLElement/Array} node The node or an array of nodes
9779  * @return {Object/Array} If you pass in an array, you get an array back, otherwise
9780  * you get the JSON object for the node
9781  */
9782     getNodeData : function(node){
9783         if(node instanceof Array){
9784             var data = [];
9785             for(var i = 0, len = node.length; i < len; i++){
9786                 data.push(this.getNodeData(node[i]));
9787             }
9788             return data;
9789         }
9790         return this.jsonData[this.indexOf(node)] || null;
9791     },
9792
9793     beforeRender : function(){
9794         this.snapshot = this.jsonData;
9795         if(this.sortInfo){
9796             this.sort.apply(this, this.sortInfo);
9797         }
9798         this.fireEvent("beforerender", this, this.jsonData);
9799     },
9800
9801     onLoad : function(el, o){
9802         this.fireEvent("load", this, this.jsonData, o);
9803     },
9804
9805     onLoadException : function(el, o){
9806         this.fireEvent("loadexception", this, o);
9807     },
9808
9809 /**
9810  * Filter the data by a specific property.
9811  * @param {String} property A property on your JSON objects
9812  * @param {String/RegExp} value Either string that the property values
9813  * should start with, or a RegExp to test against the property
9814  */
9815     filter : function(property, value){
9816         if(this.jsonData){
9817             var data = [];
9818             var ss = this.snapshot;
9819             if(typeof value == "string"){
9820                 var vlen = value.length;
9821                 if(vlen == 0){
9822                     this.clearFilter();
9823                     return;
9824                 }
9825                 value = value.toLowerCase();
9826                 for(var i = 0, len = ss.length; i < len; i++){
9827                     var o = ss[i];
9828                     if(o[property].substr(0, vlen).toLowerCase() == value){
9829                         data.push(o);
9830                     }
9831                 }
9832             } else if(value.exec){ // regex?
9833                 for(var i = 0, len = ss.length; i < len; i++){
9834                     var o = ss[i];
9835                     if(value.test(o[property])){
9836                         data.push(o);
9837                     }
9838                 }
9839             } else{
9840                 return;
9841             }
9842             this.jsonData = data;
9843             this.refresh();
9844         }
9845     },
9846
9847 /**
9848  * Filter by a function. The passed function will be called with each
9849  * object in the current dataset. If the function returns true the value is kept,
9850  * otherwise it is filtered.
9851  * @param {Function} fn
9852  * @param {Object} scope (optional) The scope of the function (defaults to this JsonView)
9853  */
9854     filterBy : function(fn, scope){
9855         if(this.jsonData){
9856             var data = [];
9857             var ss = this.snapshot;
9858             for(var i = 0, len = ss.length; i < len; i++){
9859                 var o = ss[i];
9860                 if(fn.call(scope || this, o)){
9861                     data.push(o);
9862                 }
9863             }
9864             this.jsonData = data;
9865             this.refresh();
9866         }
9867     },
9868
9869 /**
9870  * Clears the current filter.
9871  */
9872     clearFilter : function(){
9873         if(this.snapshot && this.jsonData != this.snapshot){
9874             this.jsonData = this.snapshot;
9875             this.refresh();
9876         }
9877     },
9878
9879
9880 /**
9881  * Sorts the data for this view and refreshes it.
9882  * @param {String} property A property on your JSON objects to sort on
9883  * @param {String} direction (optional) "desc" or "asc" (defaults to "asc")
9884  * @param {Function} sortType (optional) A function to call to convert the data to a sortable value.
9885  */
9886     sort : function(property, dir, sortType){
9887         this.sortInfo = Array.prototype.slice.call(arguments, 0);
9888         if(this.jsonData){
9889             var p = property;
9890             var dsc = dir && dir.toLowerCase() == "desc";
9891             var f = function(o1, o2){
9892                 var v1 = sortType ? sortType(o1[p]) : o1[p];
9893                 var v2 = sortType ? sortType(o2[p]) : o2[p];
9894                 ;
9895                 if(v1 < v2){
9896                     return dsc ? +1 : -1;
9897                 } else if(v1 > v2){
9898                     return dsc ? -1 : +1;
9899                 } else{
9900                     return 0;
9901                 }
9902             };
9903             this.jsonData.sort(f);
9904             this.refresh();
9905             if(this.jsonData != this.snapshot){
9906                 this.snapshot.sort(f);
9907             }
9908         }
9909     }
9910 });/*
9911  * Based on:
9912  * Ext JS Library 1.1.1
9913  * Copyright(c) 2006-2007, Ext JS, LLC.
9914  *
9915  * Originally Released Under LGPL - original licence link has changed is not relivant.
9916  *
9917  * Fork - LGPL
9918  * <script type="text/javascript">
9919  */
9920  
9921
9922 /**
9923  * @class Roo.ColorPalette
9924  * @extends Roo.Component
9925  * Simple color palette class for choosing colors.  The palette can be rendered to any container.<br />
9926  * Here's an example of typical usage:
9927  * <pre><code>
9928 var cp = new Roo.ColorPalette({value:'993300'});  // initial selected color
9929 cp.render('my-div');
9930
9931 cp.on('select', function(palette, selColor){
9932     // do something with selColor
9933 });
9934 </code></pre>
9935  * @constructor
9936  * Create a new ColorPalette
9937  * @param {Object} config The config object
9938  */
9939 Roo.ColorPalette = function(config){
9940     Roo.ColorPalette.superclass.constructor.call(this, config);
9941     this.addEvents({
9942         /**
9943              * @event select
9944              * Fires when a color is selected
9945              * @param {ColorPalette} this
9946              * @param {String} color The 6-digit color hex code (without the # symbol)
9947              */
9948         select: true
9949     });
9950
9951     if(this.handler){
9952         this.on("select", this.handler, this.scope, true);
9953     }
9954 };
9955 Roo.extend(Roo.ColorPalette, Roo.Component, {
9956     /**
9957      * @cfg {String} itemCls
9958      * The CSS class to apply to the containing element (defaults to "x-color-palette")
9959      */
9960     itemCls : "x-color-palette",
9961     /**
9962      * @cfg {String} value
9963      * The initial color to highlight (should be a valid 6-digit color hex code without the # symbol).  Note that
9964      * the hex codes are case-sensitive.
9965      */
9966     value : null,
9967     clickEvent:'click',
9968     // private
9969     ctype: "Roo.ColorPalette",
9970
9971     /**
9972      * @cfg {Boolean} allowReselect If set to true then reselecting a color that is already selected fires the selection event
9973      */
9974     allowReselect : false,
9975
9976     /**
9977      * <p>An array of 6-digit color hex code strings (without the # symbol).  This array can contain any number
9978      * of colors, and each hex code should be unique.  The width of the palette is controlled via CSS by adjusting
9979      * the width property of the 'x-color-palette' class (or assigning a custom class), so you can balance the number
9980      * of colors with the width setting until the box is symmetrical.</p>
9981      * <p>You can override individual colors if needed:</p>
9982      * <pre><code>
9983 var cp = new Roo.ColorPalette();
9984 cp.colors[0] = "FF0000";  // change the first box to red
9985 </code></pre>
9986
9987 Or you can provide a custom array of your own for complete control:
9988 <pre><code>
9989 var cp = new Roo.ColorPalette();
9990 cp.colors = ["000000", "993300", "333300"];
9991 </code></pre>
9992      * @type Array
9993      */
9994     colors : [
9995         "000000", "993300", "333300", "003300", "003366", "000080", "333399", "333333",
9996         "800000", "FF6600", "808000", "008000", "008080", "0000FF", "666699", "808080",
9997         "FF0000", "FF9900", "99CC00", "339966", "33CCCC", "3366FF", "800080", "969696",
9998         "FF00FF", "FFCC00", "FFFF00", "00FF00", "00FFFF", "00CCFF", "993366", "C0C0C0",
9999         "FF99CC", "FFCC99", "FFFF99", "CCFFCC", "CCFFFF", "99CCFF", "CC99FF", "FFFFFF"
10000     ],
10001
10002     // private
10003     onRender : function(container, position){
10004         var t = new Roo.MasterTemplate(
10005             '<tpl><a href="#" class="color-{0}" hidefocus="on"><em><span style="background:#{0}" unselectable="on">&#160;</span></em></a></tpl>'
10006         );
10007         var c = this.colors;
10008         for(var i = 0, len = c.length; i < len; i++){
10009             t.add([c[i]]);
10010         }
10011         var el = document.createElement("div");
10012         el.className = this.itemCls;
10013         t.overwrite(el);
10014         container.dom.insertBefore(el, position);
10015         this.el = Roo.get(el);
10016         this.el.on(this.clickEvent, this.handleClick,  this, {delegate: "a"});
10017         if(this.clickEvent != 'click'){
10018             this.el.on('click', Roo.emptyFn,  this, {delegate: "a", preventDefault:true});
10019         }
10020     },
10021
10022     // private
10023     afterRender : function(){
10024         Roo.ColorPalette.superclass.afterRender.call(this);
10025         if(this.value){
10026             var s = this.value;
10027             this.value = null;
10028             this.select(s);
10029         }
10030     },
10031
10032     // private
10033     handleClick : function(e, t){
10034         e.preventDefault();
10035         if(!this.disabled){
10036             var c = t.className.match(/(?:^|\s)color-(.{6})(?:\s|$)/)[1];
10037             this.select(c.toUpperCase());
10038         }
10039     },
10040
10041     /**
10042      * Selects the specified color in the palette (fires the select event)
10043      * @param {String} color A valid 6-digit color hex code (# will be stripped if included)
10044      */
10045     select : function(color){
10046         color = color.replace("#", "");
10047         if(color != this.value || this.allowReselect){
10048             var el = this.el;
10049             if(this.value){
10050                 el.child("a.color-"+this.value).removeClass("x-color-palette-sel");
10051             }
10052             el.child("a.color-"+color).addClass("x-color-palette-sel");
10053             this.value = color;
10054             this.fireEvent("select", this, color);
10055         }
10056     }
10057 });/*
10058  * Based on:
10059  * Ext JS Library 1.1.1
10060  * Copyright(c) 2006-2007, Ext JS, LLC.
10061  *
10062  * Originally Released Under LGPL - original licence link has changed is not relivant.
10063  *
10064  * Fork - LGPL
10065  * <script type="text/javascript">
10066  */
10067  
10068 /**
10069  * @class Roo.DatePicker
10070  * @extends Roo.Component
10071  * Simple date picker class.
10072  * @constructor
10073  * Create a new DatePicker
10074  * @param {Object} config The config object
10075  */
10076 Roo.DatePicker = function(config){
10077     Roo.DatePicker.superclass.constructor.call(this, config);
10078
10079     this.value = config && config.value ?
10080                  config.value.clearTime() : new Date().clearTime();
10081
10082     this.addEvents({
10083         /**
10084              * @event select
10085              * Fires when a date is selected
10086              * @param {DatePicker} this
10087              * @param {Date} date The selected date
10088              */
10089         select: true
10090     });
10091
10092     if(this.handler){
10093         this.on("select", this.handler,  this.scope || this);
10094     }
10095     // build the disabledDatesRE
10096     if(!this.disabledDatesRE && this.disabledDates){
10097         var dd = this.disabledDates;
10098         var re = "(?:";
10099         for(var i = 0; i < dd.length; i++){
10100             re += dd[i];
10101             if(i != dd.length-1) re += "|";
10102         }
10103         this.disabledDatesRE = new RegExp(re + ")");
10104     }
10105 };
10106
10107 Roo.extend(Roo.DatePicker, Roo.Component, {
10108     /**
10109      * @cfg {String} todayText
10110      * The text to display on the button that selects the current date (defaults to "Today")
10111      */
10112     todayText : "Today",
10113     /**
10114      * @cfg {String} okText
10115      * The text to display on the ok button
10116      */
10117     okText : "&#160;OK&#160;", // &#160; to give the user extra clicking room
10118     /**
10119      * @cfg {String} cancelText
10120      * The text to display on the cancel button
10121      */
10122     cancelText : "Cancel",
10123     /**
10124      * @cfg {String} todayTip
10125      * The tooltip to display for the button that selects the current date (defaults to "{current date} (Spacebar)")
10126      */
10127     todayTip : "{0} (Spacebar)",
10128     /**
10129      * @cfg {Date} minDate
10130      * Minimum allowable date (JavaScript date object, defaults to null)
10131      */
10132     minDate : null,
10133     /**
10134      * @cfg {Date} maxDate
10135      * Maximum allowable date (JavaScript date object, defaults to null)
10136      */
10137     maxDate : null,
10138     /**
10139      * @cfg {String} minText
10140      * The error text to display if the minDate validation fails (defaults to "This date is before the minimum date")
10141      */
10142     minText : "This date is before the minimum date",
10143     /**
10144      * @cfg {String} maxText
10145      * The error text to display if the maxDate validation fails (defaults to "This date is after the maximum date")
10146      */
10147     maxText : "This date is after the maximum date",
10148     /**
10149      * @cfg {String} format
10150      * The default date format string which can be overriden for localization support.  The format must be
10151      * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
10152      */
10153     format : "m/d/y",
10154     /**
10155      * @cfg {Array} disabledDays
10156      * An array of days to disable, 0-based. For example, [0, 6] disables Sunday and Saturday (defaults to null).
10157      */
10158     disabledDays : null,
10159     /**
10160      * @cfg {String} disabledDaysText
10161      * The tooltip to display when the date falls on a disabled day (defaults to "")
10162      */
10163     disabledDaysText : "",
10164     /**
10165      * @cfg {RegExp} disabledDatesRE
10166      * JavaScript regular expression used to disable a pattern of dates (defaults to null)
10167      */
10168     disabledDatesRE : null,
10169     /**
10170      * @cfg {String} disabledDatesText
10171      * The tooltip text to display when the date falls on a disabled date (defaults to "")
10172      */
10173     disabledDatesText : "",
10174     /**
10175      * @cfg {Boolean} constrainToViewport
10176      * True to constrain the date picker to the viewport (defaults to true)
10177      */
10178     constrainToViewport : true,
10179     /**
10180      * @cfg {Array} monthNames
10181      * An array of textual month names which can be overriden for localization support (defaults to Date.monthNames)
10182      */
10183     monthNames : Date.monthNames,
10184     /**
10185      * @cfg {Array} dayNames
10186      * An array of textual day names which can be overriden for localization support (defaults to Date.dayNames)
10187      */
10188     dayNames : Date.dayNames,
10189     /**
10190      * @cfg {String} nextText
10191      * The next month navigation button tooltip (defaults to 'Next Month (Control+Right)')
10192      */
10193     nextText: 'Next Month (Control+Right)',
10194     /**
10195      * @cfg {String} prevText
10196      * The previous month navigation button tooltip (defaults to 'Previous Month (Control+Left)')
10197      */
10198     prevText: 'Previous Month (Control+Left)',
10199     /**
10200      * @cfg {String} monthYearText
10201      * The header month selector tooltip (defaults to 'Choose a month (Control+Up/Down to move years)')
10202      */
10203     monthYearText: 'Choose a month (Control+Up/Down to move years)',
10204     /**
10205      * @cfg {Number} startDay
10206      * Day index at which the week should begin, 0-based (defaults to 0, which is Sunday)
10207      */
10208     startDay : 0,
10209     /**
10210      * @cfg {Bool} showClear
10211      * Show a clear button (usefull for date form elements that can be blank.)
10212      */
10213     
10214     showClear: false,
10215     
10216     /**
10217      * Sets the value of the date field
10218      * @param {Date} value The date to set
10219      */
10220     setValue : function(value){
10221         var old = this.value;
10222         this.value = value.clearTime(true);
10223         if(this.el){
10224             this.update(this.value);
10225         }
10226     },
10227
10228     /**
10229      * Gets the current selected value of the date field
10230      * @return {Date} The selected date
10231      */
10232     getValue : function(){
10233         return this.value;
10234     },
10235
10236     // private
10237     focus : function(){
10238         if(this.el){
10239             this.update(this.activeDate);
10240         }
10241     },
10242
10243     // private
10244     onRender : function(container, position){
10245         var m = [
10246              '<table cellspacing="0">',
10247                 '<tr><td class="x-date-left"><a href="#" title="', this.prevText ,'">&#160;</a></td><td class="x-date-middle" align="center"></td><td class="x-date-right"><a href="#" title="', this.nextText ,'">&#160;</a></td></tr>',
10248                 '<tr><td colspan="3"><table class="x-date-inner" cellspacing="0"><thead><tr>'];
10249         var dn = this.dayNames;
10250         for(var i = 0; i < 7; i++){
10251             var d = this.startDay+i;
10252             if(d > 6){
10253                 d = d-7;
10254             }
10255             m.push("<th><span>", dn[d].substr(0,1), "</span></th>");
10256         }
10257         m[m.length] = "</tr></thead><tbody><tr>";
10258         for(var i = 0; i < 42; i++) {
10259             if(i % 7 == 0 && i != 0){
10260                 m[m.length] = "</tr><tr>";
10261             }
10262             m[m.length] = '<td><a href="#" hidefocus="on" class="x-date-date" tabIndex="1"><em><span></span></em></a></td>';
10263         }
10264         m[m.length] = '</tr></tbody></table></td></tr><tr>'+
10265             '<td colspan="3" class="x-date-bottom" align="center"></td></tr></table><div class="x-date-mp"></div>';
10266
10267         var el = document.createElement("div");
10268         el.className = "x-date-picker";
10269         el.innerHTML = m.join("");
10270
10271         container.dom.insertBefore(el, position);
10272
10273         this.el = Roo.get(el);
10274         this.eventEl = Roo.get(el.firstChild);
10275
10276         new Roo.util.ClickRepeater(this.el.child("td.x-date-left a"), {
10277             handler: this.showPrevMonth,
10278             scope: this,
10279             preventDefault:true,
10280             stopDefault:true
10281         });
10282
10283         new Roo.util.ClickRepeater(this.el.child("td.x-date-right a"), {
10284             handler: this.showNextMonth,
10285             scope: this,
10286             preventDefault:true,
10287             stopDefault:true
10288         });
10289
10290         this.eventEl.on("mousewheel", this.handleMouseWheel,  this);
10291
10292         this.monthPicker = this.el.down('div.x-date-mp');
10293         this.monthPicker.enableDisplayMode('block');
10294         
10295         var kn = new Roo.KeyNav(this.eventEl, {
10296             "left" : function(e){
10297                 e.ctrlKey ?
10298                     this.showPrevMonth() :
10299                     this.update(this.activeDate.add("d", -1));
10300             },
10301
10302             "right" : function(e){
10303                 e.ctrlKey ?
10304                     this.showNextMonth() :
10305                     this.update(this.activeDate.add("d", 1));
10306             },
10307
10308             "up" : function(e){
10309                 e.ctrlKey ?
10310                     this.showNextYear() :
10311                     this.update(this.activeDate.add("d", -7));
10312             },
10313
10314             "down" : function(e){
10315                 e.ctrlKey ?
10316                     this.showPrevYear() :
10317                     this.update(this.activeDate.add("d", 7));
10318             },
10319
10320             "pageUp" : function(e){
10321                 this.showNextMonth();
10322             },
10323
10324             "pageDown" : function(e){
10325                 this.showPrevMonth();
10326             },
10327
10328             "enter" : function(e){
10329                 e.stopPropagation();
10330                 return true;
10331             },
10332
10333             scope : this
10334         });
10335
10336         this.eventEl.on("click", this.handleDateClick,  this, {delegate: "a.x-date-date"});
10337
10338         this.eventEl.addKeyListener(Roo.EventObject.SPACE, this.selectToday,  this);
10339
10340         this.el.unselectable();
10341         
10342         this.cells = this.el.select("table.x-date-inner tbody td");
10343         this.textNodes = this.el.query("table.x-date-inner tbody span");
10344
10345         this.mbtn = new Roo.Button(this.el.child("td.x-date-middle", true), {
10346             text: "&#160;",
10347             tooltip: this.monthYearText
10348         });
10349
10350         this.mbtn.on('click', this.showMonthPicker, this);
10351         this.mbtn.el.child(this.mbtn.menuClassTarget).addClass("x-btn-with-menu");
10352
10353
10354         var today = (new Date()).dateFormat(this.format);
10355         
10356         var baseTb = new Roo.Toolbar(this.el.child("td.x-date-bottom", true));
10357         if (this.showClear) {
10358             baseTb.add( new Roo.Toolbar.Fill());
10359         }
10360         baseTb.add({
10361             text: String.format(this.todayText, today),
10362             tooltip: String.format(this.todayTip, today),
10363             handler: this.selectToday,
10364             scope: this
10365         });
10366         
10367         //var todayBtn = new Roo.Button(this.el.child("td.x-date-bottom", true), {
10368             
10369         //});
10370         if (this.showClear) {
10371             
10372             baseTb.add( new Roo.Toolbar.Fill());
10373             baseTb.add({
10374                 text: '&#160;',
10375                 cls: 'x-btn-icon x-btn-clear',
10376                 handler: function() {
10377                     //this.value = '';
10378                     this.fireEvent("select", this, '');
10379                 },
10380                 scope: this
10381             });
10382         }
10383         
10384         
10385         if(Roo.isIE){
10386             this.el.repaint();
10387         }
10388         this.update(this.value);
10389     },
10390
10391     createMonthPicker : function(){
10392         if(!this.monthPicker.dom.firstChild){
10393             var buf = ['<table border="0" cellspacing="0">'];
10394             for(var i = 0; i < 6; i++){
10395                 buf.push(
10396                     '<tr><td class="x-date-mp-month"><a href="#">', this.monthNames[i].substr(0, 3), '</a></td>',
10397                     '<td class="x-date-mp-month x-date-mp-sep"><a href="#">', this.monthNames[i+6].substr(0, 3), '</a></td>',
10398                     i == 0 ?
10399                     '<td class="x-date-mp-ybtn" align="center"><a class="x-date-mp-prev"></a></td><td class="x-date-mp-ybtn" align="center"><a class="x-date-mp-next"></a></td></tr>' :
10400                     '<td class="x-date-mp-year"><a href="#"></a></td><td class="x-date-mp-year"><a href="#"></a></td></tr>'
10401                 );
10402             }
10403             buf.push(
10404                 '<tr class="x-date-mp-btns"><td colspan="4"><button type="button" class="x-date-mp-ok">',
10405                     this.okText,
10406                     '</button><button type="button" class="x-date-mp-cancel">',
10407                     this.cancelText,
10408                     '</button></td></tr>',
10409                 '</table>'
10410             );
10411             this.monthPicker.update(buf.join(''));
10412             this.monthPicker.on('click', this.onMonthClick, this);
10413             this.monthPicker.on('dblclick', this.onMonthDblClick, this);
10414
10415             this.mpMonths = this.monthPicker.select('td.x-date-mp-month');
10416             this.mpYears = this.monthPicker.select('td.x-date-mp-year');
10417
10418             this.mpMonths.each(function(m, a, i){
10419                 i += 1;
10420                 if((i%2) == 0){
10421                     m.dom.xmonth = 5 + Math.round(i * .5);
10422                 }else{
10423                     m.dom.xmonth = Math.round((i-1) * .5);
10424                 }
10425             });
10426         }
10427     },
10428
10429     showMonthPicker : function(){
10430         this.createMonthPicker();
10431         var size = this.el.getSize();
10432         this.monthPicker.setSize(size);
10433         this.monthPicker.child('table').setSize(size);
10434
10435         this.mpSelMonth = (this.activeDate || this.value).getMonth();
10436         this.updateMPMonth(this.mpSelMonth);
10437         this.mpSelYear = (this.activeDate || this.value).getFullYear();
10438         this.updateMPYear(this.mpSelYear);
10439
10440         this.monthPicker.slideIn('t', {duration:.2});
10441     },
10442
10443     updateMPYear : function(y){
10444         this.mpyear = y;
10445         var ys = this.mpYears.elements;
10446         for(var i = 1; i <= 10; i++){
10447             var td = ys[i-1], y2;
10448             if((i%2) == 0){
10449                 y2 = y + Math.round(i * .5);
10450                 td.firstChild.innerHTML = y2;
10451                 td.xyear = y2;
10452             }else{
10453                 y2 = y - (5-Math.round(i * .5));
10454                 td.firstChild.innerHTML = y2;
10455                 td.xyear = y2;
10456             }
10457             this.mpYears.item(i-1)[y2 == this.mpSelYear ? 'addClass' : 'removeClass']('x-date-mp-sel');
10458         }
10459     },
10460
10461     updateMPMonth : function(sm){
10462         this.mpMonths.each(function(m, a, i){
10463             m[m.dom.xmonth == sm ? 'addClass' : 'removeClass']('x-date-mp-sel');
10464         });
10465     },
10466
10467     selectMPMonth: function(m){
10468         
10469     },
10470
10471     onMonthClick : function(e, t){
10472         e.stopEvent();
10473         var el = new Roo.Element(t), pn;
10474         if(el.is('button.x-date-mp-cancel')){
10475             this.hideMonthPicker();
10476         }
10477         else if(el.is('button.x-date-mp-ok')){
10478             this.update(new Date(this.mpSelYear, this.mpSelMonth, (this.activeDate || this.value).getDate()));
10479             this.hideMonthPicker();
10480         }
10481         else if(pn = el.up('td.x-date-mp-month', 2)){
10482             this.mpMonths.removeClass('x-date-mp-sel');
10483             pn.addClass('x-date-mp-sel');
10484             this.mpSelMonth = pn.dom.xmonth;
10485         }
10486         else if(pn = el.up('td.x-date-mp-year', 2)){
10487             this.mpYears.removeClass('x-date-mp-sel');
10488             pn.addClass('x-date-mp-sel');
10489             this.mpSelYear = pn.dom.xyear;
10490         }
10491         else if(el.is('a.x-date-mp-prev')){
10492             this.updateMPYear(this.mpyear-10);
10493         }
10494         else if(el.is('a.x-date-mp-next')){
10495             this.updateMPYear(this.mpyear+10);
10496         }
10497     },
10498
10499     onMonthDblClick : function(e, t){
10500         e.stopEvent();
10501         var el = new Roo.Element(t), pn;
10502         if(pn = el.up('td.x-date-mp-month', 2)){
10503             this.update(new Date(this.mpSelYear, pn.dom.xmonth, (this.activeDate || this.value).getDate()));
10504             this.hideMonthPicker();
10505         }
10506         else if(pn = el.up('td.x-date-mp-year', 2)){
10507             this.update(new Date(pn.dom.xyear, this.mpSelMonth, (this.activeDate || this.value).getDate()));
10508             this.hideMonthPicker();
10509         }
10510     },
10511
10512     hideMonthPicker : function(disableAnim){
10513         if(this.monthPicker){
10514             if(disableAnim === true){
10515                 this.monthPicker.hide();
10516             }else{
10517                 this.monthPicker.slideOut('t', {duration:.2});
10518             }
10519         }
10520     },
10521
10522     // private
10523     showPrevMonth : function(e){
10524         this.update(this.activeDate.add("mo", -1));
10525     },
10526
10527     // private
10528     showNextMonth : function(e){
10529         this.update(this.activeDate.add("mo", 1));
10530     },
10531
10532     // private
10533     showPrevYear : function(){
10534         this.update(this.activeDate.add("y", -1));
10535     },
10536
10537     // private
10538     showNextYear : function(){
10539         this.update(this.activeDate.add("y", 1));
10540     },
10541
10542     // private
10543     handleMouseWheel : function(e){
10544         var delta = e.getWheelDelta();
10545         if(delta > 0){
10546             this.showPrevMonth();
10547             e.stopEvent();
10548         } else if(delta < 0){
10549             this.showNextMonth();
10550             e.stopEvent();
10551         }
10552     },
10553
10554     // private
10555     handleDateClick : function(e, t){
10556         e.stopEvent();
10557         if(t.dateValue && !Roo.fly(t.parentNode).hasClass("x-date-disabled")){
10558             this.setValue(new Date(t.dateValue));
10559             this.fireEvent("select", this, this.value);
10560         }
10561     },
10562
10563     // private
10564     selectToday : function(){
10565         this.setValue(new Date().clearTime());
10566         this.fireEvent("select", this, this.value);
10567     },
10568
10569     // private
10570     update : function(date){
10571         var vd = this.activeDate;
10572         this.activeDate = date;
10573         if(vd && this.el){
10574             var t = date.getTime();
10575             if(vd.getMonth() == date.getMonth() && vd.getFullYear() == date.getFullYear()){
10576                 this.cells.removeClass("x-date-selected");
10577                 this.cells.each(function(c){
10578                    if(c.dom.firstChild.dateValue == t){
10579                        c.addClass("x-date-selected");
10580                        setTimeout(function(){
10581                             try{c.dom.firstChild.focus();}catch(e){}
10582                        }, 50);
10583                        return false;
10584                    }
10585                 });
10586                 return;
10587             }
10588         }
10589         var days = date.getDaysInMonth();
10590         var firstOfMonth = date.getFirstDateOfMonth();
10591         var startingPos = firstOfMonth.getDay()-this.startDay;
10592
10593         if(startingPos <= this.startDay){
10594             startingPos += 7;
10595         }
10596
10597         var pm = date.add("mo", -1);
10598         var prevStart = pm.getDaysInMonth()-startingPos;
10599
10600         var cells = this.cells.elements;
10601         var textEls = this.textNodes;
10602         days += startingPos;
10603
10604         // convert everything to numbers so it's fast
10605         var day = 86400000;
10606         var d = (new Date(pm.getFullYear(), pm.getMonth(), prevStart)).clearTime();
10607         var today = new Date().clearTime().getTime();
10608         var sel = date.clearTime().getTime();
10609         var min = this.minDate ? this.minDate.clearTime() : Number.NEGATIVE_INFINITY;
10610         var max = this.maxDate ? this.maxDate.clearTime() : Number.POSITIVE_INFINITY;
10611         var ddMatch = this.disabledDatesRE;
10612         var ddText = this.disabledDatesText;
10613         var ddays = this.disabledDays ? this.disabledDays.join("") : false;
10614         var ddaysText = this.disabledDaysText;
10615         var format = this.format;
10616
10617         var setCellClass = function(cal, cell){
10618             cell.title = "";
10619             var t = d.getTime();
10620             cell.firstChild.dateValue = t;
10621             if(t == today){
10622                 cell.className += " x-date-today";
10623                 cell.title = cal.todayText;
10624             }
10625             if(t == sel){
10626                 cell.className += " x-date-selected";
10627                 setTimeout(function(){
10628                     try{cell.firstChild.focus();}catch(e){}
10629                 }, 50);
10630             }
10631             // disabling
10632             if(t < min) {
10633                 cell.className = " x-date-disabled";
10634                 cell.title = cal.minText;
10635                 return;
10636             }
10637             if(t > max) {
10638                 cell.className = " x-date-disabled";
10639                 cell.title = cal.maxText;
10640                 return;
10641             }
10642             if(ddays){
10643                 if(ddays.indexOf(d.getDay()) != -1){
10644                     cell.title = ddaysText;
10645                     cell.className = " x-date-disabled";
10646                 }
10647             }
10648             if(ddMatch && format){
10649                 var fvalue = d.dateFormat(format);
10650                 if(ddMatch.test(fvalue)){
10651                     cell.title = ddText.replace("%0", fvalue);
10652                     cell.className = " x-date-disabled";
10653                 }
10654             }
10655         };
10656
10657         var i = 0;
10658         for(; i < startingPos; i++) {
10659             textEls[i].innerHTML = (++prevStart);
10660             d.setDate(d.getDate()+1);
10661             cells[i].className = "x-date-prevday";
10662             setCellClass(this, cells[i]);
10663         }
10664         for(; i < days; i++){
10665             intDay = i - startingPos + 1;
10666             textEls[i].innerHTML = (intDay);
10667             d.setDate(d.getDate()+1);
10668             cells[i].className = "x-date-active";
10669             setCellClass(this, cells[i]);
10670         }
10671         var extraDays = 0;
10672         for(; i < 42; i++) {
10673              textEls[i].innerHTML = (++extraDays);
10674              d.setDate(d.getDate()+1);
10675              cells[i].className = "x-date-nextday";
10676              setCellClass(this, cells[i]);
10677         }
10678
10679         this.mbtn.setText(this.monthNames[date.getMonth()] + " " + date.getFullYear());
10680
10681         if(!this.internalRender){
10682             var main = this.el.dom.firstChild;
10683             var w = main.offsetWidth;
10684             this.el.setWidth(w + this.el.getBorderWidth("lr"));
10685             Roo.fly(main).setWidth(w);
10686             this.internalRender = true;
10687             // opera does not respect the auto grow header center column
10688             // then, after it gets a width opera refuses to recalculate
10689             // without a second pass
10690             if(Roo.isOpera && !this.secondPass){
10691                 main.rows[0].cells[1].style.width = (w - (main.rows[0].cells[0].offsetWidth+main.rows[0].cells[2].offsetWidth)) + "px";
10692                 this.secondPass = true;
10693                 this.update.defer(10, this, [date]);
10694             }
10695         }
10696     }
10697 });        /*
10698  * Based on:
10699  * Ext JS Library 1.1.1
10700  * Copyright(c) 2006-2007, Ext JS, LLC.
10701  *
10702  * Originally Released Under LGPL - original licence link has changed is not relivant.
10703  *
10704  * Fork - LGPL
10705  * <script type="text/javascript">
10706  */
10707 /**
10708  * @class Roo.TabPanel
10709  * @extends Roo.util.Observable
10710  * A lightweight tab container.
10711  * <br><br>
10712  * Usage:
10713  * <pre><code>
10714 // basic tabs 1, built from existing content
10715 var tabs = new Roo.TabPanel("tabs1");
10716 tabs.addTab("script", "View Script");
10717 tabs.addTab("markup", "View Markup");
10718 tabs.activate("script");
10719
10720 // more advanced tabs, built from javascript
10721 var jtabs = new Roo.TabPanel("jtabs");
10722 jtabs.addTab("jtabs-1", "Normal Tab", "My content was added during construction.");
10723
10724 // set up the UpdateManager
10725 var tab2 = jtabs.addTab("jtabs-2", "Ajax Tab 1");
10726 var updater = tab2.getUpdateManager();
10727 updater.setDefaultUrl("ajax1.htm");
10728 tab2.on('activate', updater.refresh, updater, true);
10729
10730 // Use setUrl for Ajax loading
10731 var tab3 = jtabs.addTab("jtabs-3", "Ajax Tab 2");
10732 tab3.setUrl("ajax2.htm", null, true);
10733
10734 // Disabled tab
10735 var tab4 = jtabs.addTab("tabs1-5", "Disabled Tab", "Can't see me cause I'm disabled");
10736 tab4.disable();
10737
10738 jtabs.activate("jtabs-1");
10739  * </code></pre>
10740  * @constructor
10741  * Create a new TabPanel.
10742  * @param {String/HTMLElement/Roo.Element} container The id, DOM element or Roo.Element container where this TabPanel is to be rendered.
10743  * @param {Object/Boolean} config Config object to set any properties for this TabPanel, or true to render the tabs on the bottom.
10744  */
10745 Roo.TabPanel = function(container, config){
10746     /**
10747     * The container element for this TabPanel.
10748     * @type Roo.Element
10749     */
10750     this.el = Roo.get(container, true);
10751     if(config){
10752         if(typeof config == "boolean"){
10753             this.tabPosition = config ? "bottom" : "top";
10754         }else{
10755             Roo.apply(this, config);
10756         }
10757     }
10758     if(this.tabPosition == "bottom"){
10759         this.bodyEl = Roo.get(this.createBody(this.el.dom));
10760         this.el.addClass("x-tabs-bottom");
10761     }
10762     this.stripWrap = Roo.get(this.createStrip(this.el.dom), true);
10763     this.stripEl = Roo.get(this.createStripList(this.stripWrap.dom), true);
10764     this.stripBody = Roo.get(this.stripWrap.dom.firstChild.firstChild, true);
10765     if(Roo.isIE){
10766         Roo.fly(this.stripWrap.dom.firstChild).setStyle("overflow-x", "hidden");
10767     }
10768     if(this.tabPosition != "bottom"){
10769         /** The body element that contains {@link Roo.TabPanelItem} bodies. +
10770          * @type Roo.Element
10771          */
10772         this.bodyEl = Roo.get(this.createBody(this.el.dom));
10773         this.el.addClass("x-tabs-top");
10774     }
10775     this.items = [];
10776
10777     this.bodyEl.setStyle("position", "relative");
10778
10779     this.active = null;
10780     this.activateDelegate = this.activate.createDelegate(this);
10781
10782     this.addEvents({
10783         /**
10784          * @event tabchange
10785          * Fires when the active tab changes
10786          * @param {Roo.TabPanel} this
10787          * @param {Roo.TabPanelItem} activePanel The new active tab
10788          */
10789         "tabchange": true,
10790         /**
10791          * @event beforetabchange
10792          * Fires before the active tab changes, set cancel to true on the "e" parameter to cancel the change
10793          * @param {Roo.TabPanel} this
10794          * @param {Object} e Set cancel to true on this object to cancel the tab change
10795          * @param {Roo.TabPanelItem} tab The tab being changed to
10796          */
10797         "beforetabchange" : true
10798     });
10799
10800     Roo.EventManager.onWindowResize(this.onResize, this);
10801     this.cpad = this.el.getPadding("lr");
10802     this.hiddenCount = 0;
10803
10804
10805     // toolbar on the tabbar support...
10806     if (this.toolbar) {
10807         var tcfg = this.toolbar;
10808         tcfg.container = this.stripEl.child('td.x-tab-strip-toolbar');  
10809         this.toolbar = new Roo.Toolbar(tcfg);
10810         if (Roo.isSafari) {
10811             var tbl = tcfg.container.child('table', true);
10812             tbl.setAttribute('width', '100%');
10813         }
10814         
10815     }
10816    
10817
10818
10819     Roo.TabPanel.superclass.constructor.call(this);
10820 };
10821
10822 Roo.extend(Roo.TabPanel, Roo.util.Observable, {
10823     /*
10824      *@cfg {String} tabPosition "top" or "bottom" (defaults to "top")
10825      */
10826     tabPosition : "top",
10827     /*
10828      *@cfg {Number} currentTabWidth The width of the current tab (defaults to 0)
10829      */
10830     currentTabWidth : 0,
10831     /*
10832      *@cfg {Number} minTabWidth The minimum width of a tab (defaults to 40) (ignored if {@link #resizeTabs} is not true)
10833      */
10834     minTabWidth : 40,
10835     /*
10836      *@cfg {Number} maxTabWidth The maximum width of a tab (defaults to 250) (ignored if {@link #resizeTabs} is not true)
10837      */
10838     maxTabWidth : 250,
10839     /*
10840      *@cfg {Number} preferredTabWidth The preferred (default) width of a tab (defaults to 175) (ignored if {@link #resizeTabs} is not true)
10841      */
10842     preferredTabWidth : 175,
10843     /*
10844      *@cfg {Boolean} resizeTabs True to enable dynamic tab resizing (defaults to false)
10845      */
10846     resizeTabs : false,
10847     /*
10848      *@cfg {Boolean} monitorResize Set this to true to turn on window resize monitoring (ignored if {@link #resizeTabs} is not true) (defaults to true)
10849      */
10850     monitorResize : true,
10851     /*
10852      *@cfg {Object} toolbar xtype description of toolbar to show at the right of the tab bar. 
10853      */
10854     toolbar : false,
10855
10856     /**
10857      * Creates a new {@link Roo.TabPanelItem} by looking for an existing element with the provided id -- if it's not found it creates one.
10858      * @param {String} id The id of the div to use <b>or create</b>
10859      * @param {String} text The text for the tab
10860      * @param {String} content (optional) Content to put in the TabPanelItem body
10861      * @param {Boolean} closable (optional) True to create a close icon on the tab
10862      * @return {Roo.TabPanelItem} The created TabPanelItem
10863      */
10864     addTab : function(id, text, content, closable){
10865         var item = new Roo.TabPanelItem(this, id, text, closable);
10866         this.addTabItem(item);
10867         if(content){
10868             item.setContent(content);
10869         }
10870         return item;
10871     },
10872
10873     /**
10874      * Returns the {@link Roo.TabPanelItem} with the specified id/index
10875      * @param {String/Number} id The id or index of the TabPanelItem to fetch.
10876      * @return {Roo.TabPanelItem}
10877      */
10878     getTab : function(id){
10879         return this.items[id];
10880     },
10881
10882     /**
10883      * Hides the {@link Roo.TabPanelItem} with the specified id/index
10884      * @param {String/Number} id The id or index of the TabPanelItem to hide.
10885      */
10886     hideTab : function(id){
10887         var t = this.items[id];
10888         if(!t.isHidden()){
10889            t.setHidden(true);
10890            this.hiddenCount++;
10891            this.autoSizeTabs();
10892         }
10893     },
10894
10895     /**
10896      * "Unhides" the {@link Roo.TabPanelItem} with the specified id/index.
10897      * @param {String/Number} id The id or index of the TabPanelItem to unhide.
10898      */
10899     unhideTab : function(id){
10900         var t = this.items[id];
10901         if(t.isHidden()){
10902            t.setHidden(false);
10903            this.hiddenCount--;
10904            this.autoSizeTabs();
10905         }
10906     },
10907
10908     /**
10909      * Adds an existing {@link Roo.TabPanelItem}.
10910      * @param {Roo.TabPanelItem} item The TabPanelItem to add
10911      */
10912     addTabItem : function(item){
10913         this.items[item.id] = item;
10914         this.items.push(item);
10915         if(this.resizeTabs){
10916            item.setWidth(this.currentTabWidth || this.preferredTabWidth);
10917            this.autoSizeTabs();
10918         }else{
10919             item.autoSize();
10920         }
10921     },
10922
10923     /**
10924      * Removes a {@link Roo.TabPanelItem}.
10925      * @param {String/Number} id The id or index of the TabPanelItem to remove.
10926      */
10927     removeTab : function(id){
10928         var items = this.items;
10929         var tab = items[id];
10930         if(!tab) { return; }
10931         var index = items.indexOf(tab);
10932         if(this.active == tab && items.length > 1){
10933             var newTab = this.getNextAvailable(index);
10934             if(newTab) {
10935                 newTab.activate();
10936             }
10937         }
10938         this.stripEl.dom.removeChild(tab.pnode.dom);
10939         if(tab.bodyEl.dom.parentNode == this.bodyEl.dom){ // if it was moved already prevent error
10940             this.bodyEl.dom.removeChild(tab.bodyEl.dom);
10941         }
10942         items.splice(index, 1);
10943         delete this.items[tab.id];
10944         tab.fireEvent("close", tab);
10945         tab.purgeListeners();
10946         this.autoSizeTabs();
10947     },
10948
10949     getNextAvailable : function(start){
10950         var items = this.items;
10951         var index = start;
10952         // look for a next tab that will slide over to
10953         // replace the one being removed
10954         while(index < items.length){
10955             var item = items[++index];
10956             if(item && !item.isHidden()){
10957                 return item;
10958             }
10959         }
10960         // if one isn't found select the previous tab (on the left)
10961         index = start;
10962         while(index >= 0){
10963             var item = items[--index];
10964             if(item && !item.isHidden()){
10965                 return item;
10966             }
10967         }
10968         return null;
10969     },
10970
10971     /**
10972      * Disables a {@link Roo.TabPanelItem}. It cannot be the active tab, if it is this call is ignored.
10973      * @param {String/Number} id The id or index of the TabPanelItem to disable.
10974      */
10975     disableTab : function(id){
10976         var tab = this.items[id];
10977         if(tab && this.active != tab){
10978             tab.disable();
10979         }
10980     },
10981
10982     /**
10983      * Enables a {@link Roo.TabPanelItem} that is disabled.
10984      * @param {String/Number} id The id or index of the TabPanelItem to enable.
10985      */
10986     enableTab : function(id){
10987         var tab = this.items[id];
10988         tab.enable();
10989     },
10990
10991     /**
10992      * Activates a {@link Roo.TabPanelItem}. The currently active one will be deactivated.
10993      * @param {String/Number} id The id or index of the TabPanelItem to activate.
10994      * @return {Roo.TabPanelItem} The TabPanelItem.
10995      */
10996     activate : function(id){
10997         var tab = this.items[id];
10998         if(!tab){
10999             return null;
11000         }
11001         if(tab == this.active || tab.disabled){
11002             return tab;
11003         }
11004         var e = {};
11005         this.fireEvent("beforetabchange", this, e, tab);
11006         if(e.cancel !== true && !tab.disabled){
11007             if(this.active){
11008                 this.active.hide();
11009             }
11010             this.active = this.items[id];
11011             this.active.show();
11012             this.fireEvent("tabchange", this, this.active);
11013         }
11014         return tab;
11015     },
11016
11017     /**
11018      * Gets the active {@link Roo.TabPanelItem}.
11019      * @return {Roo.TabPanelItem} The active TabPanelItem or null if none are active.
11020      */
11021     getActiveTab : function(){
11022         return this.active;
11023     },
11024
11025     /**
11026      * Updates the tab body element to fit the height of the container element
11027      * for overflow scrolling
11028      * @param {Number} targetHeight (optional) Override the starting height from the elements height
11029      */
11030     syncHeight : function(targetHeight){
11031         var height = (targetHeight || this.el.getHeight())-this.el.getBorderWidth("tb")-this.el.getPadding("tb");
11032         var bm = this.bodyEl.getMargins();
11033         var newHeight = height-(this.stripWrap.getHeight()||0)-(bm.top+bm.bottom);
11034         this.bodyEl.setHeight(newHeight);
11035         return newHeight;
11036     },
11037
11038     onResize : function(){
11039         if(this.monitorResize){
11040             this.autoSizeTabs();
11041         }
11042     },
11043
11044     /**
11045      * Disables tab resizing while tabs are being added (if {@link #resizeTabs} is false this does nothing)
11046      */
11047     beginUpdate : function(){
11048         this.updating = true;
11049     },
11050
11051     /**
11052      * Stops an update and resizes the tabs (if {@link #resizeTabs} is false this does nothing)
11053      */
11054     endUpdate : function(){
11055         this.updating = false;
11056         this.autoSizeTabs();
11057     },
11058
11059     /**
11060      * Manual call to resize the tabs (if {@link #resizeTabs} is false this does nothing)
11061      */
11062     autoSizeTabs : function(){
11063         var count = this.items.length;
11064         var vcount = count - this.hiddenCount;
11065         if(!this.resizeTabs || count < 1 || vcount < 1 || this.updating) return;
11066         var w = Math.max(this.el.getWidth() - this.cpad, 10);
11067         var availWidth = Math.floor(w / vcount);
11068         var b = this.stripBody;
11069         if(b.getWidth() > w){
11070             var tabs = this.items;
11071             this.setTabWidth(Math.max(availWidth, this.minTabWidth)-2);
11072             if(availWidth < this.minTabWidth){
11073                 /*if(!this.sleft){    // incomplete scrolling code
11074                     this.createScrollButtons();
11075                 }
11076                 this.showScroll();
11077                 this.stripClip.setWidth(w - (this.sleft.getWidth()+this.sright.getWidth()));*/
11078             }
11079         }else{
11080             if(this.currentTabWidth < this.preferredTabWidth){
11081                 this.setTabWidth(Math.min(availWidth, this.preferredTabWidth)-2);
11082             }
11083         }
11084     },
11085
11086     /**
11087      * Returns the number of tabs in this TabPanel.
11088      * @return {Number}
11089      */
11090      getCount : function(){
11091          return this.items.length;
11092      },
11093
11094     /**
11095      * Resizes all the tabs to the passed width
11096      * @param {Number} The new width
11097      */
11098     setTabWidth : function(width){
11099         this.currentTabWidth = width;
11100         for(var i = 0, len = this.items.length; i < len; i++) {
11101                 if(!this.items[i].isHidden())this.items[i].setWidth(width);
11102         }
11103     },
11104
11105     /**
11106      * Destroys this TabPanel
11107      * @param {Boolean} removeEl (optional) True to remove the element from the DOM as well (defaults to undefined)
11108      */
11109     destroy : function(removeEl){
11110         Roo.EventManager.removeResizeListener(this.onResize, this);
11111         for(var i = 0, len = this.items.length; i < len; i++){
11112             this.items[i].purgeListeners();
11113         }
11114         if(removeEl === true){
11115             this.el.update("");
11116             this.el.remove();
11117         }
11118     }
11119 });
11120
11121 /**
11122  * @class Roo.TabPanelItem
11123  * @extends Roo.util.Observable
11124  * Represents an individual item (tab plus body) in a TabPanel.
11125  * @param {Roo.TabPanel} tabPanel The {@link Roo.TabPanel} this TabPanelItem belongs to
11126  * @param {String} id The id of this TabPanelItem
11127  * @param {String} text The text for the tab of this TabPanelItem
11128  * @param {Boolean} closable True to allow this TabPanelItem to be closable (defaults to false)
11129  */
11130 Roo.TabPanelItem = function(tabPanel, id, text, closable){
11131     /**
11132      * The {@link Roo.TabPanel} this TabPanelItem belongs to
11133      * @type Roo.TabPanel
11134      */
11135     this.tabPanel = tabPanel;
11136     /**
11137      * The id for this TabPanelItem
11138      * @type String
11139      */
11140     this.id = id;
11141     /** @private */
11142     this.disabled = false;
11143     /** @private */
11144     this.text = text;
11145     /** @private */
11146     this.loaded = false;
11147     this.closable = closable;
11148
11149     /**
11150      * The body element for this TabPanelItem.
11151      * @type Roo.Element
11152      */
11153     this.bodyEl = Roo.get(tabPanel.createItemBody(tabPanel.bodyEl.dom, id));
11154     this.bodyEl.setVisibilityMode(Roo.Element.VISIBILITY);
11155     this.bodyEl.setStyle("display", "block");
11156     this.bodyEl.setStyle("zoom", "1");
11157     this.hideAction();
11158
11159     var els = tabPanel.createStripElements(tabPanel.stripEl.dom, text, closable);
11160     /** @private */
11161     this.el = Roo.get(els.el, true);
11162     this.inner = Roo.get(els.inner, true);
11163     this.textEl = Roo.get(this.el.dom.firstChild.firstChild.firstChild, true);
11164     this.pnode = Roo.get(els.el.parentNode, true);
11165     this.el.on("mousedown", this.onTabMouseDown, this);
11166     this.el.on("click", this.onTabClick, this);
11167     /** @private */
11168     if(closable){
11169         var c = Roo.get(els.close, true);
11170         c.dom.title = this.closeText;
11171         c.addClassOnOver("close-over");
11172         c.on("click", this.closeClick, this);
11173      }
11174
11175     this.addEvents({
11176          /**
11177          * @event activate
11178          * Fires when this tab becomes the active tab.
11179          * @param {Roo.TabPanel} tabPanel The parent TabPanel
11180          * @param {Roo.TabPanelItem} this
11181          */
11182         "activate": true,
11183         /**
11184          * @event beforeclose
11185          * Fires before this tab is closed. To cancel the close, set cancel to true on e (e.cancel = true).
11186          * @param {Roo.TabPanelItem} this
11187          * @param {Object} e Set cancel to true on this object to cancel the close.
11188          */
11189         "beforeclose": true,
11190         /**
11191          * @event close
11192          * Fires when this tab is closed.
11193          * @param {Roo.TabPanelItem} this
11194          */
11195          "close": true,
11196         /**
11197          * @event deactivate
11198          * Fires when this tab is no longer the active tab.
11199          * @param {Roo.TabPanel} tabPanel The parent TabPanel
11200          * @param {Roo.TabPanelItem} this
11201          */
11202          "deactivate" : true
11203     });
11204     this.hidden = false;
11205
11206     Roo.TabPanelItem.superclass.constructor.call(this);
11207 };
11208
11209 Roo.extend(Roo.TabPanelItem, Roo.util.Observable, {
11210     purgeListeners : function(){
11211        Roo.util.Observable.prototype.purgeListeners.call(this);
11212        this.el.removeAllListeners();
11213     },
11214     /**
11215      * Shows this TabPanelItem -- this <b>does not</b> deactivate the currently active TabPanelItem.
11216      */
11217     show : function(){
11218         this.pnode.addClass("on");
11219         this.showAction();
11220         if(Roo.isOpera){
11221             this.tabPanel.stripWrap.repaint();
11222         }
11223         this.fireEvent("activate", this.tabPanel, this);
11224     },
11225
11226     /**
11227      * Returns true if this tab is the active tab.
11228      * @return {Boolean}
11229      */
11230     isActive : function(){
11231         return this.tabPanel.getActiveTab() == this;
11232     },
11233
11234     /**
11235      * Hides this TabPanelItem -- if you don't activate another TabPanelItem this could look odd.
11236      */
11237     hide : function(){
11238         this.pnode.removeClass("on");
11239         this.hideAction();
11240         this.fireEvent("deactivate", this.tabPanel, this);
11241     },
11242
11243     hideAction : function(){
11244         this.bodyEl.hide();
11245         this.bodyEl.setStyle("position", "absolute");
11246         this.bodyEl.setLeft("-20000px");
11247         this.bodyEl.setTop("-20000px");
11248     },
11249
11250     showAction : function(){
11251         this.bodyEl.setStyle("position", "relative");
11252         this.bodyEl.setTop("");
11253         this.bodyEl.setLeft("");
11254         this.bodyEl.show();
11255     },
11256
11257     /**
11258      * Set the tooltip for the tab.
11259      * @param {String} tooltip The tab's tooltip
11260      */
11261     setTooltip : function(text){
11262         if(Roo.QuickTips && Roo.QuickTips.isEnabled()){
11263             this.textEl.dom.qtip = text;
11264             this.textEl.dom.removeAttribute('title');
11265         }else{
11266             this.textEl.dom.title = text;
11267         }
11268     },
11269
11270     onTabClick : function(e){
11271         e.preventDefault();
11272         this.tabPanel.activate(this.id);
11273     },
11274
11275     onTabMouseDown : function(e){
11276         e.preventDefault();
11277         this.tabPanel.activate(this.id);
11278     },
11279
11280     getWidth : function(){
11281         return this.inner.getWidth();
11282     },
11283
11284     setWidth : function(width){
11285         var iwidth = width - this.pnode.getPadding("lr");
11286         this.inner.setWidth(iwidth);
11287         this.textEl.setWidth(iwidth-this.inner.getPadding("lr"));
11288         this.pnode.setWidth(width);
11289     },
11290
11291     /**
11292      * Show or hide the tab
11293      * @param {Boolean} hidden True to hide or false to show.
11294      */
11295     setHidden : function(hidden){
11296         this.hidden = hidden;
11297         this.pnode.setStyle("display", hidden ? "none" : "");
11298     },
11299
11300     /**
11301      * Returns true if this tab is "hidden"
11302      * @return {Boolean}
11303      */
11304     isHidden : function(){
11305         return this.hidden;
11306     },
11307
11308     /**
11309      * Returns the text for this tab
11310      * @return {String}
11311      */
11312     getText : function(){
11313         return this.text;
11314     },
11315
11316     autoSize : function(){
11317         //this.el.beginMeasure();
11318         this.textEl.setWidth(1);
11319         this.setWidth(this.textEl.dom.scrollWidth+this.pnode.getPadding("lr")+this.inner.getPadding("lr"));
11320         //this.el.endMeasure();
11321     },
11322
11323     /**
11324      * Sets the text for the tab (Note: this also sets the tooltip text)
11325      * @param {String} text The tab's text and tooltip
11326      */
11327     setText : function(text){
11328         this.text = text;
11329         this.textEl.update(text);
11330         this.setTooltip(text);
11331         if(!this.tabPanel.resizeTabs){
11332             this.autoSize();
11333         }
11334     },
11335     /**
11336      * Activates this TabPanelItem -- this <b>does</b> deactivate the currently active TabPanelItem.
11337      */
11338     activate : function(){
11339         this.tabPanel.activate(this.id);
11340     },
11341
11342     /**
11343      * Disables this TabPanelItem -- this does nothing if this is the active TabPanelItem.
11344      */
11345     disable : function(){
11346         if(this.tabPanel.active != this){
11347             this.disabled = true;
11348             this.pnode.addClass("disabled");
11349         }
11350     },
11351
11352     /**
11353      * Enables this TabPanelItem if it was previously disabled.
11354      */
11355     enable : function(){
11356         this.disabled = false;
11357         this.pnode.removeClass("disabled");
11358     },
11359
11360     /**
11361      * Sets the content for this TabPanelItem.
11362      * @param {String} content The content
11363      * @param {Boolean} loadScripts true to look for and load scripts
11364      */
11365     setContent : function(content, loadScripts){
11366         this.bodyEl.update(content, loadScripts);
11367     },
11368
11369     /**
11370      * Gets the {@link Roo.UpdateManager} for the body of this TabPanelItem. Enables you to perform Ajax updates.
11371      * @return {Roo.UpdateManager} The UpdateManager
11372      */
11373     getUpdateManager : function(){
11374         return this.bodyEl.getUpdateManager();
11375     },
11376
11377     /**
11378      * Set a URL to be used to load the content for this TabPanelItem.
11379      * @param {String/Function} url The URL to load the content from, or a function to call to get the URL
11380      * @param {String/Object} params (optional) The string params for the update call or an object of the params. See {@link Roo.UpdateManager#update} for more details. (Defaults to null)
11381      * @param {Boolean} loadOnce (optional) Whether to only load the content once. If this is false it makes the Ajax call every time this TabPanelItem is activated. (Defaults to false)
11382      * @return {Roo.UpdateManager} The UpdateManager
11383      */
11384     setUrl : function(url, params, loadOnce){
11385         if(this.refreshDelegate){
11386             this.un('activate', this.refreshDelegate);
11387         }
11388         this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
11389         this.on("activate", this.refreshDelegate);
11390         return this.bodyEl.getUpdateManager();
11391     },
11392
11393     /** @private */
11394     _handleRefresh : function(url, params, loadOnce){
11395         if(!loadOnce || !this.loaded){
11396             var updater = this.bodyEl.getUpdateManager();
11397             updater.update(url, params, this._setLoaded.createDelegate(this));
11398         }
11399     },
11400
11401     /**
11402      *   Forces a content refresh from the URL specified in the {@link #setUrl} method.
11403      *   Will fail silently if the setUrl method has not been called.
11404      *   This does not activate the panel, just updates its content.
11405      */
11406     refresh : function(){
11407         if(this.refreshDelegate){
11408            this.loaded = false;
11409            this.refreshDelegate();
11410         }
11411     },
11412
11413     /** @private */
11414     _setLoaded : function(){
11415         this.loaded = true;
11416     },
11417
11418     /** @private */
11419     closeClick : function(e){
11420         var o = {};
11421         e.stopEvent();
11422         this.fireEvent("beforeclose", this, o);
11423         if(o.cancel !== true){
11424             this.tabPanel.removeTab(this.id);
11425         }
11426     },
11427     /**
11428      * The text displayed in the tooltip for the close icon.
11429      * @type String
11430      */
11431     closeText : "Close this tab"
11432 });
11433
11434 /** @private */
11435 Roo.TabPanel.prototype.createStrip = function(container){
11436     var strip = document.createElement("div");
11437     strip.className = "x-tabs-wrap";
11438     container.appendChild(strip);
11439     return strip;
11440 };
11441 /** @private */
11442 Roo.TabPanel.prototype.createStripList = function(strip){
11443     // div wrapper for retard IE
11444     // returns the "tr" element.
11445     strip.innerHTML = '<div class="x-tabs-strip-wrap">'+
11446         '<table class="x-tabs-strip" cellspacing="0" cellpadding="0" border="0"><tbody><tr>'+
11447         '<td class="x-tab-strip-toolbar"></td></tr></tbody></table></div>';
11448     return strip.firstChild.firstChild.firstChild.firstChild;
11449 };
11450 /** @private */
11451 Roo.TabPanel.prototype.createBody = function(container){
11452     var body = document.createElement("div");
11453     Roo.id(body, "tab-body");
11454     Roo.fly(body).addClass("x-tabs-body");
11455     container.appendChild(body);
11456     return body;
11457 };
11458 /** @private */
11459 Roo.TabPanel.prototype.createItemBody = function(bodyEl, id){
11460     var body = Roo.getDom(id);
11461     if(!body){
11462         body = document.createElement("div");
11463         body.id = id;
11464     }
11465     Roo.fly(body).addClass("x-tabs-item-body");
11466     bodyEl.insertBefore(body, bodyEl.firstChild);
11467     return body;
11468 };
11469 /** @private */
11470 Roo.TabPanel.prototype.createStripElements = function(stripEl, text, closable){
11471     var td = document.createElement("td");
11472     stripEl.insertBefore(td, stripEl.childNodes[stripEl.childNodes.length-1]);
11473     //stripEl.appendChild(td);
11474     if(closable){
11475         td.className = "x-tabs-closable";
11476         if(!this.closeTpl){
11477             this.closeTpl = new Roo.Template(
11478                '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
11479                '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span>' +
11480                '<div unselectable="on" class="close-icon">&#160;</div></em></span></a>'
11481             );
11482         }
11483         var el = this.closeTpl.overwrite(td, {"text": text});
11484         var close = el.getElementsByTagName("div")[0];
11485         var inner = el.getElementsByTagName("em")[0];
11486         return {"el": el, "close": close, "inner": inner};
11487     } else {
11488         if(!this.tabTpl){
11489             this.tabTpl = new Roo.Template(
11490                '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
11491                '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span></em></span></a>'
11492             );
11493         }
11494         var el = this.tabTpl.overwrite(td, {"text": text});
11495         var inner = el.getElementsByTagName("em")[0];
11496         return {"el": el, "inner": inner};
11497     }
11498 };/*
11499  * Based on:
11500  * Ext JS Library 1.1.1
11501  * Copyright(c) 2006-2007, Ext JS, LLC.
11502  *
11503  * Originally Released Under LGPL - original licence link has changed is not relivant.
11504  *
11505  * Fork - LGPL
11506  * <script type="text/javascript">
11507  */
11508
11509 /**
11510  * @class Roo.Button
11511  * @extends Roo.util.Observable
11512  * Simple Button class
11513  * @cfg {String} text The button text
11514  * @cfg {String} icon The path to an image to display in the button (the image will be set as the background-image
11515  * CSS property of the button by default, so if you want a mixed icon/text button, set cls:"x-btn-text-icon")
11516  * @cfg {Function} handler A function called when the button is clicked (can be used instead of click event)
11517  * @cfg {Object} scope The scope of the handler
11518  * @cfg {Number} minWidth The minimum width for this button (used to give a set of buttons a common width)
11519  * @cfg {String/Object} tooltip The tooltip for the button - can be a string or QuickTips config object
11520  * @cfg {Boolean} hidden True to start hidden (defaults to false)
11521  * @cfg {Boolean} disabled True to start disabled (defaults to false)
11522  * @cfg {Boolean} pressed True to start pressed (only if enableToggle = true)
11523  * @cfg {String} toggleGroup The group this toggle button is a member of (only 1 per group can be pressed, only
11524    applies if enableToggle = true)
11525  * @cfg {String/HTMLElement/Element} renderTo The element to append the button to
11526  * @cfg {Boolean/Object} repeat True to repeat fire the click event while the mouse is down. This can also be
11527   an {@link Roo.util.ClickRepeater} config object (defaults to false).
11528  * @constructor
11529  * Create a new button
11530  * @param {Object} config The config object
11531  */
11532 Roo.Button = function(renderTo, config)
11533 {
11534     if (!config) {
11535         config = renderTo;
11536         renderTo = config.renderTo || false;
11537     }
11538     
11539     Roo.apply(this, config);
11540     this.addEvents({
11541         /**
11542              * @event click
11543              * Fires when this button is clicked
11544              * @param {Button} this
11545              * @param {EventObject} e The click event
11546              */
11547             "click" : true,
11548         /**
11549              * @event toggle
11550              * Fires when the "pressed" state of this button changes (only if enableToggle = true)
11551              * @param {Button} this
11552              * @param {Boolean} pressed
11553              */
11554             "toggle" : true,
11555         /**
11556              * @event mouseover
11557              * Fires when the mouse hovers over the button
11558              * @param {Button} this
11559              * @param {Event} e The event object
11560              */
11561         'mouseover' : true,
11562         /**
11563              * @event mouseout
11564              * Fires when the mouse exits the button
11565              * @param {Button} this
11566              * @param {Event} e The event object
11567              */
11568         'mouseout': true,
11569          /**
11570              * @event render
11571              * Fires when the button is rendered
11572              * @param {Button} this
11573              */
11574         'render': true
11575     });
11576     if(this.menu){
11577         this.menu = Roo.menu.MenuMgr.get(this.menu);
11578     }
11579     // register listeners first!!  - so render can be captured..
11580     Roo.util.Observable.call(this);
11581     if(renderTo){
11582         this.render(renderTo);
11583     }
11584     
11585   
11586 };
11587
11588 Roo.extend(Roo.Button, Roo.util.Observable, {
11589     /**
11590      * 
11591      */
11592     
11593     /**
11594      * Read-only. True if this button is hidden
11595      * @type Boolean
11596      */
11597     hidden : false,
11598     /**
11599      * Read-only. True if this button is disabled
11600      * @type Boolean
11601      */
11602     disabled : false,
11603     /**
11604      * Read-only. True if this button is pressed (only if enableToggle = true)
11605      * @type Boolean
11606      */
11607     pressed : false,
11608
11609     /**
11610      * @cfg {Number} tabIndex 
11611      * The DOM tabIndex for this button (defaults to undefined)
11612      */
11613     tabIndex : undefined,
11614
11615     /**
11616      * @cfg {Boolean} enableToggle
11617      * True to enable pressed/not pressed toggling (defaults to false)
11618      */
11619     enableToggle: false,
11620     /**
11621      * @cfg {Mixed} menu
11622      * Standard menu attribute consisting of a reference to a menu object, a menu id or a menu config blob (defaults to undefined).
11623      */
11624     menu : undefined,
11625     /**
11626      * @cfg {String} menuAlign
11627      * The position to align the menu to (see {@link Roo.Element#alignTo} for more details, defaults to 'tl-bl?').
11628      */
11629     menuAlign : "tl-bl?",
11630
11631     /**
11632      * @cfg {String} iconCls
11633      * A css class which sets a background image to be used as the icon for this button (defaults to undefined).
11634      */
11635     iconCls : undefined,
11636     /**
11637      * @cfg {String} type
11638      * The button's type, corresponding to the DOM input element type attribute.  Either "submit," "reset" or "button" (default).
11639      */
11640     type : 'button',
11641
11642     // private
11643     menuClassTarget: 'tr',
11644
11645     /**
11646      * @cfg {String} clickEvent
11647      * The type of event to map to the button's event handler (defaults to 'click')
11648      */
11649     clickEvent : 'click',
11650
11651     /**
11652      * @cfg {Boolean} handleMouseEvents
11653      * False to disable visual cues on mouseover, mouseout and mousedown (defaults to true)
11654      */
11655     handleMouseEvents : true,
11656
11657     /**
11658      * @cfg {String} tooltipType
11659      * The type of tooltip to use. Either "qtip" (default) for QuickTips or "title" for title attribute.
11660      */
11661     tooltipType : 'qtip',
11662
11663     /**
11664      * @cfg {String} cls
11665      * A CSS class to apply to the button's main element.
11666      */
11667     
11668     /**
11669      * @cfg {Roo.Template} template (Optional)
11670      * An {@link Roo.Template} with which to create the Button's main element. This Template must
11671      * contain numeric substitution parameter 0 if it is to display the tRoo property. Changing the template could
11672      * require code modifications if required elements (e.g. a button) aren't present.
11673      */
11674
11675     // private
11676     render : function(renderTo){
11677         var btn;
11678         if(this.hideParent){
11679             this.parentEl = Roo.get(renderTo);
11680         }
11681         if(!this.dhconfig){
11682             if(!this.template){
11683                 if(!Roo.Button.buttonTemplate){
11684                     // hideous table template
11685                     Roo.Button.buttonTemplate = new Roo.Template(
11686                         '<table border="0" cellpadding="0" cellspacing="0" class="x-btn-wrap"><tbody><tr>',
11687                         '<td class="x-btn-left"><i>&#160;</i></td><td class="x-btn-center"><em unselectable="on"><button class="x-btn-text" type="{1}">{0}</button></em></td><td class="x-btn-right"><i>&#160;</i></td>',
11688                         "</tr></tbody></table>");
11689                 }
11690                 this.template = Roo.Button.buttonTemplate;
11691             }
11692             btn = this.template.append(renderTo, [this.text || '&#160;', this.type], true);
11693             var btnEl = btn.child("button:first");
11694             btnEl.on('focus', this.onFocus, this);
11695             btnEl.on('blur', this.onBlur, this);
11696             if(this.cls){
11697                 btn.addClass(this.cls);
11698             }
11699             if(this.icon){
11700                 btnEl.setStyle('background-image', 'url(' +this.icon +')');
11701             }
11702             if(this.iconCls){
11703                 btnEl.addClass(this.iconCls);
11704                 if(!this.cls){
11705                     btn.addClass(this.text ? 'x-btn-text-icon' : 'x-btn-icon');
11706                 }
11707             }
11708             if(this.tabIndex !== undefined){
11709                 btnEl.dom.tabIndex = this.tabIndex;
11710             }
11711             if(this.tooltip){
11712                 if(typeof this.tooltip == 'object'){
11713                     Roo.QuickTips.tips(Roo.apply({
11714                           target: btnEl.id
11715                     }, this.tooltip));
11716                 } else {
11717                     btnEl.dom[this.tooltipType] = this.tooltip;
11718                 }
11719             }
11720         }else{
11721             btn = Roo.DomHelper.append(Roo.get(renderTo).dom, this.dhconfig, true);
11722         }
11723         this.el = btn;
11724         if(this.id){
11725             this.el.dom.id = this.el.id = this.id;
11726         }
11727         if(this.menu){
11728             this.el.child(this.menuClassTarget).addClass("x-btn-with-menu");
11729             this.menu.on("show", this.onMenuShow, this);
11730             this.menu.on("hide", this.onMenuHide, this);
11731         }
11732         btn.addClass("x-btn");
11733         if(Roo.isIE && !Roo.isIE7){
11734             this.autoWidth.defer(1, this);
11735         }else{
11736             this.autoWidth();
11737         }
11738         if(this.handleMouseEvents){
11739             btn.on("mouseover", this.onMouseOver, this);
11740             btn.on("mouseout", this.onMouseOut, this);
11741             btn.on("mousedown", this.onMouseDown, this);
11742         }
11743         btn.on(this.clickEvent, this.onClick, this);
11744         //btn.on("mouseup", this.onMouseUp, this);
11745         if(this.hidden){
11746             this.hide();
11747         }
11748         if(this.disabled){
11749             this.disable();
11750         }
11751         Roo.ButtonToggleMgr.register(this);
11752         if(this.pressed){
11753             this.el.addClass("x-btn-pressed");
11754         }
11755         if(this.repeat){
11756             var repeater = new Roo.util.ClickRepeater(btn,
11757                 typeof this.repeat == "object" ? this.repeat : {}
11758             );
11759             repeater.on("click", this.onClick,  this);
11760         }
11761         
11762         this.fireEvent('render', this);
11763         
11764     },
11765     /**
11766      * Returns the button's underlying element
11767      * @return {Roo.Element} The element
11768      */
11769     getEl : function(){
11770         return this.el;  
11771     },
11772     
11773     /**
11774      * Destroys this Button and removes any listeners.
11775      */
11776     destroy : function(){
11777         Roo.ButtonToggleMgr.unregister(this);
11778         this.el.removeAllListeners();
11779         this.purgeListeners();
11780         this.el.remove();
11781     },
11782
11783     // private
11784     autoWidth : function(){
11785         if(this.el){
11786             this.el.setWidth("auto");
11787             if(Roo.isIE7 && Roo.isStrict){
11788                 var ib = this.el.child('button');
11789                 if(ib && ib.getWidth() > 20){
11790                     ib.clip();
11791                     ib.setWidth(Roo.util.TextMetrics.measure(ib, this.text).width+ib.getFrameWidth('lr'));
11792                 }
11793             }
11794             if(this.minWidth){
11795                 if(this.hidden){
11796                     this.el.beginMeasure();
11797                 }
11798                 if(this.el.getWidth() < this.minWidth){
11799                     this.el.setWidth(this.minWidth);
11800                 }
11801                 if(this.hidden){
11802                     this.el.endMeasure();
11803                 }
11804             }
11805         }
11806     },
11807
11808     /**
11809      * Assigns this button's click handler
11810      * @param {Function} handler The function to call when the button is clicked
11811      * @param {Object} scope (optional) Scope for the function passed in
11812      */
11813     setHandler : function(handler, scope){
11814         this.handler = handler;
11815         this.scope = scope;  
11816     },
11817     
11818     /**
11819      * Sets this button's text
11820      * @param {String} text The button text
11821      */
11822     setText : function(text){
11823         this.text = text;
11824         if(this.el){
11825             this.el.child("td.x-btn-center button.x-btn-text").update(text);
11826         }
11827         this.autoWidth();
11828     },
11829     
11830     /**
11831      * Gets the text for this button
11832      * @return {String} The button text
11833      */
11834     getText : function(){
11835         return this.text;  
11836     },
11837     
11838     /**
11839      * Show this button
11840      */
11841     show: function(){
11842         this.hidden = false;
11843         if(this.el){
11844             this[this.hideParent? 'parentEl' : 'el'].setStyle("display", "");
11845         }
11846     },
11847     
11848     /**
11849      * Hide this button
11850      */
11851     hide: function(){
11852         this.hidden = true;
11853         if(this.el){
11854             this[this.hideParent? 'parentEl' : 'el'].setStyle("display", "none");
11855         }
11856     },
11857     
11858     /**
11859      * Convenience function for boolean show/hide
11860      * @param {Boolean} visible True to show, false to hide
11861      */
11862     setVisible: function(visible){
11863         if(visible) {
11864             this.show();
11865         }else{
11866             this.hide();
11867         }
11868     },
11869     
11870     /**
11871      * If a state it passed, it becomes the pressed state otherwise the current state is toggled.
11872      * @param {Boolean} state (optional) Force a particular state
11873      */
11874     toggle : function(state){
11875         state = state === undefined ? !this.pressed : state;
11876         if(state != this.pressed){
11877             if(state){
11878                 this.el.addClass("x-btn-pressed");
11879                 this.pressed = true;
11880                 this.fireEvent("toggle", this, true);
11881             }else{
11882                 this.el.removeClass("x-btn-pressed");
11883                 this.pressed = false;
11884                 this.fireEvent("toggle", this, false);
11885             }
11886             if(this.toggleHandler){
11887                 this.toggleHandler.call(this.scope || this, this, state);
11888             }
11889         }
11890     },
11891     
11892     /**
11893      * Focus the button
11894      */
11895     focus : function(){
11896         this.el.child('button:first').focus();
11897     },
11898     
11899     /**
11900      * Disable this button
11901      */
11902     disable : function(){
11903         if(this.el){
11904             this.el.addClass("x-btn-disabled");
11905         }
11906         this.disabled = true;
11907     },
11908     
11909     /**
11910      * Enable this button
11911      */
11912     enable : function(){
11913         if(this.el){
11914             this.el.removeClass("x-btn-disabled");
11915         }
11916         this.disabled = false;
11917     },
11918
11919     /**
11920      * Convenience function for boolean enable/disable
11921      * @param {Boolean} enabled True to enable, false to disable
11922      */
11923     setDisabled : function(v){
11924         this[v !== true ? "enable" : "disable"]();
11925     },
11926
11927     // private
11928     onClick : function(e){
11929         if(e){
11930             e.preventDefault();
11931         }
11932         if(e.button != 0){
11933             return;
11934         }
11935         if(!this.disabled){
11936             if(this.enableToggle){
11937                 this.toggle();
11938             }
11939             if(this.menu && !this.menu.isVisible()){
11940                 this.menu.show(this.el, this.menuAlign);
11941             }
11942             this.fireEvent("click", this, e);
11943             if(this.handler){
11944                 this.el.removeClass("x-btn-over");
11945                 this.handler.call(this.scope || this, this, e);
11946             }
11947         }
11948     },
11949     // private
11950     onMouseOver : function(e){
11951         if(!this.disabled){
11952             this.el.addClass("x-btn-over");
11953             this.fireEvent('mouseover', this, e);
11954         }
11955     },
11956     // private
11957     onMouseOut : function(e){
11958         if(!e.within(this.el,  true)){
11959             this.el.removeClass("x-btn-over");
11960             this.fireEvent('mouseout', this, e);
11961         }
11962     },
11963     // private
11964     onFocus : function(e){
11965         if(!this.disabled){
11966             this.el.addClass("x-btn-focus");
11967         }
11968     },
11969     // private
11970     onBlur : function(e){
11971         this.el.removeClass("x-btn-focus");
11972     },
11973     // private
11974     onMouseDown : function(e){
11975         if(!this.disabled && e.button == 0){
11976             this.el.addClass("x-btn-click");
11977             Roo.get(document).on('mouseup', this.onMouseUp, this);
11978         }
11979     },
11980     // private
11981     onMouseUp : function(e){
11982         if(e.button == 0){
11983             this.el.removeClass("x-btn-click");
11984             Roo.get(document).un('mouseup', this.onMouseUp, this);
11985         }
11986     },
11987     // private
11988     onMenuShow : function(e){
11989         this.el.addClass("x-btn-menu-active");
11990     },
11991     // private
11992     onMenuHide : function(e){
11993         this.el.removeClass("x-btn-menu-active");
11994     }   
11995 });
11996
11997 // Private utility class used by Button
11998 Roo.ButtonToggleMgr = function(){
11999    var groups = {};
12000    
12001    function toggleGroup(btn, state){
12002        if(state){
12003            var g = groups[btn.toggleGroup];
12004            for(var i = 0, l = g.length; i < l; i++){
12005                if(g[i] != btn){
12006                    g[i].toggle(false);
12007                }
12008            }
12009        }
12010    }
12011    
12012    return {
12013        register : function(btn){
12014            if(!btn.toggleGroup){
12015                return;
12016            }
12017            var g = groups[btn.toggleGroup];
12018            if(!g){
12019                g = groups[btn.toggleGroup] = [];
12020            }
12021            g.push(btn);
12022            btn.on("toggle", toggleGroup);
12023        },
12024        
12025        unregister : function(btn){
12026            if(!btn.toggleGroup){
12027                return;
12028            }
12029            var g = groups[btn.toggleGroup];
12030            if(g){
12031                g.remove(btn);
12032                btn.un("toggle", toggleGroup);
12033            }
12034        }
12035    };
12036 }();/*
12037  * Based on:
12038  * Ext JS Library 1.1.1
12039  * Copyright(c) 2006-2007, Ext JS, LLC.
12040  *
12041  * Originally Released Under LGPL - original licence link has changed is not relivant.
12042  *
12043  * Fork - LGPL
12044  * <script type="text/javascript">
12045  */
12046  
12047 /**
12048  * @class Roo.SplitButton
12049  * @extends Roo.Button
12050  * A split button that provides a built-in dropdown arrow that can fire an event separately from the default
12051  * click event of the button.  Typically this would be used to display a dropdown menu that provides additional
12052  * options to the primary button action, but any custom handler can provide the arrowclick implementation.
12053  * @cfg {Function} arrowHandler A function called when the arrow button is clicked (can be used instead of click event)
12054  * @cfg {String} arrowTooltip The title attribute of the arrow
12055  * @constructor
12056  * Create a new menu button
12057  * @param {String/HTMLElement/Element} renderTo The element to append the button to
12058  * @param {Object} config The config object
12059  */
12060 Roo.SplitButton = function(renderTo, config){
12061     Roo.SplitButton.superclass.constructor.call(this, renderTo, config);
12062     /**
12063      * @event arrowclick
12064      * Fires when this button's arrow is clicked
12065      * @param {SplitButton} this
12066      * @param {EventObject} e The click event
12067      */
12068     this.addEvents({"arrowclick":true});
12069 };
12070
12071 Roo.extend(Roo.SplitButton, Roo.Button, {
12072     render : function(renderTo){
12073         // this is one sweet looking template!
12074         var tpl = new Roo.Template(
12075             '<table cellspacing="0" class="x-btn-menu-wrap x-btn"><tr><td>',
12076             '<table cellspacing="0" class="x-btn-wrap x-btn-menu-text-wrap"><tbody>',
12077             '<tr><td class="x-btn-left"><i>&#160;</i></td><td class="x-btn-center"><button class="x-btn-text" type="{1}">{0}</button></td></tr>',
12078             "</tbody></table></td><td>",
12079             '<table cellspacing="0" class="x-btn-wrap x-btn-menu-arrow-wrap"><tbody>',
12080             '<tr><td class="x-btn-center"><button class="x-btn-menu-arrow-el" type="button">&#160;</button></td><td class="x-btn-right"><i>&#160;</i></td></tr>',
12081             "</tbody></table></td></tr></table>"
12082         );
12083         var btn = tpl.append(renderTo, [this.text, this.type], true);
12084         var btnEl = btn.child("button");
12085         if(this.cls){
12086             btn.addClass(this.cls);
12087         }
12088         if(this.icon){
12089             btnEl.setStyle('background-image', 'url(' +this.icon +')');
12090         }
12091         if(this.iconCls){
12092             btnEl.addClass(this.iconCls);
12093             if(!this.cls){
12094                 btn.addClass(this.text ? 'x-btn-text-icon' : 'x-btn-icon');
12095             }
12096         }
12097         this.el = btn;
12098         if(this.handleMouseEvents){
12099             btn.on("mouseover", this.onMouseOver, this);
12100             btn.on("mouseout", this.onMouseOut, this);
12101             btn.on("mousedown", this.onMouseDown, this);
12102             btn.on("mouseup", this.onMouseUp, this);
12103         }
12104         btn.on(this.clickEvent, this.onClick, this);
12105         if(this.tooltip){
12106             if(typeof this.tooltip == 'object'){
12107                 Roo.QuickTips.tips(Roo.apply({
12108                       target: btnEl.id
12109                 }, this.tooltip));
12110             } else {
12111                 btnEl.dom[this.tooltipType] = this.tooltip;
12112             }
12113         }
12114         if(this.arrowTooltip){
12115             btn.child("button:nth(2)").dom[this.tooltipType] = this.arrowTooltip;
12116         }
12117         if(this.hidden){
12118             this.hide();
12119         }
12120         if(this.disabled){
12121             this.disable();
12122         }
12123         if(this.pressed){
12124             this.el.addClass("x-btn-pressed");
12125         }
12126         if(Roo.isIE && !Roo.isIE7){
12127             this.autoWidth.defer(1, this);
12128         }else{
12129             this.autoWidth();
12130         }
12131         if(this.menu){
12132             this.menu.on("show", this.onMenuShow, this);
12133             this.menu.on("hide", this.onMenuHide, this);
12134         }
12135         this.fireEvent('render', this);
12136     },
12137
12138     // private
12139     autoWidth : function(){
12140         if(this.el){
12141             var tbl = this.el.child("table:first");
12142             var tbl2 = this.el.child("table:last");
12143             this.el.setWidth("auto");
12144             tbl.setWidth("auto");
12145             if(Roo.isIE7 && Roo.isStrict){
12146                 var ib = this.el.child('button:first');
12147                 if(ib && ib.getWidth() > 20){
12148                     ib.clip();
12149                     ib.setWidth(Roo.util.TextMetrics.measure(ib, this.text).width+ib.getFrameWidth('lr'));
12150                 }
12151             }
12152             if(this.minWidth){
12153                 if(this.hidden){
12154                     this.el.beginMeasure();
12155                 }
12156                 if((tbl.getWidth()+tbl2.getWidth()) < this.minWidth){
12157                     tbl.setWidth(this.minWidth-tbl2.getWidth());
12158                 }
12159                 if(this.hidden){
12160                     this.el.endMeasure();
12161                 }
12162             }
12163             this.el.setWidth(tbl.getWidth()+tbl2.getWidth());
12164         } 
12165     },
12166     /**
12167      * Sets this button's click handler
12168      * @param {Function} handler The function to call when the button is clicked
12169      * @param {Object} scope (optional) Scope for the function passed above
12170      */
12171     setHandler : function(handler, scope){
12172         this.handler = handler;
12173         this.scope = scope;  
12174     },
12175     
12176     /**
12177      * Sets this button's arrow click handler
12178      * @param {Function} handler The function to call when the arrow is clicked
12179      * @param {Object} scope (optional) Scope for the function passed above
12180      */
12181     setArrowHandler : function(handler, scope){
12182         this.arrowHandler = handler;
12183         this.scope = scope;  
12184     },
12185     
12186     /**
12187      * Focus the button
12188      */
12189     focus : function(){
12190         if(this.el){
12191             this.el.child("button:first").focus();
12192         }
12193     },
12194
12195     // private
12196     onClick : function(e){
12197         e.preventDefault();
12198         if(!this.disabled){
12199             if(e.getTarget(".x-btn-menu-arrow-wrap")){
12200                 if(this.menu && !this.menu.isVisible()){
12201                     this.menu.show(this.el, this.menuAlign);
12202                 }
12203                 this.fireEvent("arrowclick", this, e);
12204                 if(this.arrowHandler){
12205                     this.arrowHandler.call(this.scope || this, this, e);
12206                 }
12207             }else{
12208                 this.fireEvent("click", this, e);
12209                 if(this.handler){
12210                     this.handler.call(this.scope || this, this, e);
12211                 }
12212             }
12213         }
12214     },
12215     // private
12216     onMouseDown : function(e){
12217         if(!this.disabled){
12218             Roo.fly(e.getTarget("table")).addClass("x-btn-click");
12219         }
12220     },
12221     // private
12222     onMouseUp : function(e){
12223         Roo.fly(e.getTarget("table")).removeClass("x-btn-click");
12224     }   
12225 });
12226
12227
12228 // backwards compat
12229 Roo.MenuButton = Roo.SplitButton;/*
12230  * Based on:
12231  * Ext JS Library 1.1.1
12232  * Copyright(c) 2006-2007, Ext JS, LLC.
12233  *
12234  * Originally Released Under LGPL - original licence link has changed is not relivant.
12235  *
12236  * Fork - LGPL
12237  * <script type="text/javascript">
12238  */
12239
12240 /**
12241  * @class Roo.Toolbar
12242  * Basic Toolbar class.
12243  * @constructor
12244  * Creates a new Toolbar
12245  * @param {Object} config The config object
12246  */ 
12247 Roo.Toolbar = function(container, buttons, config)
12248 {
12249     /// old consturctor format still supported..
12250     if(container instanceof Array){ // omit the container for later rendering
12251         buttons = container;
12252         config = buttons;
12253         container = null;
12254     }
12255     if (typeof(container) == 'object' && container.xtype) {
12256         config = container;
12257         container = config.container;
12258         buttons = config.buttons; // not really - use items!!
12259     }
12260     var xitems = [];
12261     if (config && config.items) {
12262         xitems = config.items;
12263         delete config.items;
12264     }
12265     Roo.apply(this, config);
12266     this.buttons = buttons;
12267     
12268     if(container){
12269         this.render(container);
12270     }
12271     Roo.each(xitems, function(b) {
12272         this.add(b);
12273     }, this);
12274     
12275 };
12276
12277 Roo.Toolbar.prototype = {
12278     /**
12279      * @cfg {Roo.data.Store} items
12280      * array of button configs or elements to add
12281      */
12282     
12283     /**
12284      * @cfg {String/HTMLElement/Element} container
12285      * The id or element that will contain the toolbar
12286      */
12287     // private
12288     render : function(ct){
12289         this.el = Roo.get(ct);
12290         if(this.cls){
12291             this.el.addClass(this.cls);
12292         }
12293         // using a table allows for vertical alignment
12294         // 100% width is needed by Safari...
12295         this.el.update('<div class="x-toolbar x-small-editor"><table cellspacing="0"><tr></tr></table></div>');
12296         this.tr = this.el.child("tr", true);
12297         var autoId = 0;
12298         this.items = new Roo.util.MixedCollection(false, function(o){
12299             return o.id || ("item" + (++autoId));
12300         });
12301         if(this.buttons){
12302             this.add.apply(this, this.buttons);
12303             delete this.buttons;
12304         }
12305     },
12306
12307     /**
12308      * Adds element(s) to the toolbar -- this function takes a variable number of 
12309      * arguments of mixed type and adds them to the toolbar.
12310      * @param {Mixed} arg1 The following types of arguments are all valid:<br />
12311      * <ul>
12312      * <li>{@link Roo.Toolbar.Button} config: A valid button config object (equivalent to {@link #addButton})</li>
12313      * <li>HtmlElement: Any standard HTML element (equivalent to {@link #addElement})</li>
12314      * <li>Field: Any form field (equivalent to {@link #addField})</li>
12315      * <li>Item: Any subclass of {@link Roo.Toolbar.Item} (equivalent to {@link #addItem})</li>
12316      * <li>String: Any generic string (gets wrapped in a {@link Roo.Toolbar.TextItem}, equivalent to {@link #addText}).
12317      * Note that there are a few special strings that are treated differently as explained nRoo.</li>
12318      * <li>'separator' or '-': Creates a separator element (equivalent to {@link #addSeparator})</li>
12319      * <li>' ': Creates a spacer element (equivalent to {@link #addSpacer})</li>
12320      * <li>'->': Creates a fill element (equivalent to {@link #addFill})</li>
12321      * </ul>
12322      * @param {Mixed} arg2
12323      * @param {Mixed} etc.
12324      */
12325     add : function(){
12326         var a = arguments, l = a.length;
12327         for(var i = 0; i < l; i++){
12328             this._add(a[i]);
12329         }
12330     },
12331     // private..
12332     _add : function(el) {
12333         
12334         if (el.xtype) {
12335             el = Roo.factory(el, typeof(Roo.Toolbar[el.xtype]) == 'undefined' ? Roo.form : Roo.Toolbar);
12336         }
12337         
12338         if (el.applyTo){ // some kind of form field
12339             return this.addField(el);
12340         } 
12341         if (el.render){ // some kind of Toolbar.Item
12342             return this.addItem(el);
12343         }
12344         if (typeof el == "string"){ // string
12345             if(el == "separator" || el == "-"){
12346                 return this.addSeparator();
12347             }
12348             if (el == " "){
12349                 return this.addSpacer();
12350             }
12351             if(el == "->"){
12352                 return this.addFill();
12353             }
12354             return this.addText(el);
12355             
12356         }
12357         if(el.tagName){ // element
12358             return this.addElement(el);
12359         }
12360         if(typeof el == "object"){ // must be button config?
12361             return this.addButton(el);
12362         }
12363         // and now what?!?!
12364         return false;
12365         
12366     },
12367     
12368     /**
12369      * Add an Xtype element
12370      * @param {Object} xtype Xtype Object
12371      * @return {Object} created Object
12372      */
12373     addxtype : function(e){
12374         return this.add(e);  
12375     },
12376     
12377     /**
12378      * Returns the Element for this toolbar.
12379      * @return {Roo.Element}
12380      */
12381     getEl : function(){
12382         return this.el;  
12383     },
12384     
12385     /**
12386      * Adds a separator
12387      * @return {Roo.Toolbar.Item} The separator item
12388      */
12389     addSeparator : function(){
12390         return this.addItem(new Roo.Toolbar.Separator());
12391     },
12392
12393     /**
12394      * Adds a spacer element
12395      * @return {Roo.Toolbar.Spacer} The spacer item
12396      */
12397     addSpacer : function(){
12398         return this.addItem(new Roo.Toolbar.Spacer());
12399     },
12400
12401     /**
12402      * Adds a fill element that forces subsequent additions to the right side of the toolbar
12403      * @return {Roo.Toolbar.Fill} The fill item
12404      */
12405     addFill : function(){
12406         return this.addItem(new Roo.Toolbar.Fill());
12407     },
12408
12409     /**
12410      * Adds any standard HTML element to the toolbar
12411      * @param {String/HTMLElement/Element} el The element or id of the element to add
12412      * @return {Roo.Toolbar.Item} The element's item
12413      */
12414     addElement : function(el){
12415         return this.addItem(new Roo.Toolbar.Item(el));
12416     },
12417     /**
12418      * Collection of items on the toolbar.. (only Toolbar Items, so use fields to retrieve fields)
12419      * @type Roo.util.MixedCollection  
12420      */
12421     items : false,
12422      
12423     /**
12424      * Adds any Toolbar.Item or subclass
12425      * @param {Roo.Toolbar.Item} item
12426      * @return {Roo.Toolbar.Item} The item
12427      */
12428     addItem : function(item){
12429         var td = this.nextBlock();
12430         item.render(td);
12431         this.items.add(item);
12432         return item;
12433     },
12434     
12435     /**
12436      * Adds a button (or buttons). See {@link Roo.Toolbar.Button} for more info on the config.
12437      * @param {Object/Array} config A button config or array of configs
12438      * @return {Roo.Toolbar.Button/Array}
12439      */
12440     addButton : function(config){
12441         if(config instanceof Array){
12442             var buttons = [];
12443             for(var i = 0, len = config.length; i < len; i++) {
12444                 buttons.push(this.addButton(config[i]));
12445             }
12446             return buttons;
12447         }
12448         var b = config;
12449         if(!(config instanceof Roo.Toolbar.Button)){
12450             b = config.split ?
12451                 new Roo.Toolbar.SplitButton(config) :
12452                 new Roo.Toolbar.Button(config);
12453         }
12454         var td = this.nextBlock();
12455         b.render(td);
12456         this.items.add(b);
12457         return b;
12458     },
12459     
12460     /**
12461      * Adds text to the toolbar
12462      * @param {String} text The text to add
12463      * @return {Roo.Toolbar.Item} The element's item
12464      */
12465     addText : function(text){
12466         return this.addItem(new Roo.Toolbar.TextItem(text));
12467     },
12468     
12469     /**
12470      * Inserts any {@link Roo.Toolbar.Item}/{@link Roo.Toolbar.Button} at the specified index.
12471      * @param {Number} index The index where the item is to be inserted
12472      * @param {Object/Roo.Toolbar.Item/Roo.Toolbar.Button (may be Array)} item The button, or button config object to be inserted.
12473      * @return {Roo.Toolbar.Button/Item}
12474      */
12475     insertButton : function(index, item){
12476         if(item instanceof Array){
12477             var buttons = [];
12478             for(var i = 0, len = item.length; i < len; i++) {
12479                buttons.push(this.insertButton(index + i, item[i]));
12480             }
12481             return buttons;
12482         }
12483         if (!(item instanceof Roo.Toolbar.Button)){
12484            item = new Roo.Toolbar.Button(item);
12485         }
12486         var td = document.createElement("td");
12487         this.tr.insertBefore(td, this.tr.childNodes[index]);
12488         item.render(td);
12489         this.items.insert(index, item);
12490         return item;
12491     },
12492     
12493     /**
12494      * Adds a new element to the toolbar from the passed {@link Roo.DomHelper} config.
12495      * @param {Object} config
12496      * @return {Roo.Toolbar.Item} The element's item
12497      */
12498     addDom : function(config, returnEl){
12499         var td = this.nextBlock();
12500         Roo.DomHelper.overwrite(td, config);
12501         var ti = new Roo.Toolbar.Item(td.firstChild);
12502         ti.render(td);
12503         this.items.add(ti);
12504         return ti;
12505     },
12506
12507     /**
12508      * Collection of fields on the toolbar.. usefull for quering (value is false if there are no fields)
12509      * @type Roo.util.MixedCollection  
12510      */
12511     fields : false,
12512     
12513     /**
12514      * Adds a dynamically rendered Roo.form field (TextField, ComboBox, etc). Note: the field should not have
12515      * been rendered yet. For a field that has already been rendered, use {@link #addElement}.
12516      * @param {Roo.form.Field} field
12517      * @return {Roo.ToolbarItem}
12518      */
12519      
12520       
12521     addField : function(field) {
12522         if (!this.fields) {
12523             var autoId = 0;
12524             this.fields = new Roo.util.MixedCollection(false, function(o){
12525                 return o.id || ("item" + (++autoId));
12526             });
12527
12528         }
12529         
12530         var td = this.nextBlock();
12531         field.render(td);
12532         var ti = new Roo.Toolbar.Item(td.firstChild);
12533         ti.render(td);
12534         this.items.add(ti);
12535         this.fields.add(field);
12536         return ti;
12537     },
12538     /**
12539      * Hide the toolbar
12540      * @method hide
12541      */
12542      
12543       
12544     hide : function()
12545     {
12546         this.el.child('div').setVisibilityMode(Roo.Element.DISPLAY);
12547         this.el.child('div').hide();
12548     },
12549     /**
12550      * Show the toolbar
12551      * @method show
12552      */
12553     show : function()
12554     {
12555         this.el.child('div').show();
12556     },
12557       
12558     // private
12559     nextBlock : function(){
12560         var td = document.createElement("td");
12561         this.tr.appendChild(td);
12562         return td;
12563     },
12564
12565     // private
12566     destroy : function(){
12567         if(this.items){ // rendered?
12568             Roo.destroy.apply(Roo, this.items.items);
12569         }
12570         if(this.fields){ // rendered?
12571             Roo.destroy.apply(Roo, this.fields.items);
12572         }
12573         Roo.Element.uncache(this.el, this.tr);
12574     }
12575 };
12576
12577 /**
12578  * @class Roo.Toolbar.Item
12579  * The base class that other classes should extend in order to get some basic common toolbar item functionality.
12580  * @constructor
12581  * Creates a new Item
12582  * @param {HTMLElement} el 
12583  */
12584 Roo.Toolbar.Item = function(el){
12585     this.el = Roo.getDom(el);
12586     this.id = Roo.id(this.el);
12587     this.hidden = false;
12588 };
12589
12590 Roo.Toolbar.Item.prototype = {
12591     
12592     /**
12593      * Get this item's HTML Element
12594      * @return {HTMLElement}
12595      */
12596     getEl : function(){
12597        return this.el;  
12598     },
12599
12600     // private
12601     render : function(td){
12602         this.td = td;
12603         td.appendChild(this.el);
12604     },
12605     
12606     /**
12607      * Removes and destroys this item.
12608      */
12609     destroy : function(){
12610         this.td.parentNode.removeChild(this.td);
12611     },
12612     
12613     /**
12614      * Shows this item.
12615      */
12616     show: function(){
12617         this.hidden = false;
12618         this.td.style.display = "";
12619     },
12620     
12621     /**
12622      * Hides this item.
12623      */
12624     hide: function(){
12625         this.hidden = true;
12626         this.td.style.display = "none";
12627     },
12628     
12629     /**
12630      * Convenience function for boolean show/hide.
12631      * @param {Boolean} visible true to show/false to hide
12632      */
12633     setVisible: function(visible){
12634         if(visible) {
12635             this.show();
12636         }else{
12637             this.hide();
12638         }
12639     },
12640     
12641     /**
12642      * Try to focus this item.
12643      */
12644     focus : function(){
12645         Roo.fly(this.el).focus();
12646     },
12647     
12648     /**
12649      * Disables this item.
12650      */
12651     disable : function(){
12652         Roo.fly(this.td).addClass("x-item-disabled");
12653         this.disabled = true;
12654         this.el.disabled = true;
12655     },
12656     
12657     /**
12658      * Enables this item.
12659      */
12660     enable : function(){
12661         Roo.fly(this.td).removeClass("x-item-disabled");
12662         this.disabled = false;
12663         this.el.disabled = false;
12664     }
12665 };
12666
12667
12668 /**
12669  * @class Roo.Toolbar.Separator
12670  * @extends Roo.Toolbar.Item
12671  * A simple toolbar separator class
12672  * @constructor
12673  * Creates a new Separator
12674  */
12675 Roo.Toolbar.Separator = function(){
12676     var s = document.createElement("span");
12677     s.className = "ytb-sep";
12678     Roo.Toolbar.Separator.superclass.constructor.call(this, s);
12679 };
12680 Roo.extend(Roo.Toolbar.Separator, Roo.Toolbar.Item, {
12681     enable:Roo.emptyFn,
12682     disable:Roo.emptyFn,
12683     focus:Roo.emptyFn
12684 });
12685
12686 /**
12687  * @class Roo.Toolbar.Spacer
12688  * @extends Roo.Toolbar.Item
12689  * A simple element that adds extra horizontal space to a toolbar.
12690  * @constructor
12691  * Creates a new Spacer
12692  */
12693 Roo.Toolbar.Spacer = function(){
12694     var s = document.createElement("div");
12695     s.className = "ytb-spacer";
12696     Roo.Toolbar.Spacer.superclass.constructor.call(this, s);
12697 };
12698 Roo.extend(Roo.Toolbar.Spacer, Roo.Toolbar.Item, {
12699     enable:Roo.emptyFn,
12700     disable:Roo.emptyFn,
12701     focus:Roo.emptyFn
12702 });
12703
12704 /**
12705  * @class Roo.Toolbar.Fill
12706  * @extends Roo.Toolbar.Spacer
12707  * A simple element that adds a greedy (100% width) horizontal space to a toolbar.
12708  * @constructor
12709  * Creates a new Spacer
12710  */
12711 Roo.Toolbar.Fill = Roo.extend(Roo.Toolbar.Spacer, {
12712     // private
12713     render : function(td){
12714         td.style.width = '100%';
12715         Roo.Toolbar.Fill.superclass.render.call(this, td);
12716     }
12717 });
12718
12719 /**
12720  * @class Roo.Toolbar.TextItem
12721  * @extends Roo.Toolbar.Item
12722  * A simple class that renders text directly into a toolbar.
12723  * @constructor
12724  * Creates a new TextItem
12725  * @param {String} text
12726  */
12727 Roo.Toolbar.TextItem = function(text){
12728     if (typeof(text) == 'object') {
12729         text = text.text;
12730     }
12731     var s = document.createElement("span");
12732     s.className = "ytb-text";
12733     s.innerHTML = text;
12734     Roo.Toolbar.TextItem.superclass.constructor.call(this, s);
12735 };
12736 Roo.extend(Roo.Toolbar.TextItem, Roo.Toolbar.Item, {
12737     enable:Roo.emptyFn,
12738     disable:Roo.emptyFn,
12739     focus:Roo.emptyFn
12740 });
12741
12742 /**
12743  * @class Roo.Toolbar.Button
12744  * @extends Roo.Button
12745  * A button that renders into a toolbar.
12746  * @constructor
12747  * Creates a new Button
12748  * @param {Object} config A standard {@link Roo.Button} config object
12749  */
12750 Roo.Toolbar.Button = function(config){
12751     Roo.Toolbar.Button.superclass.constructor.call(this, null, config);
12752 };
12753 Roo.extend(Roo.Toolbar.Button, Roo.Button, {
12754     render : function(td){
12755         this.td = td;
12756         Roo.Toolbar.Button.superclass.render.call(this, td);
12757     },
12758     
12759     /**
12760      * Removes and destroys this button
12761      */
12762     destroy : function(){
12763         Roo.Toolbar.Button.superclass.destroy.call(this);
12764         this.td.parentNode.removeChild(this.td);
12765     },
12766     
12767     /**
12768      * Shows this button
12769      */
12770     show: function(){
12771         this.hidden = false;
12772         this.td.style.display = "";
12773     },
12774     
12775     /**
12776      * Hides this button
12777      */
12778     hide: function(){
12779         this.hidden = true;
12780         this.td.style.display = "none";
12781     },
12782
12783     /**
12784      * Disables this item
12785      */
12786     disable : function(){
12787         Roo.fly(this.td).addClass("x-item-disabled");
12788         this.disabled = true;
12789     },
12790
12791     /**
12792      * Enables this item
12793      */
12794     enable : function(){
12795         Roo.fly(this.td).removeClass("x-item-disabled");
12796         this.disabled = false;
12797     }
12798 });
12799 // backwards compat
12800 Roo.ToolbarButton = Roo.Toolbar.Button;
12801
12802 /**
12803  * @class Roo.Toolbar.SplitButton
12804  * @extends Roo.SplitButton
12805  * A menu button that renders into a toolbar.
12806  * @constructor
12807  * Creates a new SplitButton
12808  * @param {Object} config A standard {@link Roo.SplitButton} config object
12809  */
12810 Roo.Toolbar.SplitButton = function(config){
12811     Roo.Toolbar.SplitButton.superclass.constructor.call(this, null, config);
12812 };
12813 Roo.extend(Roo.Toolbar.SplitButton, Roo.SplitButton, {
12814     render : function(td){
12815         this.td = td;
12816         Roo.Toolbar.SplitButton.superclass.render.call(this, td);
12817     },
12818     
12819     /**
12820      * Removes and destroys this button
12821      */
12822     destroy : function(){
12823         Roo.Toolbar.SplitButton.superclass.destroy.call(this);
12824         this.td.parentNode.removeChild(this.td);
12825     },
12826     
12827     /**
12828      * Shows this button
12829      */
12830     show: function(){
12831         this.hidden = false;
12832         this.td.style.display = "";
12833     },
12834     
12835     /**
12836      * Hides this button
12837      */
12838     hide: function(){
12839         this.hidden = true;
12840         this.td.style.display = "none";
12841     }
12842 });
12843
12844 // backwards compat
12845 Roo.Toolbar.MenuButton = Roo.Toolbar.SplitButton;/*
12846  * Based on:
12847  * Ext JS Library 1.1.1
12848  * Copyright(c) 2006-2007, Ext JS, LLC.
12849  *
12850  * Originally Released Under LGPL - original licence link has changed is not relivant.
12851  *
12852  * Fork - LGPL
12853  * <script type="text/javascript">
12854  */
12855  
12856 /**
12857  * @class Roo.PagingToolbar
12858  * @extends Roo.Toolbar
12859  * A specialized toolbar that is bound to a {@link Roo.data.Store} and provides automatic paging controls.
12860  * @constructor
12861  * Create a new PagingToolbar
12862  * @param {Object} config The config object
12863  */
12864 Roo.PagingToolbar = function(el, ds, config)
12865 {
12866     // old args format still supported... - xtype is prefered..
12867     if (typeof(el) == 'object' && el.xtype) {
12868         // created from xtype...
12869         config = el;
12870         ds = el.dataSource;
12871         el = config.container;
12872     }
12873     var items = [];
12874     if (config.items) {
12875         items = config.items;
12876         config.items = [];
12877     }
12878     
12879     Roo.PagingToolbar.superclass.constructor.call(this, el, null, config);
12880     this.ds = ds;
12881     this.cursor = 0;
12882     this.renderButtons(this.el);
12883     this.bind(ds);
12884     
12885     // supprot items array.
12886    
12887     Roo.each(items, function(e) {
12888         this.add(Roo.factory(e));
12889     },this);
12890     
12891 };
12892
12893 Roo.extend(Roo.PagingToolbar, Roo.Toolbar, {
12894     /**
12895      * @cfg {Roo.data.Store} dataSource
12896      * The underlying data store providing the paged data
12897      */
12898     /**
12899      * @cfg {String/HTMLElement/Element} container
12900      * container The id or element that will contain the toolbar
12901      */
12902     /**
12903      * @cfg {Boolean} displayInfo
12904      * True to display the displayMsg (defaults to false)
12905      */
12906     /**
12907      * @cfg {Number} pageSize
12908      * The number of records to display per page (defaults to 20)
12909      */
12910     pageSize: 20,
12911     /**
12912      * @cfg {String} displayMsg
12913      * The paging status message to display (defaults to "Displaying {start} - {end} of {total}")
12914      */
12915     displayMsg : 'Displaying {0} - {1} of {2}',
12916     /**
12917      * @cfg {String} emptyMsg
12918      * The message to display when no records are found (defaults to "No data to display")
12919      */
12920     emptyMsg : 'No data to display',
12921     /**
12922      * Customizable piece of the default paging text (defaults to "Page")
12923      * @type String
12924      */
12925     beforePageText : "Page",
12926     /**
12927      * Customizable piece of the default paging text (defaults to "of %0")
12928      * @type String
12929      */
12930     afterPageText : "of {0}",
12931     /**
12932      * Customizable piece of the default paging text (defaults to "First Page")
12933      * @type String
12934      */
12935     firstText : "First Page",
12936     /**
12937      * Customizable piece of the default paging text (defaults to "Previous Page")
12938      * @type String
12939      */
12940     prevText : "Previous Page",
12941     /**
12942      * Customizable piece of the default paging text (defaults to "Next Page")
12943      * @type String
12944      */
12945     nextText : "Next Page",
12946     /**
12947      * Customizable piece of the default paging text (defaults to "Last Page")
12948      * @type String
12949      */
12950     lastText : "Last Page",
12951     /**
12952      * Customizable piece of the default paging text (defaults to "Refresh")
12953      * @type String
12954      */
12955     refreshText : "Refresh",
12956
12957     // private
12958     renderButtons : function(el){
12959         Roo.PagingToolbar.superclass.render.call(this, el);
12960         this.first = this.addButton({
12961             tooltip: this.firstText,
12962             cls: "x-btn-icon x-grid-page-first",
12963             disabled: true,
12964             handler: this.onClick.createDelegate(this, ["first"])
12965         });
12966         this.prev = this.addButton({
12967             tooltip: this.prevText,
12968             cls: "x-btn-icon x-grid-page-prev",
12969             disabled: true,
12970             handler: this.onClick.createDelegate(this, ["prev"])
12971         });
12972         //this.addSeparator();
12973         this.add(this.beforePageText);
12974         this.field = Roo.get(this.addDom({
12975            tag: "input",
12976            type: "text",
12977            size: "3",
12978            value: "1",
12979            cls: "x-grid-page-number"
12980         }).el);
12981         this.field.on("keydown", this.onPagingKeydown, this);
12982         this.field.on("focus", function(){this.dom.select();});
12983         this.afterTextEl = this.addText(String.format(this.afterPageText, 1));
12984         this.field.setHeight(18);
12985         //this.addSeparator();
12986         this.next = this.addButton({
12987             tooltip: this.nextText,
12988             cls: "x-btn-icon x-grid-page-next",
12989             disabled: true,
12990             handler: this.onClick.createDelegate(this, ["next"])
12991         });
12992         this.last = this.addButton({
12993             tooltip: this.lastText,
12994             cls: "x-btn-icon x-grid-page-last",
12995             disabled: true,
12996             handler: this.onClick.createDelegate(this, ["last"])
12997         });
12998         //this.addSeparator();
12999         this.loading = this.addButton({
13000             tooltip: this.refreshText,
13001             cls: "x-btn-icon x-grid-loading",
13002             handler: this.onClick.createDelegate(this, ["refresh"])
13003         });
13004
13005         if(this.displayInfo){
13006             this.displayEl = Roo.fly(this.el.dom.firstChild).createChild({cls:'x-paging-info'});
13007         }
13008     },
13009
13010     // private
13011     updateInfo : function(){
13012         if(this.displayEl){
13013             var count = this.ds.getCount();
13014             var msg = count == 0 ?
13015                 this.emptyMsg :
13016                 String.format(
13017                     this.displayMsg,
13018                     this.cursor+1, this.cursor+count, this.ds.getTotalCount()    
13019                 );
13020             this.displayEl.update(msg);
13021         }
13022     },
13023
13024     // private
13025     onLoad : function(ds, r, o){
13026        this.cursor = o.params ? o.params.start : 0;
13027        var d = this.getPageData(), ap = d.activePage, ps = d.pages;
13028
13029        this.afterTextEl.el.innerHTML = String.format(this.afterPageText, d.pages);
13030        this.field.dom.value = ap;
13031        this.first.setDisabled(ap == 1);
13032        this.prev.setDisabled(ap == 1);
13033        this.next.setDisabled(ap == ps);
13034        this.last.setDisabled(ap == ps);
13035        this.loading.enable();
13036        this.updateInfo();
13037     },
13038
13039     // private
13040     getPageData : function(){
13041         var total = this.ds.getTotalCount();
13042         return {
13043             total : total,
13044             activePage : Math.ceil((this.cursor+this.pageSize)/this.pageSize),
13045             pages :  total < this.pageSize ? 1 : Math.ceil(total/this.pageSize)
13046         };
13047     },
13048
13049     // private
13050     onLoadError : function(){
13051         this.loading.enable();
13052     },
13053
13054     // private
13055     onPagingKeydown : function(e){
13056         var k = e.getKey();
13057         var d = this.getPageData();
13058         if(k == e.RETURN){
13059             var v = this.field.dom.value, pageNum;
13060             if(!v || isNaN(pageNum = parseInt(v, 10))){
13061                 this.field.dom.value = d.activePage;
13062                 return;
13063             }
13064             pageNum = Math.min(Math.max(1, pageNum), d.pages) - 1;
13065             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
13066             e.stopEvent();
13067         }
13068         else if(k == e.HOME || (k == e.UP && e.ctrlKey) || (k == e.PAGEUP && e.ctrlKey) || (k == e.RIGHT && e.ctrlKey) || k == e.END || (k == e.DOWN && e.ctrlKey) || (k == e.LEFT && e.ctrlKey) || (k == e.PAGEDOWN && e.ctrlKey))
13069         {
13070           var pageNum = (k == e.HOME || (k == e.DOWN && e.ctrlKey) || (k == e.LEFT && e.ctrlKey) || (k == e.PAGEDOWN && e.ctrlKey)) ? 1 : d.pages;
13071           this.field.dom.value = pageNum;
13072           this.ds.load({params:{start: (pageNum - 1) * this.pageSize, limit: this.pageSize}});
13073           e.stopEvent();
13074         }
13075         else if(k == e.UP || k == e.RIGHT || k == e.PAGEUP || k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN)
13076         {
13077           var v = this.field.dom.value, pageNum; 
13078           var increment = (e.shiftKey) ? 10 : 1;
13079           if(k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN)
13080             increment *= -1;
13081           if(!v || isNaN(pageNum = parseInt(v, 10))) {
13082             this.field.dom.value = d.activePage;
13083             return;
13084           }
13085           else if(parseInt(v, 10) + increment >= 1 & parseInt(v, 10) + increment <= d.pages)
13086           {
13087             this.field.dom.value = parseInt(v, 10) + increment;
13088             pageNum = Math.min(Math.max(1, pageNum + increment), d.pages) - 1;
13089             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
13090           }
13091           e.stopEvent();
13092         }
13093     },
13094
13095     // private
13096     beforeLoad : function(){
13097         if(this.loading){
13098             this.loading.disable();
13099         }
13100     },
13101
13102     // private
13103     onClick : function(which){
13104         var ds = this.ds;
13105         switch(which){
13106             case "first":
13107                 ds.load({params:{start: 0, limit: this.pageSize}});
13108             break;
13109             case "prev":
13110                 ds.load({params:{start: Math.max(0, this.cursor-this.pageSize), limit: this.pageSize}});
13111             break;
13112             case "next":
13113                 ds.load({params:{start: this.cursor+this.pageSize, limit: this.pageSize}});
13114             break;
13115             case "last":
13116                 var total = ds.getTotalCount();
13117                 var extra = total % this.pageSize;
13118                 var lastStart = extra ? (total - extra) : total-this.pageSize;
13119                 ds.load({params:{start: lastStart, limit: this.pageSize}});
13120             break;
13121             case "refresh":
13122                 ds.load({params:{start: this.cursor, limit: this.pageSize}});
13123             break;
13124         }
13125     },
13126
13127     /**
13128      * Unbinds the paging toolbar from the specified {@link Roo.data.Store}
13129      * @param {Roo.data.Store} store The data store to unbind
13130      */
13131     unbind : function(ds){
13132         ds.un("beforeload", this.beforeLoad, this);
13133         ds.un("load", this.onLoad, this);
13134         ds.un("loadexception", this.onLoadError, this);
13135         ds.un("remove", this.updateInfo, this);
13136         ds.un("add", this.updateInfo, this);
13137         this.ds = undefined;
13138     },
13139
13140     /**
13141      * Binds the paging toolbar to the specified {@link Roo.data.Store}
13142      * @param {Roo.data.Store} store The data store to bind
13143      */
13144     bind : function(ds){
13145         ds.on("beforeload", this.beforeLoad, this);
13146         ds.on("load", this.onLoad, this);
13147         ds.on("loadexception", this.onLoadError, this);
13148         ds.on("remove", this.updateInfo, this);
13149         ds.on("add", this.updateInfo, this);
13150         this.ds = ds;
13151     }
13152 });/*
13153  * Based on:
13154  * Ext JS Library 1.1.1
13155  * Copyright(c) 2006-2007, Ext JS, LLC.
13156  *
13157  * Originally Released Under LGPL - original licence link has changed is not relivant.
13158  *
13159  * Fork - LGPL
13160  * <script type="text/javascript">
13161  */
13162
13163 /**
13164  * @class Roo.Resizable
13165  * @extends Roo.util.Observable
13166  * <p>Applies drag handles to an element to make it resizable. The drag handles are inserted into the element
13167  * and positioned absolute. Some elements, such as a textarea or image, don't support this. To overcome that, you can wrap
13168  * the textarea in a div and set "resizeChild" to true (or to the id of the element), <b>or</b> set wrap:true in your config and
13169  * the element will be wrapped for you automatically.</p>
13170  * <p>Here is the list of valid resize handles:</p>
13171  * <pre>
13172 Value   Description
13173 ------  -------------------
13174  'n'     north
13175  's'     south
13176  'e'     east
13177  'w'     west
13178  'nw'    northwest
13179  'sw'    southwest
13180  'se'    southeast
13181  'ne'    northeast
13182  'hd'    horizontal drag
13183  'all'   all
13184 </pre>
13185  * <p>Here's an example showing the creation of a typical Resizable:</p>
13186  * <pre><code>
13187 var resizer = new Roo.Resizable("element-id", {
13188     handles: 'all',
13189     minWidth: 200,
13190     minHeight: 100,
13191     maxWidth: 500,
13192     maxHeight: 400,
13193     pinned: true
13194 });
13195 resizer.on("resize", myHandler);
13196 </code></pre>
13197  * <p>To hide a particular handle, set its display to none in CSS, or through script:<br>
13198  * resizer.east.setDisplayed(false);</p>
13199  * @cfg {Boolean/String/Element} resizeChild True to resize the first child, or id/element to resize (defaults to false)
13200  * @cfg {Array/String} adjustments String "auto" or an array [width, height] with values to be <b>added</b> to the
13201  * resize operation's new size (defaults to [0, 0])
13202  * @cfg {Number} minWidth The minimum width for the element (defaults to 5)
13203  * @cfg {Number} minHeight The minimum height for the element (defaults to 5)
13204  * @cfg {Number} maxWidth The maximum width for the element (defaults to 10000)
13205  * @cfg {Number} maxHeight The maximum height for the element (defaults to 10000)
13206  * @cfg {Boolean} enabled False to disable resizing (defaults to true)
13207  * @cfg {Boolean} wrap True to wrap an element with a div if needed (required for textareas and images, defaults to false)
13208  * @cfg {Number} width The width of the element in pixels (defaults to null)
13209  * @cfg {Number} height The height of the element in pixels (defaults to null)
13210  * @cfg {Boolean} animate True to animate the resize (not compatible with dynamic sizing, defaults to false)
13211  * @cfg {Number} duration Animation duration if animate = true (defaults to .35)
13212  * @cfg {Boolean} dynamic True to resize the element while dragging instead of using a proxy (defaults to false)
13213  * @cfg {String} handles String consisting of the resize handles to display (defaults to undefined)
13214  * @cfg {Boolean} multiDirectional <b>Deprecated</b>.  The old style of adding multi-direction resize handles, deprecated
13215  * in favor of the handles config option (defaults to false)
13216  * @cfg {Boolean} disableTrackOver True to disable mouse tracking. This is only applied at config time. (defaults to false)
13217  * @cfg {String} easing Animation easing if animate = true (defaults to 'easingOutStrong')
13218  * @cfg {Number} widthIncrement The increment to snap the width resize in pixels (dynamic must be true, defaults to 0)
13219  * @cfg {Number} heightIncrement The increment to snap the height resize in pixels (dynamic must be true, defaults to 0)
13220  * @cfg {Boolean} pinned True to ensure that the resize handles are always visible, false to display them only when the
13221  * user mouses over the resizable borders. This is only applied at config time. (defaults to false)
13222  * @cfg {Boolean} preserveRatio True to preserve the original ratio between height and width during resize (defaults to false)
13223  * @cfg {Boolean} transparent True for transparent handles. This is only applied at config time. (defaults to false)
13224  * @cfg {Number} minX The minimum allowed page X for the element (only used for west resizing, defaults to 0)
13225  * @cfg {Number} minY The minimum allowed page Y for the element (only used for north resizing, defaults to 0)
13226  * @cfg {Boolean} draggable Convenience to initialize drag drop (defaults to false)
13227  * @constructor
13228  * Create a new resizable component
13229  * @param {String/HTMLElement/Roo.Element} el The id or element to resize
13230  * @param {Object} config configuration options
13231   */
13232 Roo.Resizable = function(el, config)
13233 {
13234     this.el = Roo.get(el);
13235
13236     if(config && config.wrap){
13237         config.resizeChild = this.el;
13238         this.el = this.el.wrap(typeof config.wrap == "object" ? config.wrap : {cls:"xresizable-wrap"});
13239         this.el.id = this.el.dom.id = config.resizeChild.id + "-rzwrap";
13240         this.el.setStyle("overflow", "hidden");
13241         this.el.setPositioning(config.resizeChild.getPositioning());
13242         config.resizeChild.clearPositioning();
13243         if(!config.width || !config.height){
13244             var csize = config.resizeChild.getSize();
13245             this.el.setSize(csize.width, csize.height);
13246         }
13247         if(config.pinned && !config.adjustments){
13248             config.adjustments = "auto";
13249         }
13250     }
13251
13252     this.proxy = this.el.createProxy({tag: "div", cls: "x-resizable-proxy", id: this.el.id + "-rzproxy"});
13253     this.proxy.unselectable();
13254     this.proxy.enableDisplayMode('block');
13255
13256     Roo.apply(this, config);
13257
13258     if(this.pinned){
13259         this.disableTrackOver = true;
13260         this.el.addClass("x-resizable-pinned");
13261     }
13262     // if the element isn't positioned, make it relative
13263     var position = this.el.getStyle("position");
13264     if(position != "absolute" && position != "fixed"){
13265         this.el.setStyle("position", "relative");
13266     }
13267     if(!this.handles){ // no handles passed, must be legacy style
13268         this.handles = 's,e,se';
13269         if(this.multiDirectional){
13270             this.handles += ',n,w';
13271         }
13272     }
13273     if(this.handles == "all"){
13274         this.handles = "n s e w ne nw se sw";
13275     }
13276     var hs = this.handles.split(/\s*?[,;]\s*?| /);
13277     var ps = Roo.Resizable.positions;
13278     for(var i = 0, len = hs.length; i < len; i++){
13279         if(hs[i] && ps[hs[i]]){
13280             var pos = ps[hs[i]];
13281             this[pos] = new Roo.Resizable.Handle(this, pos, this.disableTrackOver, this.transparent);
13282         }
13283     }
13284     // legacy
13285     this.corner = this.southeast;
13286     
13287     // updateBox = the box can move..
13288     if(this.handles.indexOf("n") != -1 || this.handles.indexOf("w") != -1 || this.handles.indexOf("hd") != -1) {
13289         this.updateBox = true;
13290     }
13291
13292     this.activeHandle = null;
13293
13294     if(this.resizeChild){
13295         if(typeof this.resizeChild == "boolean"){
13296             this.resizeChild = Roo.get(this.el.dom.firstChild, true);
13297         }else{
13298             this.resizeChild = Roo.get(this.resizeChild, true);
13299         }
13300     }
13301     
13302     if(this.adjustments == "auto"){
13303         var rc = this.resizeChild;
13304         var hw = this.west, he = this.east, hn = this.north, hs = this.south;
13305         if(rc && (hw || hn)){
13306             rc.position("relative");
13307             rc.setLeft(hw ? hw.el.getWidth() : 0);
13308             rc.setTop(hn ? hn.el.getHeight() : 0);
13309         }
13310         this.adjustments = [
13311             (he ? -he.el.getWidth() : 0) + (hw ? -hw.el.getWidth() : 0),
13312             (hn ? -hn.el.getHeight() : 0) + (hs ? -hs.el.getHeight() : 0) -1
13313         ];
13314     }
13315
13316     if(this.draggable){
13317         this.dd = this.dynamic ?
13318             this.el.initDD(null) : this.el.initDDProxy(null, {dragElId: this.proxy.id});
13319         this.dd.setHandleElId(this.resizeChild ? this.resizeChild.id : this.el.id);
13320     }
13321
13322     // public events
13323     this.addEvents({
13324         /**
13325          * @event beforeresize
13326          * Fired before resize is allowed. Set enabled to false to cancel resize.
13327          * @param {Roo.Resizable} this
13328          * @param {Roo.EventObject} e The mousedown event
13329          */
13330         "beforeresize" : true,
13331         /**
13332          * @event resize
13333          * Fired after a resize.
13334          * @param {Roo.Resizable} this
13335          * @param {Number} width The new width
13336          * @param {Number} height The new height
13337          * @param {Roo.EventObject} e The mouseup event
13338          */
13339         "resize" : true
13340     });
13341
13342     if(this.width !== null && this.height !== null){
13343         this.resizeTo(this.width, this.height);
13344     }else{
13345         this.updateChildSize();
13346     }
13347     if(Roo.isIE){
13348         this.el.dom.style.zoom = 1;
13349     }
13350     Roo.Resizable.superclass.constructor.call(this);
13351 };
13352
13353 Roo.extend(Roo.Resizable, Roo.util.Observable, {
13354         resizeChild : false,
13355         adjustments : [0, 0],
13356         minWidth : 5,
13357         minHeight : 5,
13358         maxWidth : 10000,
13359         maxHeight : 10000,
13360         enabled : true,
13361         animate : false,
13362         duration : .35,
13363         dynamic : false,
13364         handles : false,
13365         multiDirectional : false,
13366         disableTrackOver : false,
13367         easing : 'easeOutStrong',
13368         widthIncrement : 0,
13369         heightIncrement : 0,
13370         pinned : false,
13371         width : null,
13372         height : null,
13373         preserveRatio : false,
13374         transparent: false,
13375         minX: 0,
13376         minY: 0,
13377         draggable: false,
13378
13379         /**
13380          * @cfg {String/HTMLElement/Element} constrainTo Constrain the resize to a particular element
13381          */
13382         constrainTo: undefined,
13383         /**
13384          * @cfg {Roo.lib.Region} resizeRegion Constrain the resize to a particular region
13385          */
13386         resizeRegion: undefined,
13387
13388
13389     /**
13390      * Perform a manual resize
13391      * @param {Number} width
13392      * @param {Number} height
13393      */
13394     resizeTo : function(width, height){
13395         this.el.setSize(width, height);
13396         this.updateChildSize();
13397         this.fireEvent("resize", this, width, height, null);
13398     },
13399
13400     // private
13401     startSizing : function(e, handle){
13402         this.fireEvent("beforeresize", this, e);
13403         if(this.enabled){ // 2nd enabled check in case disabled before beforeresize handler
13404
13405             if(!this.overlay){
13406                 this.overlay = this.el.createProxy({tag: "div", cls: "x-resizable-overlay", html: "&#160;"});
13407                 this.overlay.unselectable();
13408                 this.overlay.enableDisplayMode("block");
13409                 this.overlay.on("mousemove", this.onMouseMove, this);
13410                 this.overlay.on("mouseup", this.onMouseUp, this);
13411             }
13412             this.overlay.setStyle("cursor", handle.el.getStyle("cursor"));
13413
13414             this.resizing = true;
13415             this.startBox = this.el.getBox();
13416             this.startPoint = e.getXY();
13417             this.offsets = [(this.startBox.x + this.startBox.width) - this.startPoint[0],
13418                             (this.startBox.y + this.startBox.height) - this.startPoint[1]];
13419
13420             this.overlay.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
13421             this.overlay.show();
13422
13423             if(this.constrainTo) {
13424                 var ct = Roo.get(this.constrainTo);
13425                 this.resizeRegion = ct.getRegion().adjust(
13426                     ct.getFrameWidth('t'),
13427                     ct.getFrameWidth('l'),
13428                     -ct.getFrameWidth('b'),
13429                     -ct.getFrameWidth('r')
13430                 );
13431             }
13432
13433             this.proxy.setStyle('visibility', 'hidden'); // workaround display none
13434             this.proxy.show();
13435             this.proxy.setBox(this.startBox);
13436             if(!this.dynamic){
13437                 this.proxy.setStyle('visibility', 'visible');
13438             }
13439         }
13440     },
13441
13442     // private
13443     onMouseDown : function(handle, e){
13444         if(this.enabled){
13445             e.stopEvent();
13446             this.activeHandle = handle;
13447             this.startSizing(e, handle);
13448         }
13449     },
13450
13451     // private
13452     onMouseUp : function(e){
13453         var size = this.resizeElement();
13454         this.resizing = false;
13455         this.handleOut();
13456         this.overlay.hide();
13457         this.proxy.hide();
13458         this.fireEvent("resize", this, size.width, size.height, e);
13459     },
13460
13461     // private
13462     updateChildSize : function(){
13463         if(this.resizeChild){
13464             var el = this.el;
13465             var child = this.resizeChild;
13466             var adj = this.adjustments;
13467             if(el.dom.offsetWidth){
13468                 var b = el.getSize(true);
13469                 child.setSize(b.width+adj[0], b.height+adj[1]);
13470             }
13471             // Second call here for IE
13472             // The first call enables instant resizing and
13473             // the second call corrects scroll bars if they
13474             // exist
13475             if(Roo.isIE){
13476                 setTimeout(function(){
13477                     if(el.dom.offsetWidth){
13478                         var b = el.getSize(true);
13479                         child.setSize(b.width+adj[0], b.height+adj[1]);
13480                     }
13481                 }, 10);
13482             }
13483         }
13484     },
13485
13486     // private
13487     snap : function(value, inc, min){
13488         if(!inc || !value) return value;
13489         var newValue = value;
13490         var m = value % inc;
13491         if(m > 0){
13492             if(m > (inc/2)){
13493                 newValue = value + (inc-m);
13494             }else{
13495                 newValue = value - m;
13496             }
13497         }
13498         return Math.max(min, newValue);
13499     },
13500
13501     // private
13502     resizeElement : function(){
13503         var box = this.proxy.getBox();
13504         if(this.updateBox){
13505             this.el.setBox(box, false, this.animate, this.duration, null, this.easing);
13506         }else{
13507             this.el.setSize(box.width, box.height, this.animate, this.duration, null, this.easing);
13508         }
13509         this.updateChildSize();
13510         if(!this.dynamic){
13511             this.proxy.hide();
13512         }
13513         return box;
13514     },
13515
13516     // private
13517     constrain : function(v, diff, m, mx){
13518         if(v - diff < m){
13519             diff = v - m;
13520         }else if(v - diff > mx){
13521             diff = mx - v;
13522         }
13523         return diff;
13524     },
13525
13526     // private
13527     onMouseMove : function(e){
13528         if(this.enabled){
13529             try{// try catch so if something goes wrong the user doesn't get hung
13530
13531             if(this.resizeRegion && !this.resizeRegion.contains(e.getPoint())) {
13532                 return;
13533             }
13534
13535             //var curXY = this.startPoint;
13536             var curSize = this.curSize || this.startBox;
13537             var x = this.startBox.x, y = this.startBox.y;
13538             var ox = x, oy = y;
13539             var w = curSize.width, h = curSize.height;
13540             var ow = w, oh = h;
13541             var mw = this.minWidth, mh = this.minHeight;
13542             var mxw = this.maxWidth, mxh = this.maxHeight;
13543             var wi = this.widthIncrement;
13544             var hi = this.heightIncrement;
13545
13546             var eventXY = e.getXY();
13547             var diffX = -(this.startPoint[0] - Math.max(this.minX, eventXY[0]));
13548             var diffY = -(this.startPoint[1] - Math.max(this.minY, eventXY[1]));
13549
13550             var pos = this.activeHandle.position;
13551
13552             switch(pos){
13553                 case "east":
13554                     w += diffX;
13555                     w = Math.min(Math.max(mw, w), mxw);
13556                     break;
13557              
13558                 case "south":
13559                     h += diffY;
13560                     h = Math.min(Math.max(mh, h), mxh);
13561                     break;
13562                 case "southeast":
13563                     w += diffX;
13564                     h += diffY;
13565                     w = Math.min(Math.max(mw, w), mxw);
13566                     h = Math.min(Math.max(mh, h), mxh);
13567                     break;
13568                 case "north":
13569                     diffY = this.constrain(h, diffY, mh, mxh);
13570                     y += diffY;
13571                     h -= diffY;
13572                     break;
13573                 case "hdrag":
13574                     
13575                     if (wi) {
13576                         var adiffX = Math.abs(diffX);
13577                         var sub = (adiffX % wi); // how much 
13578                         if (sub > (wi/2)) { // far enough to snap
13579                             diffX = (diffX > 0) ? diffX-sub + wi : diffX+sub - wi;
13580                         } else {
13581                             // remove difference.. 
13582                             diffX = (diffX > 0) ? diffX-sub : diffX+sub;
13583                         }
13584                     }
13585                     x += diffX;
13586                     x = Math.max(this.minX, x);
13587                     break;
13588                 case "west":
13589                     diffX = this.constrain(w, diffX, mw, mxw);
13590                     x += diffX;
13591                     w -= diffX;
13592                     break;
13593                 case "northeast":
13594                     w += diffX;
13595                     w = Math.min(Math.max(mw, w), mxw);
13596                     diffY = this.constrain(h, diffY, mh, mxh);
13597                     y += diffY;
13598                     h -= diffY;
13599                     break;
13600                 case "northwest":
13601                     diffX = this.constrain(w, diffX, mw, mxw);
13602                     diffY = this.constrain(h, diffY, mh, mxh);
13603                     y += diffY;
13604                     h -= diffY;
13605                     x += diffX;
13606                     w -= diffX;
13607                     break;
13608                case "southwest":
13609                     diffX = this.constrain(w, diffX, mw, mxw);
13610                     h += diffY;
13611                     h = Math.min(Math.max(mh, h), mxh);
13612                     x += diffX;
13613                     w -= diffX;
13614                     break;
13615             }
13616
13617             var sw = this.snap(w, wi, mw);
13618             var sh = this.snap(h, hi, mh);
13619             if(sw != w || sh != h){
13620                 switch(pos){
13621                     case "northeast":
13622                         y -= sh - h;
13623                     break;
13624                     case "north":
13625                         y -= sh - h;
13626                         break;
13627                     case "southwest":
13628                         x -= sw - w;
13629                     break;
13630                     case "west":
13631                         x -= sw - w;
13632                         break;
13633                     case "northwest":
13634                         x -= sw - w;
13635                         y -= sh - h;
13636                     break;
13637                 }
13638                 w = sw;
13639                 h = sh;
13640             }
13641
13642             if(this.preserveRatio){
13643                 switch(pos){
13644                     case "southeast":
13645                     case "east":
13646                         h = oh * (w/ow);
13647                         h = Math.min(Math.max(mh, h), mxh);
13648                         w = ow * (h/oh);
13649                        break;
13650                     case "south":
13651                         w = ow * (h/oh);
13652                         w = Math.min(Math.max(mw, w), mxw);
13653                         h = oh * (w/ow);
13654                         break;
13655                     case "northeast":
13656                         w = ow * (h/oh);
13657                         w = Math.min(Math.max(mw, w), mxw);
13658                         h = oh * (w/ow);
13659                     break;
13660                     case "north":
13661                         var tw = w;
13662                         w = ow * (h/oh);
13663                         w = Math.min(Math.max(mw, w), mxw);
13664                         h = oh * (w/ow);
13665                         x += (tw - w) / 2;
13666                         break;
13667                     case "southwest":
13668                         h = oh * (w/ow);
13669                         h = Math.min(Math.max(mh, h), mxh);
13670                         var tw = w;
13671                         w = ow * (h/oh);
13672                         x += tw - w;
13673                         break;
13674                     case "west":
13675                         var th = h;
13676                         h = oh * (w/ow);
13677                         h = Math.min(Math.max(mh, h), mxh);
13678                         y += (th - h) / 2;
13679                         var tw = w;
13680                         w = ow * (h/oh);
13681                         x += tw - w;
13682                        break;
13683                     case "northwest":
13684                         var tw = w;
13685                         var th = h;
13686                         h = oh * (w/ow);
13687                         h = Math.min(Math.max(mh, h), mxh);
13688                         w = ow * (h/oh);
13689                         y += th - h;
13690                         x += tw - w;
13691                        break;
13692
13693                 }
13694             }
13695             if (pos == 'hdrag') {
13696                 w = ow;
13697             }
13698             this.proxy.setBounds(x, y, w, h);
13699             if(this.dynamic){
13700                 this.resizeElement();
13701             }
13702             }catch(e){}
13703         }
13704     },
13705
13706     // private
13707     handleOver : function(){
13708         if(this.enabled){
13709             this.el.addClass("x-resizable-over");
13710         }
13711     },
13712
13713     // private
13714     handleOut : function(){
13715         if(!this.resizing){
13716             this.el.removeClass("x-resizable-over");
13717         }
13718     },
13719
13720     /**
13721      * Returns the element this component is bound to.
13722      * @return {Roo.Element}
13723      */
13724     getEl : function(){
13725         return this.el;
13726     },
13727
13728     /**
13729      * Returns the resizeChild element (or null).
13730      * @return {Roo.Element}
13731      */
13732     getResizeChild : function(){
13733         return this.resizeChild;
13734     },
13735
13736     /**
13737      * Destroys this resizable. If the element was wrapped and
13738      * removeEl is not true then the element remains.
13739      * @param {Boolean} removeEl (optional) true to remove the element from the DOM
13740      */
13741     destroy : function(removeEl){
13742         this.proxy.remove();
13743         if(this.overlay){
13744             this.overlay.removeAllListeners();
13745             this.overlay.remove();
13746         }
13747         var ps = Roo.Resizable.positions;
13748         for(var k in ps){
13749             if(typeof ps[k] != "function" && this[ps[k]]){
13750                 var h = this[ps[k]];
13751                 h.el.removeAllListeners();
13752                 h.el.remove();
13753             }
13754         }
13755         if(removeEl){
13756             this.el.update("");
13757             this.el.remove();
13758         }
13759     }
13760 });
13761
13762 // private
13763 // hash to map config positions to true positions
13764 Roo.Resizable.positions = {
13765     n: "north", s: "south", e: "east", w: "west", se: "southeast", sw: "southwest", nw: "northwest", ne: "northeast", 
13766     hd: "hdrag"
13767 };
13768
13769 // private
13770 Roo.Resizable.Handle = function(rz, pos, disableTrackOver, transparent){
13771     if(!this.tpl){
13772         // only initialize the template if resizable is used
13773         var tpl = Roo.DomHelper.createTemplate(
13774             {tag: "div", cls: "x-resizable-handle x-resizable-handle-{0}"}
13775         );
13776         tpl.compile();
13777         Roo.Resizable.Handle.prototype.tpl = tpl;
13778     }
13779     this.position = pos;
13780     this.rz = rz;
13781     // show north drag fro topdra
13782     var handlepos = pos == 'hdrag' ? 'north' : pos;
13783     
13784     this.el = this.tpl.append(rz.el.dom, [handlepos], true);
13785     if (pos == 'hdrag') {
13786         this.el.setStyle('cursor', 'pointer');
13787     }
13788     this.el.unselectable();
13789     if(transparent){
13790         this.el.setOpacity(0);
13791     }
13792     this.el.on("mousedown", this.onMouseDown, this);
13793     if(!disableTrackOver){
13794         this.el.on("mouseover", this.onMouseOver, this);
13795         this.el.on("mouseout", this.onMouseOut, this);
13796     }
13797 };
13798
13799 // private
13800 Roo.Resizable.Handle.prototype = {
13801     afterResize : function(rz){
13802         // do nothing
13803     },
13804     // private
13805     onMouseDown : function(e){
13806         this.rz.onMouseDown(this, e);
13807     },
13808     // private
13809     onMouseOver : function(e){
13810         this.rz.handleOver(this, e);
13811     },
13812     // private
13813     onMouseOut : function(e){
13814         this.rz.handleOut(this, e);
13815     }
13816 };/*
13817  * Based on:
13818  * Ext JS Library 1.1.1
13819  * Copyright(c) 2006-2007, Ext JS, LLC.
13820  *
13821  * Originally Released Under LGPL - original licence link has changed is not relivant.
13822  *
13823  * Fork - LGPL
13824  * <script type="text/javascript">
13825  */
13826
13827 /**
13828  * @class Roo.Editor
13829  * @extends Roo.Component
13830  * A base editor field that handles displaying/hiding on demand and has some built-in sizing and event handling logic.
13831  * @constructor
13832  * Create a new Editor
13833  * @param {Roo.form.Field} field The Field object (or descendant)
13834  * @param {Object} config The config object
13835  */
13836 Roo.Editor = function(field, config){
13837     Roo.Editor.superclass.constructor.call(this, config);
13838     this.field = field;
13839     this.addEvents({
13840         /**
13841              * @event beforestartedit
13842              * Fires when editing is initiated, but before the value changes.  Editing can be canceled by returning
13843              * false from the handler of this event.
13844              * @param {Editor} this
13845              * @param {Roo.Element} boundEl The underlying element bound to this editor
13846              * @param {Mixed} value The field value being set
13847              */
13848         "beforestartedit" : true,
13849         /**
13850              * @event startedit
13851              * Fires when this editor is displayed
13852              * @param {Roo.Element} boundEl The underlying element bound to this editor
13853              * @param {Mixed} value The starting field value
13854              */
13855         "startedit" : true,
13856         /**
13857              * @event beforecomplete
13858              * Fires after a change has been made to the field, but before the change is reflected in the underlying
13859              * field.  Saving the change to the field can be canceled by returning false from the handler of this event.
13860              * Note that if the value has not changed and ignoreNoChange = true, the editing will still end but this
13861              * event will not fire since no edit actually occurred.
13862              * @param {Editor} this
13863              * @param {Mixed} value The current field value
13864              * @param {Mixed} startValue The original field value
13865              */
13866         "beforecomplete" : true,
13867         /**
13868              * @event complete
13869              * Fires after editing is complete and any changed value has been written to the underlying field.
13870              * @param {Editor} this
13871              * @param {Mixed} value The current field value
13872              * @param {Mixed} startValue The original field value
13873              */
13874         "complete" : true,
13875         /**
13876          * @event specialkey
13877          * Fires when any key related to navigation (arrows, tab, enter, esc, etc.) is pressed.  You can check
13878          * {@link Roo.EventObject#getKey} to determine which key was pressed.
13879          * @param {Roo.form.Field} this
13880          * @param {Roo.EventObject} e The event object
13881          */
13882         "specialkey" : true
13883     });
13884 };
13885
13886 Roo.extend(Roo.Editor, Roo.Component, {
13887     /**
13888      * @cfg {Boolean/String} autosize
13889      * True for the editor to automatically adopt the size of the underlying field, "width" to adopt the width only,
13890      * or "height" to adopt the height only (defaults to false)
13891      */
13892     /**
13893      * @cfg {Boolean} revertInvalid
13894      * True to automatically revert the field value and cancel the edit when the user completes an edit and the field
13895      * validation fails (defaults to true)
13896      */
13897     /**
13898      * @cfg {Boolean} ignoreNoChange
13899      * True to skip the the edit completion process (no save, no events fired) if the user completes an edit and
13900      * the value has not changed (defaults to false).  Applies only to string values - edits for other data types
13901      * will never be ignored.
13902      */
13903     /**
13904      * @cfg {Boolean} hideEl
13905      * False to keep the bound element visible while the editor is displayed (defaults to true)
13906      */
13907     /**
13908      * @cfg {Mixed} value
13909      * The data value of the underlying field (defaults to "")
13910      */
13911     value : "",
13912     /**
13913      * @cfg {String} alignment
13914      * The position to align to (see {@link Roo.Element#alignTo} for more details, defaults to "c-c?").
13915      */
13916     alignment: "c-c?",
13917     /**
13918      * @cfg {Boolean/String} shadow "sides" for sides/bottom only, "frame" for 4-way shadow, and "drop"
13919      * for bottom-right shadow (defaults to "frame")
13920      */
13921     shadow : "frame",
13922     /**
13923      * @cfg {Boolean} constrain True to constrain the editor to the viewport
13924      */
13925     constrain : false,
13926     /**
13927      * @cfg {Boolean} completeOnEnter True to complete the edit when the enter key is pressed (defaults to false)
13928      */
13929     completeOnEnter : false,
13930     /**
13931      * @cfg {Boolean} cancelOnEsc True to cancel the edit when the escape key is pressed (defaults to false)
13932      */
13933     cancelOnEsc : false,
13934     /**
13935      * @cfg {Boolean} updateEl True to update the innerHTML of the bound element when the update completes (defaults to false)
13936      */
13937     updateEl : false,
13938
13939     // private
13940     onRender : function(ct, position){
13941         this.el = new Roo.Layer({
13942             shadow: this.shadow,
13943             cls: "x-editor",
13944             parentEl : ct,
13945             shim : this.shim,
13946             shadowOffset:4,
13947             id: this.id,
13948             constrain: this.constrain
13949         });
13950         this.el.setStyle("overflow", Roo.isGecko ? "auto" : "hidden");
13951         if(this.field.msgTarget != 'title'){
13952             this.field.msgTarget = 'qtip';
13953         }
13954         this.field.render(this.el);
13955         if(Roo.isGecko){
13956             this.field.el.dom.setAttribute('autocomplete', 'off');
13957         }
13958         this.field.on("specialkey", this.onSpecialKey, this);
13959         if(this.swallowKeys){
13960             this.field.el.swallowEvent(['keydown','keypress']);
13961         }
13962         this.field.show();
13963         this.field.on("blur", this.onBlur, this);
13964         if(this.field.grow){
13965             this.field.on("autosize", this.el.sync,  this.el, {delay:1});
13966         }
13967     },
13968
13969     onSpecialKey : function(field, e)
13970     {
13971         //Roo.log('editor onSpecialKey');
13972         if(this.completeOnEnter && e.getKey() == e.ENTER){
13973             e.stopEvent();
13974             this.completeEdit();
13975             return;
13976         }
13977         // do not fire special key otherwise it might hide close the editor...
13978         if(e.getKey() == e.ENTER){    
13979             return;
13980         }
13981         if(this.cancelOnEsc && e.getKey() == e.ESC){
13982             this.cancelEdit();
13983             return;
13984         } 
13985         this.fireEvent('specialkey', field, e);
13986     
13987     },
13988
13989     /**
13990      * Starts the editing process and shows the editor.
13991      * @param {String/HTMLElement/Element} el The element to edit
13992      * @param {String} value (optional) A value to initialize the editor with. If a value is not provided, it defaults
13993       * to the innerHTML of el.
13994      */
13995     startEdit : function(el, value){
13996         if(this.editing){
13997             this.completeEdit();
13998         }
13999         this.boundEl = Roo.get(el);
14000         var v = value !== undefined ? value : this.boundEl.dom.innerHTML;
14001         if(!this.rendered){
14002             this.render(this.parentEl || document.body);
14003         }
14004         if(this.fireEvent("beforestartedit", this, this.boundEl, v) === false){
14005             return;
14006         }
14007         this.startValue = v;
14008         this.field.setValue(v);
14009         if(this.autoSize){
14010             var sz = this.boundEl.getSize();
14011             switch(this.autoSize){
14012                 case "width":
14013                 this.setSize(sz.width,  "");
14014                 break;
14015                 case "height":
14016                 this.setSize("",  sz.height);
14017                 break;
14018                 default:
14019                 this.setSize(sz.width,  sz.height);
14020             }
14021         }
14022         this.el.alignTo(this.boundEl, this.alignment);
14023         this.editing = true;
14024         if(Roo.QuickTips){
14025             Roo.QuickTips.disable();
14026         }
14027         this.show();
14028     },
14029
14030     /**
14031      * Sets the height and width of this editor.
14032      * @param {Number} width The new width
14033      * @param {Number} height The new height
14034      */
14035     setSize : function(w, h){
14036         this.field.setSize(w, h);
14037         if(this.el){
14038             this.el.sync();
14039         }
14040     },
14041
14042     /**
14043      * Realigns the editor to the bound field based on the current alignment config value.
14044      */
14045     realign : function(){
14046         this.el.alignTo(this.boundEl, this.alignment);
14047     },
14048
14049     /**
14050      * Ends the editing process, persists the changed value to the underlying field, and hides the editor.
14051      * @param {Boolean} remainVisible Override the default behavior and keep the editor visible after edit (defaults to false)
14052      */
14053     completeEdit : function(remainVisible){
14054         if(!this.editing){
14055             return;
14056         }
14057         var v = this.getValue();
14058         if(this.revertInvalid !== false && !this.field.isValid()){
14059             v = this.startValue;
14060             this.cancelEdit(true);
14061         }
14062         if(String(v) === String(this.startValue) && this.ignoreNoChange){
14063             this.editing = false;
14064             this.hide();
14065             return;
14066         }
14067         if(this.fireEvent("beforecomplete", this, v, this.startValue) !== false){
14068             this.editing = false;
14069             if(this.updateEl && this.boundEl){
14070                 this.boundEl.update(v);
14071             }
14072             if(remainVisible !== true){
14073                 this.hide();
14074             }
14075             this.fireEvent("complete", this, v, this.startValue);
14076         }
14077     },
14078
14079     // private
14080     onShow : function(){
14081         this.el.show();
14082         if(this.hideEl !== false){
14083             this.boundEl.hide();
14084         }
14085         this.field.show();
14086         if(Roo.isIE && !this.fixIEFocus){ // IE has problems with focusing the first time
14087             this.fixIEFocus = true;
14088             this.deferredFocus.defer(50, this);
14089         }else{
14090             this.field.focus();
14091         }
14092         this.fireEvent("startedit", this.boundEl, this.startValue);
14093     },
14094
14095     deferredFocus : function(){
14096         if(this.editing){
14097             this.field.focus();
14098         }
14099     },
14100
14101     /**
14102      * Cancels the editing process and hides the editor without persisting any changes.  The field value will be
14103      * reverted to the original starting value.
14104      * @param {Boolean} remainVisible Override the default behavior and keep the editor visible after
14105      * cancel (defaults to false)
14106      */
14107     cancelEdit : function(remainVisible){
14108         if(this.editing){
14109             this.setValue(this.startValue);
14110             if(remainVisible !== true){
14111                 this.hide();
14112             }
14113         }
14114     },
14115
14116     // private
14117     onBlur : function(){
14118         if(this.allowBlur !== true && this.editing){
14119             this.completeEdit();
14120         }
14121     },
14122
14123     // private
14124     onHide : function(){
14125         if(this.editing){
14126             this.completeEdit();
14127             return;
14128         }
14129         this.field.blur();
14130         if(this.field.collapse){
14131             this.field.collapse();
14132         }
14133         this.el.hide();
14134         if(this.hideEl !== false){
14135             this.boundEl.show();
14136         }
14137         if(Roo.QuickTips){
14138             Roo.QuickTips.enable();
14139         }
14140     },
14141
14142     /**
14143      * Sets the data value of the editor
14144      * @param {Mixed} value Any valid value supported by the underlying field
14145      */
14146     setValue : function(v){
14147         this.field.setValue(v);
14148     },
14149
14150     /**
14151      * Gets the data value of the editor
14152      * @return {Mixed} The data value
14153      */
14154     getValue : function(){
14155         return this.field.getValue();
14156     }
14157 });/*
14158  * Based on:
14159  * Ext JS Library 1.1.1
14160  * Copyright(c) 2006-2007, Ext JS, LLC.
14161  *
14162  * Originally Released Under LGPL - original licence link has changed is not relivant.
14163  *
14164  * Fork - LGPL
14165  * <script type="text/javascript">
14166  */
14167  
14168 /**
14169  * @class Roo.BasicDialog
14170  * @extends Roo.util.Observable
14171  * Lightweight Dialog Class.  The code below shows the creation of a typical dialog using existing HTML markup:
14172  * <pre><code>
14173 var dlg = new Roo.BasicDialog("my-dlg", {
14174     height: 200,
14175     width: 300,
14176     minHeight: 100,
14177     minWidth: 150,
14178     modal: true,
14179     proxyDrag: true,
14180     shadow: true
14181 });
14182 dlg.addKeyListener(27, dlg.hide, dlg); // ESC can also close the dialog
14183 dlg.addButton('OK', dlg.hide, dlg);    // Could call a save function instead of hiding
14184 dlg.addButton('Cancel', dlg.hide, dlg);
14185 dlg.show();
14186 </code></pre>
14187   <b>A Dialog should always be a direct child of the body element.</b>
14188  * @cfg {Boolean/DomHelper} autoCreate True to auto create from scratch, or using a DomHelper Object (defaults to false)
14189  * @cfg {String} title Default text to display in the title bar (defaults to null)
14190  * @cfg {Number} width Width of the dialog in pixels (can also be set via CSS).  Determined by browser if unspecified.
14191  * @cfg {Number} height Height of the dialog in pixels (can also be set via CSS).  Determined by browser if unspecified.
14192  * @cfg {Number} x The default left page coordinate of the dialog (defaults to center screen)
14193  * @cfg {Number} y The default top page coordinate of the dialog (defaults to center screen)
14194  * @cfg {String/Element} animateTarget Id or element from which the dialog should animate while opening
14195  * (defaults to null with no animation)
14196  * @cfg {Boolean} resizable False to disable manual dialog resizing (defaults to true)
14197  * @cfg {String} resizeHandles Which resize handles to display - see the {@link Roo.Resizable} handles config
14198  * property for valid values (defaults to 'all')
14199  * @cfg {Number} minHeight The minimum allowable height for a resizable dialog (defaults to 80)
14200  * @cfg {Number} minWidth The minimum allowable width for a resizable dialog (defaults to 200)
14201  * @cfg {Boolean} modal True to show the dialog modally, preventing user interaction with the rest of the page (defaults to false)
14202  * @cfg {Boolean} autoScroll True to allow the dialog body contents to overflow and display scrollbars (defaults to false)
14203  * @cfg {Boolean} closable False to remove the built-in top-right corner close button (defaults to true)
14204  * @cfg {Boolean} collapsible False to remove the built-in top-right corner collapse button (defaults to true)
14205  * @cfg {Boolean} constraintoviewport True to keep the dialog constrained within the visible viewport boundaries (defaults to true)
14206  * @cfg {Boolean} syncHeightBeforeShow True to cause the dimensions to be recalculated before the dialog is shown (defaults to false)
14207  * @cfg {Boolean} draggable False to disable dragging of the dialog within the viewport (defaults to true)
14208  * @cfg {Boolean} autoTabs If true, all elements with class 'x-dlg-tab' will get automatically converted to tabs (defaults to false)
14209  * @cfg {String} tabTag The tag name of tab elements, used when autoTabs = true (defaults to 'div')
14210  * @cfg {Boolean} proxyDrag True to drag a lightweight proxy element rather than the dialog itself, used when
14211  * draggable = true (defaults to false)
14212  * @cfg {Boolean} fixedcenter True to ensure that anytime the dialog is shown or resized it gets centered (defaults to false)
14213  * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
14214  * shadow (defaults to false)
14215  * @cfg {Number} shadowOffset The number of pixels to offset the shadow if displayed (defaults to 5)
14216  * @cfg {String} buttonAlign Valid values are "left," "center" and "right" (defaults to "right")
14217  * @cfg {Number} minButtonWidth Minimum width of all dialog buttons (defaults to 75)
14218  * @cfg {Array} buttons Array of buttons
14219  * @cfg {Boolean} shim True to create an iframe shim that prevents selects from showing through (defaults to false)
14220  * @constructor
14221  * Create a new BasicDialog.
14222  * @param {String/HTMLElement/Roo.Element} el The container element or DOM node, or its id
14223  * @param {Object} config Configuration options
14224  */
14225 Roo.BasicDialog = function(el, config){
14226     this.el = Roo.get(el);
14227     var dh = Roo.DomHelper;
14228     if(!this.el && config && config.autoCreate){
14229         if(typeof config.autoCreate == "object"){
14230             if(!config.autoCreate.id){
14231                 config.autoCreate.id = el;
14232             }
14233             this.el = dh.append(document.body,
14234                         config.autoCreate, true);
14235         }else{
14236             this.el = dh.append(document.body,
14237                         {tag: "div", id: el, style:'visibility:hidden;'}, true);
14238         }
14239     }
14240     el = this.el;
14241     el.setDisplayed(true);
14242     el.hide = this.hideAction;
14243     this.id = el.id;
14244     el.addClass("x-dlg");
14245
14246     Roo.apply(this, config);
14247
14248     this.proxy = el.createProxy("x-dlg-proxy");
14249     this.proxy.hide = this.hideAction;
14250     this.proxy.setOpacity(.5);
14251     this.proxy.hide();
14252
14253     if(config.width){
14254         el.setWidth(config.width);
14255     }
14256     if(config.height){
14257         el.setHeight(config.height);
14258     }
14259     this.size = el.getSize();
14260     if(typeof config.x != "undefined" && typeof config.y != "undefined"){
14261         this.xy = [config.x,config.y];
14262     }else{
14263         this.xy = el.getCenterXY(true);
14264     }
14265     /** The header element @type Roo.Element */
14266     this.header = el.child("> .x-dlg-hd");
14267     /** The body element @type Roo.Element */
14268     this.body = el.child("> .x-dlg-bd");
14269     /** The footer element @type Roo.Element */
14270     this.footer = el.child("> .x-dlg-ft");
14271
14272     if(!this.header){
14273         this.header = el.createChild({tag: "div", cls:"x-dlg-hd", html: "&#160;"}, this.body ? this.body.dom : null);
14274     }
14275     if(!this.body){
14276         this.body = el.createChild({tag: "div", cls:"x-dlg-bd"});
14277     }
14278
14279     this.header.unselectable();
14280     if(this.title){
14281         this.header.update(this.title);
14282     }
14283     // this element allows the dialog to be focused for keyboard event
14284     this.focusEl = el.createChild({tag: "a", href:"#", cls:"x-dlg-focus", tabIndex:"-1"});
14285     this.focusEl.swallowEvent("click", true);
14286
14287     this.header.wrap({cls:"x-dlg-hd-right"}).wrap({cls:"x-dlg-hd-left"}, true);
14288
14289     // wrap the body and footer for special rendering
14290     this.bwrap = this.body.wrap({tag: "div", cls:"x-dlg-dlg-body"});
14291     if(this.footer){
14292         this.bwrap.dom.appendChild(this.footer.dom);
14293     }
14294
14295     this.bg = this.el.createChild({
14296         tag: "div", cls:"x-dlg-bg",
14297         html: '<div class="x-dlg-bg-left"><div class="x-dlg-bg-right"><div class="x-dlg-bg-center">&#160;</div></div></div>'
14298     });
14299     this.centerBg = this.bg.child("div.x-dlg-bg-center");
14300
14301
14302     if(this.autoScroll !== false && !this.autoTabs){
14303         this.body.setStyle("overflow", "auto");
14304     }
14305
14306     this.toolbox = this.el.createChild({cls: "x-dlg-toolbox"});
14307
14308     if(this.closable !== false){
14309         this.el.addClass("x-dlg-closable");
14310         this.close = this.toolbox.createChild({cls:"x-dlg-close"});
14311         this.close.on("click", this.closeClick, this);
14312         this.close.addClassOnOver("x-dlg-close-over");
14313     }
14314     if(this.collapsible !== false){
14315         this.collapseBtn = this.toolbox.createChild({cls:"x-dlg-collapse"});
14316         this.collapseBtn.on("click", this.collapseClick, this);
14317         this.collapseBtn.addClassOnOver("x-dlg-collapse-over");
14318         this.header.on("dblclick", this.collapseClick, this);
14319     }
14320     if(this.resizable !== false){
14321         this.el.addClass("x-dlg-resizable");
14322         this.resizer = new Roo.Resizable(el, {
14323             minWidth: this.minWidth || 80,
14324             minHeight:this.minHeight || 80,
14325             handles: this.resizeHandles || "all",
14326             pinned: true
14327         });
14328         this.resizer.on("beforeresize", this.beforeResize, this);
14329         this.resizer.on("resize", this.onResize, this);
14330     }
14331     if(this.draggable !== false){
14332         el.addClass("x-dlg-draggable");
14333         if (!this.proxyDrag) {
14334             var dd = new Roo.dd.DD(el.dom.id, "WindowDrag");
14335         }
14336         else {
14337             var dd = new Roo.dd.DDProxy(el.dom.id, "WindowDrag", {dragElId: this.proxy.id});
14338         }
14339         dd.setHandleElId(this.header.id);
14340         dd.endDrag = this.endMove.createDelegate(this);
14341         dd.startDrag = this.startMove.createDelegate(this);
14342         dd.onDrag = this.onDrag.createDelegate(this);
14343         dd.scroll = false;
14344         this.dd = dd;
14345     }
14346     if(this.modal){
14347         this.mask = dh.append(document.body, {tag: "div", cls:"x-dlg-mask"}, true);
14348         this.mask.enableDisplayMode("block");
14349         this.mask.hide();
14350         this.el.addClass("x-dlg-modal");
14351     }
14352     if(this.shadow){
14353         this.shadow = new Roo.Shadow({
14354             mode : typeof this.shadow == "string" ? this.shadow : "sides",
14355             offset : this.shadowOffset
14356         });
14357     }else{
14358         this.shadowOffset = 0;
14359     }
14360     if(Roo.useShims && this.shim !== false){
14361         this.shim = this.el.createShim();
14362         this.shim.hide = this.hideAction;
14363         this.shim.hide();
14364     }else{
14365         this.shim = false;
14366     }
14367     if(this.autoTabs){
14368         this.initTabs();
14369     }
14370     if (this.buttons) { 
14371         var bts= this.buttons;
14372         this.buttons = [];
14373         Roo.each(bts, function(b) {
14374             this.addButton(b);
14375         }, this);
14376     }
14377     
14378     
14379     this.addEvents({
14380         /**
14381          * @event keydown
14382          * Fires when a key is pressed
14383          * @param {Roo.BasicDialog} this
14384          * @param {Roo.EventObject} e
14385          */
14386         "keydown" : true,
14387         /**
14388          * @event move
14389          * Fires when this dialog is moved by the user.
14390          * @param {Roo.BasicDialog} this
14391          * @param {Number} x The new page X
14392          * @param {Number} y The new page Y
14393          */
14394         "move" : true,
14395         /**
14396          * @event resize
14397          * Fires when this dialog is resized by the user.
14398          * @param {Roo.BasicDialog} this
14399          * @param {Number} width The new width
14400          * @param {Number} height The new height
14401          */
14402         "resize" : true,
14403         /**
14404          * @event beforehide
14405          * Fires before this dialog is hidden.
14406          * @param {Roo.BasicDialog} this
14407          */
14408         "beforehide" : true,
14409         /**
14410          * @event hide
14411          * Fires when this dialog is hidden.
14412          * @param {Roo.BasicDialog} this
14413          */
14414         "hide" : true,
14415         /**
14416          * @event beforeshow
14417          * Fires before this dialog is shown.
14418          * @param {Roo.BasicDialog} this
14419          */
14420         "beforeshow" : true,
14421         /**
14422          * @event show
14423          * Fires when this dialog is shown.
14424          * @param {Roo.BasicDialog} this
14425          */
14426         "show" : true
14427     });
14428     el.on("keydown", this.onKeyDown, this);
14429     el.on("mousedown", this.toFront, this);
14430     Roo.EventManager.onWindowResize(this.adjustViewport, this, true);
14431     this.el.hide();
14432     Roo.DialogManager.register(this);
14433     Roo.BasicDialog.superclass.constructor.call(this);
14434 };
14435
14436 Roo.extend(Roo.BasicDialog, Roo.util.Observable, {
14437     shadowOffset: Roo.isIE ? 6 : 5,
14438     minHeight: 80,
14439     minWidth: 200,
14440     minButtonWidth: 75,
14441     defaultButton: null,
14442     buttonAlign: "right",
14443     tabTag: 'div',
14444     firstShow: true,
14445
14446     /**
14447      * Sets the dialog title text
14448      * @param {String} text The title text to display
14449      * @return {Roo.BasicDialog} this
14450      */
14451     setTitle : function(text){
14452         this.header.update(text);
14453         return this;
14454     },
14455
14456     // private
14457     closeClick : function(){
14458         this.hide();
14459     },
14460
14461     // private
14462     collapseClick : function(){
14463         this[this.collapsed ? "expand" : "collapse"]();
14464     },
14465
14466     /**
14467      * Collapses the dialog to its minimized state (only the title bar is visible).
14468      * Equivalent to the user clicking the collapse dialog button.
14469      */
14470     collapse : function(){
14471         if(!this.collapsed){
14472             this.collapsed = true;
14473             this.el.addClass("x-dlg-collapsed");
14474             this.restoreHeight = this.el.getHeight();
14475             this.resizeTo(this.el.getWidth(), this.header.getHeight());
14476         }
14477     },
14478
14479     /**
14480      * Expands a collapsed dialog back to its normal state.  Equivalent to the user
14481      * clicking the expand dialog button.
14482      */
14483     expand : function(){
14484         if(this.collapsed){
14485             this.collapsed = false;
14486             this.el.removeClass("x-dlg-collapsed");
14487             this.resizeTo(this.el.getWidth(), this.restoreHeight);
14488         }
14489     },
14490
14491     /**
14492      * Reinitializes the tabs component, clearing out old tabs and finding new ones.
14493      * @return {Roo.TabPanel} The tabs component
14494      */
14495     initTabs : function(){
14496         var tabs = this.getTabs();
14497         while(tabs.getTab(0)){
14498             tabs.removeTab(0);
14499         }
14500         this.el.select(this.tabTag+'.x-dlg-tab').each(function(el){
14501             var dom = el.dom;
14502             tabs.addTab(Roo.id(dom), dom.title);
14503             dom.title = "";
14504         });
14505         tabs.activate(0);
14506         return tabs;
14507     },
14508
14509     // private
14510     beforeResize : function(){
14511         this.resizer.minHeight = Math.max(this.minHeight, this.getHeaderFooterHeight(true)+40);
14512     },
14513
14514     // private
14515     onResize : function(){
14516         this.refreshSize();
14517         this.syncBodyHeight();
14518         this.adjustAssets();
14519         this.focus();
14520         this.fireEvent("resize", this, this.size.width, this.size.height);
14521     },
14522
14523     // private
14524     onKeyDown : function(e){
14525         if(this.isVisible()){
14526             this.fireEvent("keydown", this, e);
14527         }
14528     },
14529
14530     /**
14531      * Resizes the dialog.
14532      * @param {Number} width
14533      * @param {Number} height
14534      * @return {Roo.BasicDialog} this
14535      */
14536     resizeTo : function(width, height){
14537         this.el.setSize(width, height);
14538         this.size = {width: width, height: height};
14539         this.syncBodyHeight();
14540         if(this.fixedcenter){
14541             this.center();
14542         }
14543         if(this.isVisible()){
14544             this.constrainXY();
14545             this.adjustAssets();
14546         }
14547         this.fireEvent("resize", this, width, height);
14548         return this;
14549     },
14550
14551
14552     /**
14553      * Resizes the dialog to fit the specified content size.
14554      * @param {Number} width
14555      * @param {Number} height
14556      * @return {Roo.BasicDialog} this
14557      */
14558     setContentSize : function(w, h){
14559         h += this.getHeaderFooterHeight() + this.body.getMargins("tb");
14560         w += this.body.getMargins("lr") + this.bwrap.getMargins("lr") + this.centerBg.getPadding("lr");
14561         //if(!this.el.isBorderBox()){
14562             h +=  this.body.getPadding("tb") + this.bwrap.getBorderWidth("tb") + this.body.getBorderWidth("tb") + this.el.getBorderWidth("tb");
14563             w += this.body.getPadding("lr") + this.bwrap.getBorderWidth("lr") + this.body.getBorderWidth("lr") + this.bwrap.getPadding("lr") + this.el.getBorderWidth("lr");
14564         //}
14565         if(this.tabs){
14566             h += this.tabs.stripWrap.getHeight() + this.tabs.bodyEl.getMargins("tb") + this.tabs.bodyEl.getPadding("tb");
14567             w += this.tabs.bodyEl.getMargins("lr") + this.tabs.bodyEl.getPadding("lr");
14568         }
14569         this.resizeTo(w, h);
14570         return this;
14571     },
14572
14573     /**
14574      * Adds a key listener for when this dialog is displayed.  This allows you to hook in a function that will be
14575      * executed in response to a particular key being pressed while the dialog is active.
14576      * @param {Number/Array/Object} key Either the numeric key code, array of key codes or an object with the following options:
14577      *                                  {key: (number or array), shift: (true/false), ctrl: (true/false), alt: (true/false)}
14578      * @param {Function} fn The function to call
14579      * @param {Object} scope (optional) The scope of the function
14580      * @return {Roo.BasicDialog} this
14581      */
14582     addKeyListener : function(key, fn, scope){
14583         var keyCode, shift, ctrl, alt;
14584         if(typeof key == "object" && !(key instanceof Array)){
14585             keyCode = key["key"];
14586             shift = key["shift"];
14587             ctrl = key["ctrl"];
14588             alt = key["alt"];
14589         }else{
14590             keyCode = key;
14591         }
14592         var handler = function(dlg, e){
14593             if((!shift || e.shiftKey) && (!ctrl || e.ctrlKey) &&  (!alt || e.altKey)){
14594                 var k = e.getKey();
14595                 if(keyCode instanceof Array){
14596                     for(var i = 0, len = keyCode.length; i < len; i++){
14597                         if(keyCode[i] == k){
14598                           fn.call(scope || window, dlg, k, e);
14599                           return;
14600                         }
14601                     }
14602                 }else{
14603                     if(k == keyCode){
14604                         fn.call(scope || window, dlg, k, e);
14605                     }
14606                 }
14607             }
14608         };
14609         this.on("keydown", handler);
14610         return this;
14611     },
14612
14613     /**
14614      * Returns the TabPanel component (creates it if it doesn't exist).
14615      * Note: If you wish to simply check for the existence of tabs without creating them,
14616      * check for a null 'tabs' property.
14617      * @return {Roo.TabPanel} The tabs component
14618      */
14619     getTabs : function(){
14620         if(!this.tabs){
14621             this.el.addClass("x-dlg-auto-tabs");
14622             this.body.addClass(this.tabPosition == "bottom" ? "x-tabs-bottom" : "x-tabs-top");
14623             this.tabs = new Roo.TabPanel(this.body.dom, this.tabPosition == "bottom");
14624         }
14625         return this.tabs;
14626     },
14627
14628     /**
14629      * Adds a button to the footer section of the dialog.
14630      * @param {String/Object} config A string becomes the button text, an object can either be a Button config
14631      * object or a valid Roo.DomHelper element config
14632      * @param {Function} handler The function called when the button is clicked
14633      * @param {Object} scope (optional) The scope of the handler function (accepts position as a property)
14634      * @return {Roo.Button} The new button
14635      */
14636     addButton : function(config, handler, scope){
14637         var dh = Roo.DomHelper;
14638         if(!this.footer){
14639             this.footer = dh.append(this.bwrap, {tag: "div", cls:"x-dlg-ft"}, true);
14640         }
14641         if(!this.btnContainer){
14642             var tb = this.footer.createChild({
14643
14644                 cls:"x-dlg-btns x-dlg-btns-"+this.buttonAlign,
14645                 html:'<table cellspacing="0"><tbody><tr></tr></tbody></table><div class="x-clear"></div>'
14646             }, null, true);
14647             this.btnContainer = tb.firstChild.firstChild.firstChild;
14648         }
14649         var bconfig = {
14650             handler: handler,
14651             scope: scope,
14652             minWidth: this.minButtonWidth,
14653             hideParent:true
14654         };
14655         if(typeof config == "string"){
14656             bconfig.text = config;
14657         }else{
14658             if(config.tag){
14659                 bconfig.dhconfig = config;
14660             }else{
14661                 Roo.apply(bconfig, config);
14662             }
14663         }
14664         var fc = false;
14665         if ((typeof(bconfig.position) != 'undefined') && bconfig.position < this.btnContainer.childNodes.length-1) {
14666             bconfig.position = Math.max(0, bconfig.position);
14667             fc = this.btnContainer.childNodes[bconfig.position];
14668         }
14669          
14670         var btn = new Roo.Button(
14671             fc ? 
14672                 this.btnContainer.insertBefore(document.createElement("td"),fc)
14673                 : this.btnContainer.appendChild(document.createElement("td")),
14674             //Roo.get(this.btnContainer).createChild( { tag: 'td'},  fc ),
14675             bconfig
14676         );
14677         this.syncBodyHeight();
14678         if(!this.buttons){
14679             /**
14680              * Array of all the buttons that have been added to this dialog via addButton
14681              * @type Array
14682              */
14683             this.buttons = [];
14684         }
14685         this.buttons.push(btn);
14686         return btn;
14687     },
14688
14689     /**
14690      * Sets the default button to be focused when the dialog is displayed.
14691      * @param {Roo.BasicDialog.Button} btn The button object returned by {@link #addButton}
14692      * @return {Roo.BasicDialog} this
14693      */
14694     setDefaultButton : function(btn){
14695         this.defaultButton = btn;
14696         return this;
14697     },
14698
14699     // private
14700     getHeaderFooterHeight : function(safe){
14701         var height = 0;
14702         if(this.header){
14703            height += this.header.getHeight();
14704         }
14705         if(this.footer){
14706            var fm = this.footer.getMargins();
14707             height += (this.footer.getHeight()+fm.top+fm.bottom);
14708         }
14709         height += this.bwrap.getPadding("tb")+this.bwrap.getBorderWidth("tb");
14710         height += this.centerBg.getPadding("tb");
14711         return height;
14712     },
14713
14714     // private
14715     syncBodyHeight : function(){
14716         var bd = this.body, cb = this.centerBg, bw = this.bwrap;
14717         var height = this.size.height - this.getHeaderFooterHeight(false);
14718         bd.setHeight(height-bd.getMargins("tb"));
14719         var hh = this.header.getHeight();
14720         var h = this.size.height-hh;
14721         cb.setHeight(h);
14722         bw.setLeftTop(cb.getPadding("l"), hh+cb.getPadding("t"));
14723         bw.setHeight(h-cb.getPadding("tb"));
14724         bw.setWidth(this.el.getWidth(true)-cb.getPadding("lr"));
14725         bd.setWidth(bw.getWidth(true));
14726         if(this.tabs){
14727             this.tabs.syncHeight();
14728             if(Roo.isIE){
14729                 this.tabs.el.repaint();
14730             }
14731         }
14732     },
14733
14734     /**
14735      * Restores the previous state of the dialog if Roo.state is configured.
14736      * @return {Roo.BasicDialog} this
14737      */
14738     restoreState : function(){
14739         var box = Roo.state.Manager.get(this.stateId || (this.el.id + "-state"));
14740         if(box && box.width){
14741             this.xy = [box.x, box.y];
14742             this.resizeTo(box.width, box.height);
14743         }
14744         return this;
14745     },
14746
14747     // private
14748     beforeShow : function(){
14749         this.expand();
14750         if(this.fixedcenter){
14751             this.xy = this.el.getCenterXY(true);
14752         }
14753         if(this.modal){
14754             Roo.get(document.body).addClass("x-body-masked");
14755             this.mask.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
14756             this.mask.show();
14757         }
14758         this.constrainXY();
14759     },
14760
14761     // private
14762     animShow : function(){
14763         var b = Roo.get(this.animateTarget).getBox();
14764         this.proxy.setSize(b.width, b.height);
14765         this.proxy.setLocation(b.x, b.y);
14766         this.proxy.show();
14767         this.proxy.setBounds(this.xy[0], this.xy[1], this.size.width, this.size.height,
14768                     true, .35, this.showEl.createDelegate(this));
14769     },
14770
14771     /**
14772      * Shows the dialog.
14773      * @param {String/HTMLElement/Roo.Element} animateTarget (optional) Reset the animation target
14774      * @return {Roo.BasicDialog} this
14775      */
14776     show : function(animateTarget){
14777         if (this.fireEvent("beforeshow", this) === false){
14778             return;
14779         }
14780         if(this.syncHeightBeforeShow){
14781             this.syncBodyHeight();
14782         }else if(this.firstShow){
14783             this.firstShow = false;
14784             this.syncBodyHeight(); // sync the height on the first show instead of in the constructor
14785         }
14786         this.animateTarget = animateTarget || this.animateTarget;
14787         if(!this.el.isVisible()){
14788             this.beforeShow();
14789             if(this.animateTarget && Roo.get(this.animateTarget)){
14790                 this.animShow();
14791             }else{
14792                 this.showEl();
14793             }
14794         }
14795         return this;
14796     },
14797
14798     // private
14799     showEl : function(){
14800         this.proxy.hide();
14801         this.el.setXY(this.xy);
14802         this.el.show();
14803         this.adjustAssets(true);
14804         this.toFront();
14805         this.focus();
14806         // IE peekaboo bug - fix found by Dave Fenwick
14807         if(Roo.isIE){
14808             this.el.repaint();
14809         }
14810         this.fireEvent("show", this);
14811     },
14812
14813     /**
14814      * Focuses the dialog.  If a defaultButton is set, it will receive focus, otherwise the
14815      * dialog itself will receive focus.
14816      */
14817     focus : function(){
14818         if(this.defaultButton){
14819             this.defaultButton.focus();
14820         }else{
14821             this.focusEl.focus();
14822         }
14823     },
14824
14825     // private
14826     constrainXY : function(){
14827         if(this.constraintoviewport !== false){
14828             if(!this.viewSize){
14829                 if(this.container){
14830                     var s = this.container.getSize();
14831                     this.viewSize = [s.width, s.height];
14832                 }else{
14833                     this.viewSize = [Roo.lib.Dom.getViewWidth(),Roo.lib.Dom.getViewHeight()];
14834                 }
14835             }
14836             var s = Roo.get(this.container||document).getScroll();
14837
14838             var x = this.xy[0], y = this.xy[1];
14839             var w = this.size.width, h = this.size.height;
14840             var vw = this.viewSize[0], vh = this.viewSize[1];
14841             // only move it if it needs it
14842             var moved = false;
14843             // first validate right/bottom
14844             if(x + w > vw+s.left){
14845                 x = vw - w;
14846                 moved = true;
14847             }
14848             if(y + h > vh+s.top){
14849                 y = vh - h;
14850                 moved = true;
14851             }
14852             // then make sure top/left isn't negative
14853             if(x < s.left){
14854                 x = s.left;
14855                 moved = true;
14856             }
14857             if(y < s.top){
14858                 y = s.top;
14859                 moved = true;
14860             }
14861             if(moved){
14862                 // cache xy
14863                 this.xy = [x, y];
14864                 if(this.isVisible()){
14865                     this.el.setLocation(x, y);
14866                     this.adjustAssets();
14867                 }
14868             }
14869         }
14870     },
14871
14872     // private
14873     onDrag : function(){
14874         if(!this.proxyDrag){
14875             this.xy = this.el.getXY();
14876             this.adjustAssets();
14877         }
14878     },
14879
14880     // private
14881     adjustAssets : function(doShow){
14882         var x = this.xy[0], y = this.xy[1];
14883         var w = this.size.width, h = this.size.height;
14884         if(doShow === true){
14885             if(this.shadow){
14886                 this.shadow.show(this.el);
14887             }
14888             if(this.shim){
14889                 this.shim.show();
14890             }
14891         }
14892         if(this.shadow && this.shadow.isVisible()){
14893             this.shadow.show(this.el);
14894         }
14895         if(this.shim && this.shim.isVisible()){
14896             this.shim.setBounds(x, y, w, h);
14897         }
14898     },
14899
14900     // private
14901     adjustViewport : function(w, h){
14902         if(!w || !h){
14903             w = Roo.lib.Dom.getViewWidth();
14904             h = Roo.lib.Dom.getViewHeight();
14905         }
14906         // cache the size
14907         this.viewSize = [w, h];
14908         if(this.modal && this.mask.isVisible()){
14909             this.mask.setSize(w, h); // first make sure the mask isn't causing overflow
14910             this.mask.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
14911         }
14912         if(this.isVisible()){
14913             this.constrainXY();
14914         }
14915     },
14916
14917     /**
14918      * Destroys this dialog and all its supporting elements (including any tabs, shim,
14919      * shadow, proxy, mask, etc.)  Also removes all event listeners.
14920      * @param {Boolean} removeEl (optional) true to remove the element from the DOM
14921      */
14922     destroy : function(removeEl){
14923         if(this.isVisible()){
14924             this.animateTarget = null;
14925             this.hide();
14926         }
14927         Roo.EventManager.removeResizeListener(this.adjustViewport, this);
14928         if(this.tabs){
14929             this.tabs.destroy(removeEl);
14930         }
14931         Roo.destroy(
14932              this.shim,
14933              this.proxy,
14934              this.resizer,
14935              this.close,
14936              this.mask
14937         );
14938         if(this.dd){
14939             this.dd.unreg();
14940         }
14941         if(this.buttons){
14942            for(var i = 0, len = this.buttons.length; i < len; i++){
14943                this.buttons[i].destroy();
14944            }
14945         }
14946         this.el.removeAllListeners();
14947         if(removeEl === true){
14948             this.el.update("");
14949             this.el.remove();
14950         }
14951         Roo.DialogManager.unregister(this);
14952     },
14953
14954     // private
14955     startMove : function(){
14956         if(this.proxyDrag){
14957             this.proxy.show();
14958         }
14959         if(this.constraintoviewport !== false){
14960             this.dd.constrainTo(document.body, {right: this.shadowOffset, bottom: this.shadowOffset});
14961         }
14962     },
14963
14964     // private
14965     endMove : function(){
14966         if(!this.proxyDrag){
14967             Roo.dd.DD.prototype.endDrag.apply(this.dd, arguments);
14968         }else{
14969             Roo.dd.DDProxy.prototype.endDrag.apply(this.dd, arguments);
14970             this.proxy.hide();
14971         }
14972         this.refreshSize();
14973         this.adjustAssets();
14974         this.focus();
14975         this.fireEvent("move", this, this.xy[0], this.xy[1]);
14976     },
14977
14978     /**
14979      * Brings this dialog to the front of any other visible dialogs
14980      * @return {Roo.BasicDialog} this
14981      */
14982     toFront : function(){
14983         Roo.DialogManager.bringToFront(this);
14984         return this;
14985     },
14986
14987     /**
14988      * Sends this dialog to the back (under) of any other visible dialogs
14989      * @return {Roo.BasicDialog} this
14990      */
14991     toBack : function(){
14992         Roo.DialogManager.sendToBack(this);
14993         return this;
14994     },
14995
14996     /**
14997      * Centers this dialog in the viewport
14998      * @return {Roo.BasicDialog} this
14999      */
15000     center : function(){
15001         var xy = this.el.getCenterXY(true);
15002         this.moveTo(xy[0], xy[1]);
15003         return this;
15004     },
15005
15006     /**
15007      * Moves the dialog's top-left corner to the specified point
15008      * @param {Number} x
15009      * @param {Number} y
15010      * @return {Roo.BasicDialog} this
15011      */
15012     moveTo : function(x, y){
15013         this.xy = [x,y];
15014         if(this.isVisible()){
15015             this.el.setXY(this.xy);
15016             this.adjustAssets();
15017         }
15018         return this;
15019     },
15020
15021     /**
15022      * Aligns the dialog to the specified element
15023      * @param {String/HTMLElement/Roo.Element} element The element to align to.
15024      * @param {String} position The position to align to (see {@link Roo.Element#alignTo} for more details).
15025      * @param {Array} offsets (optional) Offset the positioning by [x, y]
15026      * @return {Roo.BasicDialog} this
15027      */
15028     alignTo : function(element, position, offsets){
15029         this.xy = this.el.getAlignToXY(element, position, offsets);
15030         if(this.isVisible()){
15031             this.el.setXY(this.xy);
15032             this.adjustAssets();
15033         }
15034         return this;
15035     },
15036
15037     /**
15038      * Anchors an element to another element and realigns it when the window is resized.
15039      * @param {String/HTMLElement/Roo.Element} element The element to align to.
15040      * @param {String} position The position to align to (see {@link Roo.Element#alignTo} for more details)
15041      * @param {Array} offsets (optional) Offset the positioning by [x, y]
15042      * @param {Boolean/Number} monitorScroll (optional) true to monitor body scroll and reposition. If this parameter
15043      * is a number, it is used as the buffer delay (defaults to 50ms).
15044      * @return {Roo.BasicDialog} this
15045      */
15046     anchorTo : function(el, alignment, offsets, monitorScroll){
15047         var action = function(){
15048             this.alignTo(el, alignment, offsets);
15049         };
15050         Roo.EventManager.onWindowResize(action, this);
15051         var tm = typeof monitorScroll;
15052         if(tm != 'undefined'){
15053             Roo.EventManager.on(window, 'scroll', action, this,
15054                 {buffer: tm == 'number' ? monitorScroll : 50});
15055         }
15056         action.call(this);
15057         return this;
15058     },
15059
15060     /**
15061      * Returns true if the dialog is visible
15062      * @return {Boolean}
15063      */
15064     isVisible : function(){
15065         return this.el.isVisible();
15066     },
15067
15068     // private
15069     animHide : function(callback){
15070         var b = Roo.get(this.animateTarget).getBox();
15071         this.proxy.show();
15072         this.proxy.setBounds(this.xy[0], this.xy[1], this.size.width, this.size.height);
15073         this.el.hide();
15074         this.proxy.setBounds(b.x, b.y, b.width, b.height, true, .35,
15075                     this.hideEl.createDelegate(this, [callback]));
15076     },
15077
15078     /**
15079      * Hides the dialog.
15080      * @param {Function} callback (optional) Function to call when the dialog is hidden
15081      * @return {Roo.BasicDialog} this
15082      */
15083     hide : function(callback){
15084         if (this.fireEvent("beforehide", this) === false){
15085             return;
15086         }
15087         if(this.shadow){
15088             this.shadow.hide();
15089         }
15090         if(this.shim) {
15091           this.shim.hide();
15092         }
15093         // sometimes animateTarget seems to get set.. causing problems...
15094         // this just double checks..
15095         if(this.animateTarget && Roo.get(this.animateTarget)) {
15096            this.animHide(callback);
15097         }else{
15098             this.el.hide();
15099             this.hideEl(callback);
15100         }
15101         return this;
15102     },
15103
15104     // private
15105     hideEl : function(callback){
15106         this.proxy.hide();
15107         if(this.modal){
15108             this.mask.hide();
15109             Roo.get(document.body).removeClass("x-body-masked");
15110         }
15111         this.fireEvent("hide", this);
15112         if(typeof callback == "function"){
15113             callback();
15114         }
15115     },
15116
15117     // private
15118     hideAction : function(){
15119         this.setLeft("-10000px");
15120         this.setTop("-10000px");
15121         this.setStyle("visibility", "hidden");
15122     },
15123
15124     // private
15125     refreshSize : function(){
15126         this.size = this.el.getSize();
15127         this.xy = this.el.getXY();
15128         Roo.state.Manager.set(this.stateId || this.el.id + "-state", this.el.getBox());
15129     },
15130
15131     // private
15132     // z-index is managed by the DialogManager and may be overwritten at any time
15133     setZIndex : function(index){
15134         if(this.modal){
15135             this.mask.setStyle("z-index", index);
15136         }
15137         if(this.shim){
15138             this.shim.setStyle("z-index", ++index);
15139         }
15140         if(this.shadow){
15141             this.shadow.setZIndex(++index);
15142         }
15143         this.el.setStyle("z-index", ++index);
15144         if(this.proxy){
15145             this.proxy.setStyle("z-index", ++index);
15146         }
15147         if(this.resizer){
15148             this.resizer.proxy.setStyle("z-index", ++index);
15149         }
15150
15151         this.lastZIndex = index;
15152     },
15153
15154     /**
15155      * Returns the element for this dialog
15156      * @return {Roo.Element} The underlying dialog Element
15157      */
15158     getEl : function(){
15159         return this.el;
15160     }
15161 });
15162
15163 /**
15164  * @class Roo.DialogManager
15165  * Provides global access to BasicDialogs that have been created and
15166  * support for z-indexing (layering) multiple open dialogs.
15167  */
15168 Roo.DialogManager = function(){
15169     var list = {};
15170     var accessList = [];
15171     var front = null;
15172
15173     // private
15174     var sortDialogs = function(d1, d2){
15175         return (!d1._lastAccess || d1._lastAccess < d2._lastAccess) ? -1 : 1;
15176     };
15177
15178     // private
15179     var orderDialogs = function(){
15180         accessList.sort(sortDialogs);
15181         var seed = Roo.DialogManager.zseed;
15182         for(var i = 0, len = accessList.length; i < len; i++){
15183             var dlg = accessList[i];
15184             if(dlg){
15185                 dlg.setZIndex(seed + (i*10));
15186             }
15187         }
15188     };
15189
15190     return {
15191         /**
15192          * The starting z-index for BasicDialogs (defaults to 9000)
15193          * @type Number The z-index value
15194          */
15195         zseed : 9000,
15196
15197         // private
15198         register : function(dlg){
15199             list[dlg.id] = dlg;
15200             accessList.push(dlg);
15201         },
15202
15203         // private
15204         unregister : function(dlg){
15205             delete list[dlg.id];
15206             var i=0;
15207             var len=0;
15208             if(!accessList.indexOf){
15209                 for(  i = 0, len = accessList.length; i < len; i++){
15210                     if(accessList[i] == dlg){
15211                         accessList.splice(i, 1);
15212                         return;
15213                     }
15214                 }
15215             }else{
15216                  i = accessList.indexOf(dlg);
15217                 if(i != -1){
15218                     accessList.splice(i, 1);
15219                 }
15220             }
15221         },
15222
15223         /**
15224          * Gets a registered dialog by id
15225          * @param {String/Object} id The id of the dialog or a dialog
15226          * @return {Roo.BasicDialog} this
15227          */
15228         get : function(id){
15229             return typeof id == "object" ? id : list[id];
15230         },
15231
15232         /**
15233          * Brings the specified dialog to the front
15234          * @param {String/Object} dlg The id of the dialog or a dialog
15235          * @return {Roo.BasicDialog} this
15236          */
15237         bringToFront : function(dlg){
15238             dlg = this.get(dlg);
15239             if(dlg != front){
15240                 front = dlg;
15241                 dlg._lastAccess = new Date().getTime();
15242                 orderDialogs();
15243             }
15244             return dlg;
15245         },
15246
15247         /**
15248          * Sends the specified dialog to the back
15249          * @param {String/Object} dlg The id of the dialog or a dialog
15250          * @return {Roo.BasicDialog} this
15251          */
15252         sendToBack : function(dlg){
15253             dlg = this.get(dlg);
15254             dlg._lastAccess = -(new Date().getTime());
15255             orderDialogs();
15256             return dlg;
15257         },
15258
15259         /**
15260          * Hides all dialogs
15261          */
15262         hideAll : function(){
15263             for(var id in list){
15264                 if(list[id] && typeof list[id] != "function" && list[id].isVisible()){
15265                     list[id].hide();
15266                 }
15267             }
15268         }
15269     };
15270 }();
15271
15272 /**
15273  * @class Roo.LayoutDialog
15274  * @extends Roo.BasicDialog
15275  * Dialog which provides adjustments for working with a layout in a Dialog.
15276  * Add your necessary layout config options to the dialog's config.<br>
15277  * Example usage (including a nested layout):
15278  * <pre><code>
15279 if(!dialog){
15280     dialog = new Roo.LayoutDialog("download-dlg", {
15281         modal: true,
15282         width:600,
15283         height:450,
15284         shadow:true,
15285         minWidth:500,
15286         minHeight:350,
15287         autoTabs:true,
15288         proxyDrag:true,
15289         // layout config merges with the dialog config
15290         center:{
15291             tabPosition: "top",
15292             alwaysShowTabs: true
15293         }
15294     });
15295     dialog.addKeyListener(27, dialog.hide, dialog);
15296     dialog.setDefaultButton(dialog.addButton("Close", dialog.hide, dialog));
15297     dialog.addButton("Build It!", this.getDownload, this);
15298
15299     // we can even add nested layouts
15300     var innerLayout = new Roo.BorderLayout("dl-inner", {
15301         east: {
15302             initialSize: 200,
15303             autoScroll:true,
15304             split:true
15305         },
15306         center: {
15307             autoScroll:true
15308         }
15309     });
15310     innerLayout.beginUpdate();
15311     innerLayout.add("east", new Roo.ContentPanel("dl-details"));
15312     innerLayout.add("center", new Roo.ContentPanel("selection-panel"));
15313     innerLayout.endUpdate(true);
15314
15315     var layout = dialog.getLayout();
15316     layout.beginUpdate();
15317     layout.add("center", new Roo.ContentPanel("standard-panel",
15318                         {title: "Download the Source", fitToFrame:true}));
15319     layout.add("center", new Roo.NestedLayoutPanel(innerLayout,
15320                {title: "Build your own roo.js"}));
15321     layout.getRegion("center").showPanel(sp);
15322     layout.endUpdate();
15323 }
15324 </code></pre>
15325     * @constructor
15326     * @param {String/HTMLElement/Roo.Element} el The id of or container element, or config
15327     * @param {Object} config configuration options
15328   */
15329 Roo.LayoutDialog = function(el, cfg){
15330     
15331     var config=  cfg;
15332     if (typeof(cfg) == 'undefined') {
15333         config = Roo.apply({}, el);
15334         // not sure why we use documentElement here.. - it should always be body.
15335         // IE7 borks horribly if we use documentElement.
15336         // webkit also does not like documentElement - it creates a body element...
15337         el = Roo.get( document.body || document.documentElement ).createChild();
15338         //config.autoCreate = true;
15339     }
15340     
15341     
15342     config.autoTabs = false;
15343     Roo.LayoutDialog.superclass.constructor.call(this, el, config);
15344     this.body.setStyle({overflow:"hidden", position:"relative"});
15345     this.layout = new Roo.BorderLayout(this.body.dom, config);
15346     this.layout.monitorWindowResize = false;
15347     this.el.addClass("x-dlg-auto-layout");
15348     // fix case when center region overwrites center function
15349     this.center = Roo.BasicDialog.prototype.center;
15350     this.on("show", this.layout.layout, this.layout, true);
15351     if (config.items) {
15352         var xitems = config.items;
15353         delete config.items;
15354         Roo.each(xitems, this.addxtype, this);
15355     }
15356     
15357     
15358 };
15359 Roo.extend(Roo.LayoutDialog, Roo.BasicDialog, {
15360     /**
15361      * Ends update of the layout <strike>and resets display to none</strike>. Use standard beginUpdate/endUpdate on the layout.
15362      * @deprecated
15363      */
15364     endUpdate : function(){
15365         this.layout.endUpdate();
15366     },
15367
15368     /**
15369      * Begins an update of the layout <strike>and sets display to block and visibility to hidden</strike>. Use standard beginUpdate/endUpdate on the layout.
15370      *  @deprecated
15371      */
15372     beginUpdate : function(){
15373         this.layout.beginUpdate();
15374     },
15375
15376     /**
15377      * Get the BorderLayout for this dialog
15378      * @return {Roo.BorderLayout}
15379      */
15380     getLayout : function(){
15381         return this.layout;
15382     },
15383
15384     showEl : function(){
15385         Roo.LayoutDialog.superclass.showEl.apply(this, arguments);
15386         if(Roo.isIE7){
15387             this.layout.layout();
15388         }
15389     },
15390
15391     // private
15392     // Use the syncHeightBeforeShow config option to control this automatically
15393     syncBodyHeight : function(){
15394         Roo.LayoutDialog.superclass.syncBodyHeight.call(this);
15395         if(this.layout){this.layout.layout();}
15396     },
15397     
15398       /**
15399      * Add an xtype element (actually adds to the layout.)
15400      * @return {Object} xdata xtype object data.
15401      */
15402     
15403     addxtype : function(c) {
15404         return this.layout.addxtype(c);
15405     }
15406 });/*
15407  * Based on:
15408  * Ext JS Library 1.1.1
15409  * Copyright(c) 2006-2007, Ext JS, LLC.
15410  *
15411  * Originally Released Under LGPL - original licence link has changed is not relivant.
15412  *
15413  * Fork - LGPL
15414  * <script type="text/javascript">
15415  */
15416  
15417 /**
15418  * @class Roo.MessageBox
15419  * Utility class for generating different styles of message boxes.  The alias Roo.Msg can also be used.
15420  * Example usage:
15421  *<pre><code>
15422 // Basic alert:
15423 Roo.Msg.alert('Status', 'Changes saved successfully.');
15424
15425 // Prompt for user data:
15426 Roo.Msg.prompt('Name', 'Please enter your name:', function(btn, text){
15427     if (btn == 'ok'){
15428         // process text value...
15429     }
15430 });
15431
15432 // Show a dialog using config options:
15433 Roo.Msg.show({
15434    title:'Save Changes?',
15435    msg: 'Your are closing a tab that has unsaved changes. Would you like to save your changes?',
15436    buttons: Roo.Msg.YESNOCANCEL,
15437    fn: processResult,
15438    animEl: 'elId'
15439 });
15440 </code></pre>
15441  * @singleton
15442  */
15443 Roo.MessageBox = function(){
15444     var dlg, opt, mask, waitTimer;
15445     var bodyEl, msgEl, textboxEl, textareaEl, progressEl, pp;
15446     var buttons, activeTextEl, bwidth;
15447
15448     // private
15449     var handleButton = function(button){
15450         dlg.hide();
15451         Roo.callback(opt.fn, opt.scope||window, [button, activeTextEl.dom.value], 1);
15452     };
15453
15454     // private
15455     var handleHide = function(){
15456         if(opt && opt.cls){
15457             dlg.el.removeClass(opt.cls);
15458         }
15459         if(waitTimer){
15460             Roo.TaskMgr.stop(waitTimer);
15461             waitTimer = null;
15462         }
15463     };
15464
15465     // private
15466     var updateButtons = function(b){
15467         var width = 0;
15468         if(!b){
15469             buttons["ok"].hide();
15470             buttons["cancel"].hide();
15471             buttons["yes"].hide();
15472             buttons["no"].hide();
15473             dlg.footer.dom.style.display = 'none';
15474             return width;
15475         }
15476         dlg.footer.dom.style.display = '';
15477         for(var k in buttons){
15478             if(typeof buttons[k] != "function"){
15479                 if(b[k]){
15480                     buttons[k].show();
15481                     buttons[k].setText(typeof b[k] == "string" ? b[k] : Roo.MessageBox.buttonText[k]);
15482                     width += buttons[k].el.getWidth()+15;
15483                 }else{
15484                     buttons[k].hide();
15485                 }
15486             }
15487         }
15488         return width;
15489     };
15490
15491     // private
15492     var handleEsc = function(d, k, e){
15493         if(opt && opt.closable !== false){
15494             dlg.hide();
15495         }
15496         if(e){
15497             e.stopEvent();
15498         }
15499     };
15500
15501     return {
15502         /**
15503          * Returns a reference to the underlying {@link Roo.BasicDialog} element
15504          * @return {Roo.BasicDialog} The BasicDialog element
15505          */
15506         getDialog : function(){
15507            if(!dlg){
15508                 dlg = new Roo.BasicDialog("x-msg-box", {
15509                     autoCreate : true,
15510                     shadow: true,
15511                     draggable: true,
15512                     resizable:false,
15513                     constraintoviewport:false,
15514                     fixedcenter:true,
15515                     collapsible : false,
15516                     shim:true,
15517                     modal: true,
15518                     width:400, height:100,
15519                     buttonAlign:"center",
15520                     closeClick : function(){
15521                         if(opt && opt.buttons && opt.buttons.no && !opt.buttons.cancel){
15522                             handleButton("no");
15523                         }else{
15524                             handleButton("cancel");
15525                         }
15526                     }
15527                 });
15528                 dlg.on("hide", handleHide);
15529                 mask = dlg.mask;
15530                 dlg.addKeyListener(27, handleEsc);
15531                 buttons = {};
15532                 var bt = this.buttonText;
15533                 buttons["ok"] = dlg.addButton(bt["ok"], handleButton.createCallback("ok"));
15534                 buttons["yes"] = dlg.addButton(bt["yes"], handleButton.createCallback("yes"));
15535                 buttons["no"] = dlg.addButton(bt["no"], handleButton.createCallback("no"));
15536                 buttons["cancel"] = dlg.addButton(bt["cancel"], handleButton.createCallback("cancel"));
15537                 bodyEl = dlg.body.createChild({
15538
15539                     html:'<span class="roo-mb-text"></span><br /><input type="text" class="roo-mb-input" /><textarea class="roo-mb-textarea"></textarea><div class="roo-mb-progress-wrap"><div class="roo-mb-progress"><div class="roo-mb-progress-bar">&#160;</div></div></div>'
15540                 });
15541                 msgEl = bodyEl.dom.firstChild;
15542                 textboxEl = Roo.get(bodyEl.dom.childNodes[2]);
15543                 textboxEl.enableDisplayMode();
15544                 textboxEl.addKeyListener([10,13], function(){
15545                     if(dlg.isVisible() && opt && opt.buttons){
15546                         if(opt.buttons.ok){
15547                             handleButton("ok");
15548                         }else if(opt.buttons.yes){
15549                             handleButton("yes");
15550                         }
15551                     }
15552                 });
15553                 textareaEl = Roo.get(bodyEl.dom.childNodes[3]);
15554                 textareaEl.enableDisplayMode();
15555                 progressEl = Roo.get(bodyEl.dom.childNodes[4]);
15556                 progressEl.enableDisplayMode();
15557                 var pf = progressEl.dom.firstChild;
15558                 if (pf) {
15559                     pp = Roo.get(pf.firstChild);
15560                     pp.setHeight(pf.offsetHeight);
15561                 }
15562                 
15563             }
15564             return dlg;
15565         },
15566
15567         /**
15568          * Updates the message box body text
15569          * @param {String} text (optional) Replaces the message box element's innerHTML with the specified string (defaults to
15570          * the XHTML-compliant non-breaking space character '&amp;#160;')
15571          * @return {Roo.MessageBox} This message box
15572          */
15573         updateText : function(text){
15574             if(!dlg.isVisible() && !opt.width){
15575                 dlg.resizeTo(this.maxWidth, 100); // resize first so content is never clipped from previous shows
15576             }
15577             msgEl.innerHTML = text || '&#160;';
15578             var w = Math.max(Math.min(opt.width || msgEl.offsetWidth, this.maxWidth), 
15579                         Math.max(opt.minWidth || this.minWidth, bwidth));
15580             if(opt.prompt){
15581                 activeTextEl.setWidth(w);
15582             }
15583             if(dlg.isVisible()){
15584                 dlg.fixedcenter = false;
15585             }
15586             dlg.setContentSize(w, bodyEl.getHeight());
15587             if(dlg.isVisible()){
15588                 dlg.fixedcenter = true;
15589             }
15590             return this;
15591         },
15592
15593         /**
15594          * Updates a progress-style message box's text and progress bar.  Only relevant on message boxes
15595          * initiated via {@link Roo.MessageBox#progress} or by calling {@link Roo.MessageBox#show} with progress: true.
15596          * @param {Number} value Any number between 0 and 1 (e.g., .5)
15597          * @param {String} text (optional) If defined, the message box's body text is replaced with the specified string (defaults to undefined)
15598          * @return {Roo.MessageBox} This message box
15599          */
15600         updateProgress : function(value, text){
15601             if(text){
15602                 this.updateText(text);
15603             }
15604             if (pp) { // weird bug on my firefox - for some reason this is not defined
15605                 pp.setWidth(Math.floor(value*progressEl.dom.firstChild.offsetWidth));
15606             }
15607             return this;
15608         },        
15609
15610         /**
15611          * Returns true if the message box is currently displayed
15612          * @return {Boolean} True if the message box is visible, else false
15613          */
15614         isVisible : function(){
15615             return dlg && dlg.isVisible();  
15616         },
15617
15618         /**
15619          * Hides the message box if it is displayed
15620          */
15621         hide : function(){
15622             if(this.isVisible()){
15623                 dlg.hide();
15624             }  
15625         },
15626
15627         /**
15628          * Displays a new message box, or reinitializes an existing message box, based on the config options
15629          * passed in. All functions (e.g. prompt, alert, etc) on MessageBox call this function internally.
15630          * The following config object properties are supported:
15631          * <pre>
15632 Property    Type             Description
15633 ----------  ---------------  ------------------------------------------------------------------------------------
15634 animEl            String/Element   An id or Element from which the message box should animate as it opens and
15635                                    closes (defaults to undefined)
15636 buttons           Object/Boolean   A button config object (e.g., Roo.MessageBox.OKCANCEL or {ok:'Foo',
15637                                    cancel:'Bar'}), or false to not show any buttons (defaults to false)
15638 closable          Boolean          False to hide the top-right close button (defaults to true).  Note that
15639                                    progress and wait dialogs will ignore this property and always hide the
15640                                    close button as they can only be closed programmatically.
15641 cls               String           A custom CSS class to apply to the message box element
15642 defaultTextHeight Number           The default height in pixels of the message box's multiline textarea if
15643                                    displayed (defaults to 75)
15644 fn                Function         A callback function to execute after closing the dialog.  The arguments to the
15645                                    function will be btn (the name of the button that was clicked, if applicable,
15646                                    e.g. "ok"), and text (the value of the active text field, if applicable).
15647                                    Progress and wait dialogs will ignore this option since they do not respond to
15648                                    user actions and can only be closed programmatically, so any required function
15649                                    should be called by the same code after it closes the dialog.
15650 icon              String           A CSS class that provides a background image to be used as an icon for
15651                                    the dialog (e.g., Roo.MessageBox.WARNING or 'custom-class', defaults to '')
15652 maxWidth          Number           The maximum width in pixels of the message box (defaults to 600)
15653 minWidth          Number           The minimum width in pixels of the message box (defaults to 100)
15654 modal             Boolean          False to allow user interaction with the page while the message box is
15655                                    displayed (defaults to true)
15656 msg               String           A string that will replace the existing message box body text (defaults
15657                                    to the XHTML-compliant non-breaking space character '&#160;')
15658 multiline         Boolean          True to prompt the user to enter multi-line text (defaults to false)
15659 progress          Boolean          True to display a progress bar (defaults to false)
15660 progressText      String           The text to display inside the progress bar if progress = true (defaults to '')
15661 prompt            Boolean          True to prompt the user to enter single-line text (defaults to false)
15662 proxyDrag         Boolean          True to display a lightweight proxy while dragging (defaults to false)
15663 title             String           The title text
15664 value             String           The string value to set into the active textbox element if displayed
15665 wait              Boolean          True to display a progress bar (defaults to false)
15666 width             Number           The width of the dialog in pixels
15667 </pre>
15668          *
15669          * Example usage:
15670          * <pre><code>
15671 Roo.Msg.show({
15672    title: 'Address',
15673    msg: 'Please enter your address:',
15674    width: 300,
15675    buttons: Roo.MessageBox.OKCANCEL,
15676    multiline: true,
15677    fn: saveAddress,
15678    animEl: 'addAddressBtn'
15679 });
15680 </code></pre>
15681          * @param {Object} config Configuration options
15682          * @return {Roo.MessageBox} This message box
15683          */
15684         show : function(options){
15685             if(this.isVisible()){
15686                 this.hide();
15687             }
15688             var d = this.getDialog();
15689             opt = options;
15690             d.setTitle(opt.title || "&#160;");
15691             d.close.setDisplayed(opt.closable !== false);
15692             activeTextEl = textboxEl;
15693             opt.prompt = opt.prompt || (opt.multiline ? true : false);
15694             if(opt.prompt){
15695                 if(opt.multiline){
15696                     textboxEl.hide();
15697                     textareaEl.show();
15698                     textareaEl.setHeight(typeof opt.multiline == "number" ?
15699                         opt.multiline : this.defaultTextHeight);
15700                     activeTextEl = textareaEl;
15701                 }else{
15702                     textboxEl.show();
15703                     textareaEl.hide();
15704                 }
15705             }else{
15706                 textboxEl.hide();
15707                 textareaEl.hide();
15708             }
15709             progressEl.setDisplayed(opt.progress === true);
15710             this.updateProgress(0);
15711             activeTextEl.dom.value = opt.value || "";
15712             if(opt.prompt){
15713                 dlg.setDefaultButton(activeTextEl);
15714             }else{
15715                 var bs = opt.buttons;
15716                 var db = null;
15717                 if(bs && bs.ok){
15718                     db = buttons["ok"];
15719                 }else if(bs && bs.yes){
15720                     db = buttons["yes"];
15721                 }
15722                 dlg.setDefaultButton(db);
15723             }
15724             bwidth = updateButtons(opt.buttons);
15725             this.updateText(opt.msg);
15726             if(opt.cls){
15727                 d.el.addClass(opt.cls);
15728             }
15729             d.proxyDrag = opt.proxyDrag === true;
15730             d.modal = opt.modal !== false;
15731             d.mask = opt.modal !== false ? mask : false;
15732             if(!d.isVisible()){
15733                 // force it to the end of the z-index stack so it gets a cursor in FF
15734                 document.body.appendChild(dlg.el.dom);
15735                 d.animateTarget = null;
15736                 d.show(options.animEl);
15737             }
15738             return this;
15739         },
15740
15741         /**
15742          * Displays a message box with a progress bar.  This message box has no buttons and is not closeable by
15743          * the user.  You are responsible for updating the progress bar as needed via {@link Roo.MessageBox#updateProgress}
15744          * and closing the message box when the process is complete.
15745          * @param {String} title The title bar text
15746          * @param {String} msg The message box body text
15747          * @return {Roo.MessageBox} This message box
15748          */
15749         progress : function(title, msg){
15750             this.show({
15751                 title : title,
15752                 msg : msg,
15753                 buttons: false,
15754                 progress:true,
15755                 closable:false,
15756                 minWidth: this.minProgressWidth,
15757                 modal : true
15758             });
15759             return this;
15760         },
15761
15762         /**
15763          * Displays a standard read-only message box with an OK button (comparable to the basic JavaScript Window.alert).
15764          * If a callback function is passed it will be called after the user clicks the button, and the
15765          * id of the button that was clicked will be passed as the only parameter to the callback
15766          * (could also be the top-right close button).
15767          * @param {String} title The title bar text
15768          * @param {String} msg The message box body text
15769          * @param {Function} fn (optional) The callback function invoked after the message box is closed
15770          * @param {Object} scope (optional) The scope of the callback function
15771          * @return {Roo.MessageBox} This message box
15772          */
15773         alert : function(title, msg, fn, scope){
15774             this.show({
15775                 title : title,
15776                 msg : msg,
15777                 buttons: this.OK,
15778                 fn: fn,
15779                 scope : scope,
15780                 modal : true
15781             });
15782             return this;
15783         },
15784
15785         /**
15786          * Displays a message box with an infinitely auto-updating progress bar.  This can be used to block user
15787          * interaction while waiting for a long-running process to complete that does not have defined intervals.
15788          * You are responsible for closing the message box when the process is complete.
15789          * @param {String} msg The message box body text
15790          * @param {String} title (optional) The title bar text
15791          * @return {Roo.MessageBox} This message box
15792          */
15793         wait : function(msg, title){
15794             this.show({
15795                 title : title,
15796                 msg : msg,
15797                 buttons: false,
15798                 closable:false,
15799                 progress:true,
15800                 modal:true,
15801                 width:300,
15802                 wait:true
15803             });
15804             waitTimer = Roo.TaskMgr.start({
15805                 run: function(i){
15806                     Roo.MessageBox.updateProgress(((((i+20)%20)+1)*5)*.01);
15807                 },
15808                 interval: 1000
15809             });
15810             return this;
15811         },
15812
15813         /**
15814          * Displays a confirmation message box with Yes and No buttons (comparable to JavaScript's Window.confirm).
15815          * If a callback function is passed it will be called after the user clicks either button, and the id of the
15816          * button that was clicked will be passed as the only parameter to the callback (could also be the top-right close button).
15817          * @param {String} title The title bar text
15818          * @param {String} msg The message box body text
15819          * @param {Function} fn (optional) The callback function invoked after the message box is closed
15820          * @param {Object} scope (optional) The scope of the callback function
15821          * @return {Roo.MessageBox} This message box
15822          */
15823         confirm : function(title, msg, fn, scope){
15824             this.show({
15825                 title : title,
15826                 msg : msg,
15827                 buttons: this.YESNO,
15828                 fn: fn,
15829                 scope : scope,
15830                 modal : true
15831             });
15832             return this;
15833         },
15834
15835         /**
15836          * Displays a message box with OK and Cancel buttons prompting the user to enter some text (comparable to
15837          * JavaScript's Window.prompt).  The prompt can be a single-line or multi-line textbox.  If a callback function
15838          * is passed it will be called after the user clicks either button, and the id of the button that was clicked
15839          * (could also be the top-right close button) and the text that was entered will be passed as the two
15840          * parameters to the callback.
15841          * @param {String} title The title bar text
15842          * @param {String} msg The message box body text
15843          * @param {Function} fn (optional) The callback function invoked after the message box is closed
15844          * @param {Object} scope (optional) The scope of the callback function
15845          * @param {Boolean/Number} multiline (optional) True to create a multiline textbox using the defaultTextHeight
15846          * property, or the height in pixels to create the textbox (defaults to false / single-line)
15847          * @return {Roo.MessageBox} This message box
15848          */
15849         prompt : function(title, msg, fn, scope, multiline){
15850             this.show({
15851                 title : title,
15852                 msg : msg,
15853                 buttons: this.OKCANCEL,
15854                 fn: fn,
15855                 minWidth:250,
15856                 scope : scope,
15857                 prompt:true,
15858                 multiline: multiline,
15859                 modal : true
15860             });
15861             return this;
15862         },
15863
15864         /**
15865          * Button config that displays a single OK button
15866          * @type Object
15867          */
15868         OK : {ok:true},
15869         /**
15870          * Button config that displays Yes and No buttons
15871          * @type Object
15872          */
15873         YESNO : {yes:true, no:true},
15874         /**
15875          * Button config that displays OK and Cancel buttons
15876          * @type Object
15877          */
15878         OKCANCEL : {ok:true, cancel:true},
15879         /**
15880          * Button config that displays Yes, No and Cancel buttons
15881          * @type Object
15882          */
15883         YESNOCANCEL : {yes:true, no:true, cancel:true},
15884
15885         /**
15886          * The default height in pixels of the message box's multiline textarea if displayed (defaults to 75)
15887          * @type Number
15888          */
15889         defaultTextHeight : 75,
15890         /**
15891          * The maximum width in pixels of the message box (defaults to 600)
15892          * @type Number
15893          */
15894         maxWidth : 600,
15895         /**
15896          * The minimum width in pixels of the message box (defaults to 100)
15897          * @type Number
15898          */
15899         minWidth : 100,
15900         /**
15901          * The minimum width in pixels of the message box if it is a progress-style dialog.  This is useful
15902          * for setting a different minimum width than text-only dialogs may need (defaults to 250)
15903          * @type Number
15904          */
15905         minProgressWidth : 250,
15906         /**
15907          * An object containing the default button text strings that can be overriden for localized language support.
15908          * Supported properties are: ok, cancel, yes and no.
15909          * Customize the default text like so: Roo.MessageBox.buttonText.yes = "S?";
15910          * @type Object
15911          */
15912         buttonText : {
15913             ok : "OK",
15914             cancel : "Cancel",
15915             yes : "Yes",
15916             no : "No"
15917         }
15918     };
15919 }();
15920
15921 /**
15922  * Shorthand for {@link Roo.MessageBox}
15923  */
15924 Roo.Msg = Roo.MessageBox;/*
15925  * Based on:
15926  * Ext JS Library 1.1.1
15927  * Copyright(c) 2006-2007, Ext JS, LLC.
15928  *
15929  * Originally Released Under LGPL - original licence link has changed is not relivant.
15930  *
15931  * Fork - LGPL
15932  * <script type="text/javascript">
15933  */
15934 /**
15935  * @class Roo.QuickTips
15936  * Provides attractive and customizable tooltips for any element.
15937  * @singleton
15938  */
15939 Roo.QuickTips = function(){
15940     var el, tipBody, tipBodyText, tipTitle, tm, cfg, close, tagEls = {}, esc, removeCls = null, bdLeft, bdRight;
15941     var ce, bd, xy, dd;
15942     var visible = false, disabled = true, inited = false;
15943     var showProc = 1, hideProc = 1, dismissProc = 1, locks = [];
15944     
15945     var onOver = function(e){
15946         if(disabled){
15947             return;
15948         }
15949         var t = e.getTarget();
15950         if(!t || t.nodeType !== 1 || t == document || t == document.body){
15951             return;
15952         }
15953         if(ce && t == ce.el){
15954             clearTimeout(hideProc);
15955             return;
15956         }
15957         if(t && tagEls[t.id]){
15958             tagEls[t.id].el = t;
15959             showProc = show.defer(tm.showDelay, tm, [tagEls[t.id]]);
15960             return;
15961         }
15962         var ttp, et = Roo.fly(t);
15963         var ns = cfg.namespace;
15964         if(tm.interceptTitles && t.title){
15965             ttp = t.title;
15966             t.qtip = ttp;
15967             t.removeAttribute("title");
15968             e.preventDefault();
15969         }else{
15970             ttp = t.qtip || et.getAttributeNS(ns, cfg.attribute);
15971         }
15972         if(ttp){
15973             showProc = show.defer(tm.showDelay, tm, [{
15974                 el: t, 
15975                 text: ttp, 
15976                 width: et.getAttributeNS(ns, cfg.width),
15977                 autoHide: et.getAttributeNS(ns, cfg.hide) != "user",
15978                 title: et.getAttributeNS(ns, cfg.title),
15979                     cls: et.getAttributeNS(ns, cfg.cls)
15980             }]);
15981         }
15982     };
15983     
15984     var onOut = function(e){
15985         clearTimeout(showProc);
15986         var t = e.getTarget();
15987         if(t && ce && ce.el == t && (tm.autoHide && ce.autoHide !== false)){
15988             hideProc = setTimeout(hide, tm.hideDelay);
15989         }
15990     };
15991     
15992     var onMove = function(e){
15993         if(disabled){
15994             return;
15995         }
15996         xy = e.getXY();
15997         xy[1] += 18;
15998         if(tm.trackMouse && ce){
15999             el.setXY(xy);
16000         }
16001     };
16002     
16003     var onDown = function(e){
16004         clearTimeout(showProc);
16005         clearTimeout(hideProc);
16006         if(!e.within(el)){
16007             if(tm.hideOnClick){
16008                 hide();
16009                 tm.disable();
16010                 tm.enable.defer(100, tm);
16011             }
16012         }
16013     };
16014     
16015     var getPad = function(){
16016         return 2;//bdLeft.getPadding('l')+bdRight.getPadding('r');
16017     };
16018
16019     var show = function(o){
16020         if(disabled){
16021             return;
16022         }
16023         clearTimeout(dismissProc);
16024         ce = o;
16025         if(removeCls){ // in case manually hidden
16026             el.removeClass(removeCls);
16027             removeCls = null;
16028         }
16029         if(ce.cls){
16030             el.addClass(ce.cls);
16031             removeCls = ce.cls;
16032         }
16033         if(ce.title){
16034             tipTitle.update(ce.title);
16035             tipTitle.show();
16036         }else{
16037             tipTitle.update('');
16038             tipTitle.hide();
16039         }
16040         el.dom.style.width  = tm.maxWidth+'px';
16041         //tipBody.dom.style.width = '';
16042         tipBodyText.update(o.text);
16043         var p = getPad(), w = ce.width;
16044         if(!w){
16045             var td = tipBodyText.dom;
16046             var aw = Math.max(td.offsetWidth, td.clientWidth, td.scrollWidth);
16047             if(aw > tm.maxWidth){
16048                 w = tm.maxWidth;
16049             }else if(aw < tm.minWidth){
16050                 w = tm.minWidth;
16051             }else{
16052                 w = aw;
16053             }
16054         }
16055         //tipBody.setWidth(w);
16056         el.setWidth(parseInt(w, 10) + p);
16057         if(ce.autoHide === false){
16058             close.setDisplayed(true);
16059             if(dd){
16060                 dd.unlock();
16061             }
16062         }else{
16063             close.setDisplayed(false);
16064             if(dd){
16065                 dd.lock();
16066             }
16067         }
16068         if(xy){
16069             el.avoidY = xy[1]-18;
16070             el.setXY(xy);
16071         }
16072         if(tm.animate){
16073             el.setOpacity(.1);
16074             el.setStyle("visibility", "visible");
16075             el.fadeIn({callback: afterShow});
16076         }else{
16077             afterShow();
16078         }
16079     };
16080     
16081     var afterShow = function(){
16082         if(ce){
16083             el.show();
16084             esc.enable();
16085             if(tm.autoDismiss && ce.autoHide !== false){
16086                 dismissProc = setTimeout(hide, tm.autoDismissDelay);
16087             }
16088         }
16089     };
16090     
16091     var hide = function(noanim){
16092         clearTimeout(dismissProc);
16093         clearTimeout(hideProc);
16094         ce = null;
16095         if(el.isVisible()){
16096             esc.disable();
16097             if(noanim !== true && tm.animate){
16098                 el.fadeOut({callback: afterHide});
16099             }else{
16100                 afterHide();
16101             } 
16102         }
16103     };
16104     
16105     var afterHide = function(){
16106         el.hide();
16107         if(removeCls){
16108             el.removeClass(removeCls);
16109             removeCls = null;
16110         }
16111     };
16112     
16113     return {
16114         /**
16115         * @cfg {Number} minWidth
16116         * The minimum width of the quick tip (defaults to 40)
16117         */
16118        minWidth : 40,
16119         /**
16120         * @cfg {Number} maxWidth
16121         * The maximum width of the quick tip (defaults to 300)
16122         */
16123        maxWidth : 300,
16124         /**
16125         * @cfg {Boolean} interceptTitles
16126         * True to automatically use the element's DOM title value if available (defaults to false)
16127         */
16128        interceptTitles : false,
16129         /**
16130         * @cfg {Boolean} trackMouse
16131         * True to have the quick tip follow the mouse as it moves over the target element (defaults to false)
16132         */
16133        trackMouse : false,
16134         /**
16135         * @cfg {Boolean} hideOnClick
16136         * True to hide the quick tip if the user clicks anywhere in the document (defaults to true)
16137         */
16138        hideOnClick : true,
16139         /**
16140         * @cfg {Number} showDelay
16141         * Delay in milliseconds before the quick tip displays after the mouse enters the target element (defaults to 500)
16142         */
16143        showDelay : 500,
16144         /**
16145         * @cfg {Number} hideDelay
16146         * Delay in milliseconds before the quick tip hides when autoHide = true (defaults to 200)
16147         */
16148        hideDelay : 200,
16149         /**
16150         * @cfg {Boolean} autoHide
16151         * True to automatically hide the quick tip after the mouse exits the target element (defaults to true).
16152         * Used in conjunction with hideDelay.
16153         */
16154        autoHide : true,
16155         /**
16156         * @cfg {Boolean}
16157         * True to automatically hide the quick tip after a set period of time, regardless of the user's actions
16158         * (defaults to true).  Used in conjunction with autoDismissDelay.
16159         */
16160        autoDismiss : true,
16161         /**
16162         * @cfg {Number}
16163         * Delay in milliseconds before the quick tip hides when autoDismiss = true (defaults to 5000)
16164         */
16165        autoDismissDelay : 5000,
16166        /**
16167         * @cfg {Boolean} animate
16168         * True to turn on fade animation. Defaults to false (ClearType/scrollbar flicker issues in IE7).
16169         */
16170        animate : false,
16171
16172        /**
16173         * @cfg {String} title
16174         * Title text to display (defaults to '').  This can be any valid HTML markup.
16175         */
16176         title: '',
16177        /**
16178         * @cfg {String} text
16179         * Body text to display (defaults to '').  This can be any valid HTML markup.
16180         */
16181         text : '',
16182        /**
16183         * @cfg {String} cls
16184         * A CSS class to apply to the base quick tip element (defaults to '').
16185         */
16186         cls : '',
16187        /**
16188         * @cfg {Number} width
16189         * Width in pixels of the quick tip (defaults to auto).  Width will be ignored if it exceeds the bounds of
16190         * minWidth or maxWidth.
16191         */
16192         width : null,
16193
16194     /**
16195      * Initialize and enable QuickTips for first use.  This should be called once before the first attempt to access
16196      * or display QuickTips in a page.
16197      */
16198        init : function(){
16199           tm = Roo.QuickTips;
16200           cfg = tm.tagConfig;
16201           if(!inited){
16202               if(!Roo.isReady){ // allow calling of init() before onReady
16203                   Roo.onReady(Roo.QuickTips.init, Roo.QuickTips);
16204                   return;
16205               }
16206               el = new Roo.Layer({cls:"x-tip", shadow:"drop", shim: true, constrain:true, shadowOffset:4});
16207               el.fxDefaults = {stopFx: true};
16208               // maximum custom styling
16209               //el.update('<div class="x-tip-top-left"><div class="x-tip-top-right"><div class="x-tip-top"></div></div></div><div class="x-tip-bd-left"><div class="x-tip-bd-right"><div class="x-tip-bd"><div class="x-tip-close"></div><h3></h3><div class="x-tip-bd-inner"></div><div class="x-clear"></div></div></div></div><div class="x-tip-ft-left"><div class="x-tip-ft-right"><div class="x-tip-ft"></div></div></div>');
16210               el.update('<div class="x-tip-bd"><div class="x-tip-close"></div><h3></h3><div class="x-tip-bd-inner"></div><div class="x-clear"></div></div>');              
16211               tipTitle = el.child('h3');
16212               tipTitle.enableDisplayMode("block");
16213               tipBody = el.child('div.x-tip-bd');
16214               tipBodyText = el.child('div.x-tip-bd-inner');
16215               //bdLeft = el.child('div.x-tip-bd-left');
16216               //bdRight = el.child('div.x-tip-bd-right');
16217               close = el.child('div.x-tip-close');
16218               close.enableDisplayMode("block");
16219               close.on("click", hide);
16220               var d = Roo.get(document);
16221               d.on("mousedown", onDown);
16222               d.on("mouseover", onOver);
16223               d.on("mouseout", onOut);
16224               d.on("mousemove", onMove);
16225               esc = d.addKeyListener(27, hide);
16226               esc.disable();
16227               if(Roo.dd.DD){
16228                   dd = el.initDD("default", null, {
16229                       onDrag : function(){
16230                           el.sync();  
16231                       }
16232                   });
16233                   dd.setHandleElId(tipTitle.id);
16234                   dd.lock();
16235               }
16236               inited = true;
16237           }
16238           this.enable(); 
16239        },
16240
16241     /**
16242      * Configures a new quick tip instance and assigns it to a target element.  The following config options
16243      * are supported:
16244      * <pre>
16245 Property    Type                   Description
16246 ----------  ---------------------  ------------------------------------------------------------------------
16247 target      Element/String/Array   An Element, id or array of ids that this quick tip should be tied to
16248      * </ul>
16249      * @param {Object} config The config object
16250      */
16251        register : function(config){
16252            var cs = config instanceof Array ? config : arguments;
16253            for(var i = 0, len = cs.length; i < len; i++) {
16254                var c = cs[i];
16255                var target = c.target;
16256                if(target){
16257                    if(target instanceof Array){
16258                        for(var j = 0, jlen = target.length; j < jlen; j++){
16259                            tagEls[target[j]] = c;
16260                        }
16261                    }else{
16262                        tagEls[typeof target == 'string' ? target : Roo.id(target)] = c;
16263                    }
16264                }
16265            }
16266        },
16267
16268     /**
16269      * Removes this quick tip from its element and destroys it.
16270      * @param {String/HTMLElement/Element} el The element from which the quick tip is to be removed.
16271      */
16272        unregister : function(el){
16273            delete tagEls[Roo.id(el)];
16274        },
16275
16276     /**
16277      * Enable this quick tip.
16278      */
16279        enable : function(){
16280            if(inited && disabled){
16281                locks.pop();
16282                if(locks.length < 1){
16283                    disabled = false;
16284                }
16285            }
16286        },
16287
16288     /**
16289      * Disable this quick tip.
16290      */
16291        disable : function(){
16292           disabled = true;
16293           clearTimeout(showProc);
16294           clearTimeout(hideProc);
16295           clearTimeout(dismissProc);
16296           if(ce){
16297               hide(true);
16298           }
16299           locks.push(1);
16300        },
16301
16302     /**
16303      * Returns true if the quick tip is enabled, else false.
16304      */
16305        isEnabled : function(){
16306             return !disabled;
16307        },
16308
16309         // private
16310        tagConfig : {
16311            namespace : "ext",
16312            attribute : "qtip",
16313            width : "width",
16314            target : "target",
16315            title : "qtitle",
16316            hide : "hide",
16317            cls : "qclass"
16318        }
16319    };
16320 }();
16321
16322 // backwards compat
16323 Roo.QuickTips.tips = Roo.QuickTips.register;/*
16324  * Based on:
16325  * Ext JS Library 1.1.1
16326  * Copyright(c) 2006-2007, Ext JS, LLC.
16327  *
16328  * Originally Released Under LGPL - original licence link has changed is not relivant.
16329  *
16330  * Fork - LGPL
16331  * <script type="text/javascript">
16332  */
16333  
16334
16335 /**
16336  * @class Roo.tree.TreePanel
16337  * @extends Roo.data.Tree
16338
16339  * @cfg {Boolean} rootVisible false to hide the root node (defaults to true)
16340  * @cfg {Boolean} lines false to disable tree lines (defaults to true)
16341  * @cfg {Boolean} enableDD true to enable drag and drop
16342  * @cfg {Boolean} enableDrag true to enable just drag
16343  * @cfg {Boolean} enableDrop true to enable just drop
16344  * @cfg {Object} dragConfig Custom config to pass to the {@link Roo.tree.TreeDragZone} instance
16345  * @cfg {Object} dropConfig Custom config to pass to the {@link Roo.tree.TreeDropZone} instance
16346  * @cfg {String} ddGroup The DD group this TreePanel belongs to
16347  * @cfg {String} ddAppendOnly True if the tree should only allow append drops (use for trees which are sorted)
16348  * @cfg {Boolean} ddScroll true to enable YUI body scrolling
16349  * @cfg {Boolean} containerScroll true to register this container with ScrollManager
16350  * @cfg {Boolean} hlDrop false to disable node highlight on drop (defaults to the value of Roo.enableFx)
16351  * @cfg {String} hlColor The color of the node highlight (defaults to C3DAF9)
16352  * @cfg {Boolean} animate true to enable animated expand/collapse (defaults to the value of Roo.enableFx)
16353  * @cfg {Boolean} singleExpand true if only 1 node per branch may be expanded
16354  * @cfg {Boolean} selModel A tree selection model to use with this TreePanel (defaults to a {@link Roo.tree.DefaultSelectionModel})
16355  * @cfg {Boolean} loader A TreeLoader for use with this TreePanel
16356  * @cfg {String} pathSeparator The token used to separate sub-paths in path strings (defaults to '/')
16357  * @cfg {Function} renderer Sets the rendering (formatting) function for the nodes. to return HTML markup for the tree view. The render function is called with  the following parameters:<ul><li>The {Object} The data for the node.</li></ul>
16358  * @cfg {Function} rendererTip Sets the rendering (formatting) function for the nodes hovertip to return HTML markup for the tree view. The render function is called with  the following parameters:<ul><li>The {Object} The data for the node.</li></ul>
16359  * 
16360  * @constructor
16361  * @param {String/HTMLElement/Element} el The container element
16362  * @param {Object} config
16363  */
16364 Roo.tree.TreePanel = function(el, config){
16365     var root = false;
16366     var loader = false;
16367     if (config.root) {
16368         root = config.root;
16369         delete config.root;
16370     }
16371     if (config.loader) {
16372         loader = config.loader;
16373         delete config.loader;
16374     }
16375     
16376     Roo.apply(this, config);
16377     Roo.tree.TreePanel.superclass.constructor.call(this);
16378     this.el = Roo.get(el);
16379     this.el.addClass('x-tree');
16380     //console.log(root);
16381     if (root) {
16382         this.setRootNode( Roo.factory(root, Roo.tree));
16383     }
16384     if (loader) {
16385         this.loader = Roo.factory(loader, Roo.tree);
16386     }
16387    /**
16388     * Read-only. The id of the container element becomes this TreePanel's id.
16389     */
16390    this.id = this.el.id;
16391    this.addEvents({
16392         /**
16393         * @event beforeload
16394         * Fires before a node is loaded, return false to cancel
16395         * @param {Node} node The node being loaded
16396         */
16397         "beforeload" : true,
16398         /**
16399         * @event load
16400         * Fires when a node is loaded
16401         * @param {Node} node The node that was loaded
16402         */
16403         "load" : true,
16404         /**
16405         * @event textchange
16406         * Fires when the text for a node is changed
16407         * @param {Node} node The node
16408         * @param {String} text The new text
16409         * @param {String} oldText The old text
16410         */
16411         "textchange" : true,
16412         /**
16413         * @event beforeexpand
16414         * Fires before a node is expanded, return false to cancel.
16415         * @param {Node} node The node
16416         * @param {Boolean} deep
16417         * @param {Boolean} anim
16418         */
16419         "beforeexpand" : true,
16420         /**
16421         * @event beforecollapse
16422         * Fires before a node is collapsed, return false to cancel.
16423         * @param {Node} node The node
16424         * @param {Boolean} deep
16425         * @param {Boolean} anim
16426         */
16427         "beforecollapse" : true,
16428         /**
16429         * @event expand
16430         * Fires when a node is expanded
16431         * @param {Node} node The node
16432         */
16433         "expand" : true,
16434         /**
16435         * @event disabledchange
16436         * Fires when the disabled status of a node changes
16437         * @param {Node} node The node
16438         * @param {Boolean} disabled
16439         */
16440         "disabledchange" : true,
16441         /**
16442         * @event collapse
16443         * Fires when a node is collapsed
16444         * @param {Node} node The node
16445         */
16446         "collapse" : true,
16447         /**
16448         * @event beforeclick
16449         * Fires before click processing on a node. Return false to cancel the default action.
16450         * @param {Node} node The node
16451         * @param {Roo.EventObject} e The event object
16452         */
16453         "beforeclick":true,
16454         /**
16455         * @event checkchange
16456         * Fires when a node with a checkbox's checked property changes
16457         * @param {Node} this This node
16458         * @param {Boolean} checked
16459         */
16460         "checkchange":true,
16461         /**
16462         * @event click
16463         * Fires when a node is clicked
16464         * @param {Node} node The node
16465         * @param {Roo.EventObject} e The event object
16466         */
16467         "click":true,
16468         /**
16469         * @event dblclick
16470         * Fires when a node is double clicked
16471         * @param {Node} node The node
16472         * @param {Roo.EventObject} e The event object
16473         */
16474         "dblclick":true,
16475         /**
16476         * @event contextmenu
16477         * Fires when a node is right clicked
16478         * @param {Node} node The node
16479         * @param {Roo.EventObject} e The event object
16480         */
16481         "contextmenu":true,
16482         /**
16483         * @event beforechildrenrendered
16484         * Fires right before the child nodes for a node are rendered
16485         * @param {Node} node The node
16486         */
16487         "beforechildrenrendered":true,
16488        /**
16489              * @event startdrag
16490              * Fires when a node starts being dragged
16491              * @param {Roo.tree.TreePanel} this
16492              * @param {Roo.tree.TreeNode} node
16493              * @param {event} e The raw browser event
16494              */ 
16495             "startdrag" : true,
16496             /**
16497              * @event enddrag
16498              * Fires when a drag operation is complete
16499              * @param {Roo.tree.TreePanel} this
16500              * @param {Roo.tree.TreeNode} node
16501              * @param {event} e The raw browser event
16502              */
16503             "enddrag" : true,
16504             /**
16505              * @event dragdrop
16506              * Fires when a dragged node is dropped on a valid DD target
16507              * @param {Roo.tree.TreePanel} this
16508              * @param {Roo.tree.TreeNode} node
16509              * @param {DD} dd The dd it was dropped on
16510              * @param {event} e The raw browser event
16511              */
16512             "dragdrop" : true,
16513             /**
16514              * @event beforenodedrop
16515              * Fires when a DD object is dropped on a node in this tree for preprocessing. Return false to cancel the drop. The dropEvent
16516              * passed to handlers has the following properties:<br />
16517              * <ul style="padding:5px;padding-left:16px;">
16518              * <li>tree - The TreePanel</li>
16519              * <li>target - The node being targeted for the drop</li>
16520              * <li>data - The drag data from the drag source</li>
16521              * <li>point - The point of the drop - append, above or below</li>
16522              * <li>source - The drag source</li>
16523              * <li>rawEvent - Raw mouse event</li>
16524              * <li>dropNode - Drop node(s) provided by the source <b>OR</b> you can supply node(s)
16525              * to be inserted by setting them on this object.</li>
16526              * <li>cancel - Set this to true to cancel the drop.</li>
16527              * </ul>
16528              * @param {Object} dropEvent
16529              */
16530             "beforenodedrop" : true,
16531             /**
16532              * @event nodedrop
16533              * Fires after a DD object is dropped on a node in this tree. The dropEvent
16534              * passed to handlers has the following properties:<br />
16535              * <ul style="padding:5px;padding-left:16px;">
16536              * <li>tree - The TreePanel</li>
16537              * <li>target - The node being targeted for the drop</li>
16538              * <li>data - The drag data from the drag source</li>
16539              * <li>point - The point of the drop - append, above or below</li>
16540              * <li>source - The drag source</li>
16541              * <li>rawEvent - Raw mouse event</li>
16542              * <li>dropNode - Dropped node(s).</li>
16543              * </ul>
16544              * @param {Object} dropEvent
16545              */
16546             "nodedrop" : true,
16547              /**
16548              * @event nodedragover
16549              * Fires when a tree node is being targeted for a drag drop, return false to signal drop not allowed. The dragOverEvent
16550              * passed to handlers has the following properties:<br />
16551              * <ul style="padding:5px;padding-left:16px;">
16552              * <li>tree - The TreePanel</li>
16553              * <li>target - The node being targeted for the drop</li>
16554              * <li>data - The drag data from the drag source</li>
16555              * <li>point - The point of the drop - append, above or below</li>
16556              * <li>source - The drag source</li>
16557              * <li>rawEvent - Raw mouse event</li>
16558              * <li>dropNode - Drop node(s) provided by the source.</li>
16559              * <li>cancel - Set this to true to signal drop not allowed.</li>
16560              * </ul>
16561              * @param {Object} dragOverEvent
16562              */
16563             "nodedragover" : true
16564         
16565    });
16566    if(this.singleExpand){
16567        this.on("beforeexpand", this.restrictExpand, this);
16568    }
16569 };
16570 Roo.extend(Roo.tree.TreePanel, Roo.data.Tree, {
16571     rootVisible : true,
16572     animate: Roo.enableFx,
16573     lines : true,
16574     enableDD : false,
16575     hlDrop : Roo.enableFx,
16576   
16577     renderer: false,
16578     
16579     rendererTip: false,
16580     // private
16581     restrictExpand : function(node){
16582         var p = node.parentNode;
16583         if(p){
16584             if(p.expandedChild && p.expandedChild.parentNode == p){
16585                 p.expandedChild.collapse();
16586             }
16587             p.expandedChild = node;
16588         }
16589     },
16590
16591     // private override
16592     setRootNode : function(node){
16593         Roo.tree.TreePanel.superclass.setRootNode.call(this, node);
16594         if(!this.rootVisible){
16595             node.ui = new Roo.tree.RootTreeNodeUI(node);
16596         }
16597         return node;
16598     },
16599
16600     /**
16601      * Returns the container element for this TreePanel
16602      */
16603     getEl : function(){
16604         return this.el;
16605     },
16606
16607     /**
16608      * Returns the default TreeLoader for this TreePanel
16609      */
16610     getLoader : function(){
16611         return this.loader;
16612     },
16613
16614     /**
16615      * Expand all nodes
16616      */
16617     expandAll : function(){
16618         this.root.expand(true);
16619     },
16620
16621     /**
16622      * Collapse all nodes
16623      */
16624     collapseAll : function(){
16625         this.root.collapse(true);
16626     },
16627
16628     /**
16629      * Returns the selection model used by this TreePanel
16630      */
16631     getSelectionModel : function(){
16632         if(!this.selModel){
16633             this.selModel = new Roo.tree.DefaultSelectionModel();
16634         }
16635         return this.selModel;
16636     },
16637
16638     /**
16639      * Retrieve an array of checked nodes, or an array of a specific attribute of checked nodes (e.g. "id")
16640      * @param {String} attribute (optional) Defaults to null (return the actual nodes)
16641      * @param {TreeNode} startNode (optional) The node to start from, defaults to the root
16642      * @return {Array}
16643      */
16644     getChecked : function(a, startNode){
16645         startNode = startNode || this.root;
16646         var r = [];
16647         var f = function(){
16648             if(this.attributes.checked){
16649                 r.push(!a ? this : (a == 'id' ? this.id : this.attributes[a]));
16650             }
16651         }
16652         startNode.cascade(f);
16653         return r;
16654     },
16655
16656     /**
16657      * Expands a specified path in this TreePanel. A path can be retrieved from a node with {@link Roo.data.Node#getPath}
16658      * @param {String} path
16659      * @param {String} attr (optional) The attribute used in the path (see {@link Roo.data.Node#getPath} for more info)
16660      * @param {Function} callback (optional) The callback to call when the expand is complete. The callback will be called with
16661      * (bSuccess, oLastNode) where bSuccess is if the expand was successful and oLastNode is the last node that was expanded.
16662      */
16663     expandPath : function(path, attr, callback){
16664         attr = attr || "id";
16665         var keys = path.split(this.pathSeparator);
16666         var curNode = this.root;
16667         if(curNode.attributes[attr] != keys[1]){ // invalid root
16668             if(callback){
16669                 callback(false, null);
16670             }
16671             return;
16672         }
16673         var index = 1;
16674         var f = function(){
16675             if(++index == keys.length){
16676                 if(callback){
16677                     callback(true, curNode);
16678                 }
16679                 return;
16680             }
16681             var c = curNode.findChild(attr, keys[index]);
16682             if(!c){
16683                 if(callback){
16684                     callback(false, curNode);
16685                 }
16686                 return;
16687             }
16688             curNode = c;
16689             c.expand(false, false, f);
16690         };
16691         curNode.expand(false, false, f);
16692     },
16693
16694     /**
16695      * Selects the node in this tree at the specified path. A path can be retrieved from a node with {@link Roo.data.Node#getPath}
16696      * @param {String} path
16697      * @param {String} attr (optional) The attribute used in the path (see {@link Roo.data.Node#getPath} for more info)
16698      * @param {Function} callback (optional) The callback to call when the selection is complete. The callback will be called with
16699      * (bSuccess, oSelNode) where bSuccess is if the selection was successful and oSelNode is the selected node.
16700      */
16701     selectPath : function(path, attr, callback){
16702         attr = attr || "id";
16703         var keys = path.split(this.pathSeparator);
16704         var v = keys.pop();
16705         if(keys.length > 0){
16706             var f = function(success, node){
16707                 if(success && node){
16708                     var n = node.findChild(attr, v);
16709                     if(n){
16710                         n.select();
16711                         if(callback){
16712                             callback(true, n);
16713                         }
16714                     }else if(callback){
16715                         callback(false, n);
16716                     }
16717                 }else{
16718                     if(callback){
16719                         callback(false, n);
16720                     }
16721                 }
16722             };
16723             this.expandPath(keys.join(this.pathSeparator), attr, f);
16724         }else{
16725             this.root.select();
16726             if(callback){
16727                 callback(true, this.root);
16728             }
16729         }
16730     },
16731
16732     getTreeEl : function(){
16733         return this.el;
16734     },
16735
16736     /**
16737      * Trigger rendering of this TreePanel
16738      */
16739     render : function(){
16740         if (this.innerCt) {
16741             return this; // stop it rendering more than once!!
16742         }
16743         
16744         this.innerCt = this.el.createChild({tag:"ul",
16745                cls:"x-tree-root-ct " +
16746                (this.lines ? "x-tree-lines" : "x-tree-no-lines")});
16747
16748         if(this.containerScroll){
16749             Roo.dd.ScrollManager.register(this.el);
16750         }
16751         if((this.enableDD || this.enableDrop) && !this.dropZone){
16752            /**
16753             * The dropZone used by this tree if drop is enabled
16754             * @type Roo.tree.TreeDropZone
16755             */
16756              this.dropZone = new Roo.tree.TreeDropZone(this, this.dropConfig || {
16757                ddGroup: this.ddGroup || "TreeDD", appendOnly: this.ddAppendOnly === true
16758            });
16759         }
16760         if((this.enableDD || this.enableDrag) && !this.dragZone){
16761            /**
16762             * The dragZone used by this tree if drag is enabled
16763             * @type Roo.tree.TreeDragZone
16764             */
16765             this.dragZone = new Roo.tree.TreeDragZone(this, this.dragConfig || {
16766                ddGroup: this.ddGroup || "TreeDD",
16767                scroll: this.ddScroll
16768            });
16769         }
16770         this.getSelectionModel().init(this);
16771         if (!this.root) {
16772             console.log("ROOT not set in tree");
16773             return;
16774         }
16775         this.root.render();
16776         if(!this.rootVisible){
16777             this.root.renderChildren();
16778         }
16779         return this;
16780     }
16781 });/*
16782  * Based on:
16783  * Ext JS Library 1.1.1
16784  * Copyright(c) 2006-2007, Ext JS, LLC.
16785  *
16786  * Originally Released Under LGPL - original licence link has changed is not relivant.
16787  *
16788  * Fork - LGPL
16789  * <script type="text/javascript">
16790  */
16791  
16792
16793 /**
16794  * @class Roo.tree.DefaultSelectionModel
16795  * @extends Roo.util.Observable
16796  * The default single selection for a TreePanel.
16797  */
16798 Roo.tree.DefaultSelectionModel = function(){
16799    this.selNode = null;
16800    
16801    this.addEvents({
16802        /**
16803         * @event selectionchange
16804         * Fires when the selected node changes
16805         * @param {DefaultSelectionModel} this
16806         * @param {TreeNode} node the new selection
16807         */
16808        "selectionchange" : true,
16809
16810        /**
16811         * @event beforeselect
16812         * Fires before the selected node changes, return false to cancel the change
16813         * @param {DefaultSelectionModel} this
16814         * @param {TreeNode} node the new selection
16815         * @param {TreeNode} node the old selection
16816         */
16817        "beforeselect" : true
16818    });
16819 };
16820
16821 Roo.extend(Roo.tree.DefaultSelectionModel, Roo.util.Observable, {
16822     init : function(tree){
16823         this.tree = tree;
16824         tree.getTreeEl().on("keydown", this.onKeyDown, this);
16825         tree.on("click", this.onNodeClick, this);
16826     },
16827     
16828     onNodeClick : function(node, e){
16829         if (e.ctrlKey && this.selNode == node)  {
16830             this.unselect(node);
16831             return;
16832         }
16833         this.select(node);
16834     },
16835     
16836     /**
16837      * Select a node.
16838      * @param {TreeNode} node The node to select
16839      * @return {TreeNode} The selected node
16840      */
16841     select : function(node){
16842         var last = this.selNode;
16843         if(last != node && this.fireEvent('beforeselect', this, node, last) !== false){
16844             if(last){
16845                 last.ui.onSelectedChange(false);
16846             }
16847             this.selNode = node;
16848             node.ui.onSelectedChange(true);
16849             this.fireEvent("selectionchange", this, node, last);
16850         }
16851         return node;
16852     },
16853     
16854     /**
16855      * Deselect a node.
16856      * @param {TreeNode} node The node to unselect
16857      */
16858     unselect : function(node){
16859         if(this.selNode == node){
16860             this.clearSelections();
16861         }    
16862     },
16863     
16864     /**
16865      * Clear all selections
16866      */
16867     clearSelections : function(){
16868         var n = this.selNode;
16869         if(n){
16870             n.ui.onSelectedChange(false);
16871             this.selNode = null;
16872             this.fireEvent("selectionchange", this, null);
16873         }
16874         return n;
16875     },
16876     
16877     /**
16878      * Get the selected node
16879      * @return {TreeNode} The selected node
16880      */
16881     getSelectedNode : function(){
16882         return this.selNode;    
16883     },
16884     
16885     /**
16886      * Returns true if the node is selected
16887      * @param {TreeNode} node The node to check
16888      * @return {Boolean}
16889      */
16890     isSelected : function(node){
16891         return this.selNode == node;  
16892     },
16893
16894     /**
16895      * Selects the node above the selected node in the tree, intelligently walking the nodes
16896      * @return TreeNode The new selection
16897      */
16898     selectPrevious : function(){
16899         var s = this.selNode || this.lastSelNode;
16900         if(!s){
16901             return null;
16902         }
16903         var ps = s.previousSibling;
16904         if(ps){
16905             if(!ps.isExpanded() || ps.childNodes.length < 1){
16906                 return this.select(ps);
16907             } else{
16908                 var lc = ps.lastChild;
16909                 while(lc && lc.isExpanded() && lc.childNodes.length > 0){
16910                     lc = lc.lastChild;
16911                 }
16912                 return this.select(lc);
16913             }
16914         } else if(s.parentNode && (this.tree.rootVisible || !s.parentNode.isRoot)){
16915             return this.select(s.parentNode);
16916         }
16917         return null;
16918     },
16919
16920     /**
16921      * Selects the node above the selected node in the tree, intelligently walking the nodes
16922      * @return TreeNode The new selection
16923      */
16924     selectNext : function(){
16925         var s = this.selNode || this.lastSelNode;
16926         if(!s){
16927             return null;
16928         }
16929         if(s.firstChild && s.isExpanded()){
16930              return this.select(s.firstChild);
16931          }else if(s.nextSibling){
16932              return this.select(s.nextSibling);
16933          }else if(s.parentNode){
16934             var newS = null;
16935             s.parentNode.bubble(function(){
16936                 if(this.nextSibling){
16937                     newS = this.getOwnerTree().selModel.select(this.nextSibling);
16938                     return false;
16939                 }
16940             });
16941             return newS;
16942          }
16943         return null;
16944     },
16945
16946     onKeyDown : function(e){
16947         var s = this.selNode || this.lastSelNode;
16948         // undesirable, but required
16949         var sm = this;
16950         if(!s){
16951             return;
16952         }
16953         var k = e.getKey();
16954         switch(k){
16955              case e.DOWN:
16956                  e.stopEvent();
16957                  this.selectNext();
16958              break;
16959              case e.UP:
16960                  e.stopEvent();
16961                  this.selectPrevious();
16962              break;
16963              case e.RIGHT:
16964                  e.preventDefault();
16965                  if(s.hasChildNodes()){
16966                      if(!s.isExpanded()){
16967                          s.expand();
16968                      }else if(s.firstChild){
16969                          this.select(s.firstChild, e);
16970                      }
16971                  }
16972              break;
16973              case e.LEFT:
16974                  e.preventDefault();
16975                  if(s.hasChildNodes() && s.isExpanded()){
16976                      s.collapse();
16977                  }else if(s.parentNode && (this.tree.rootVisible || s.parentNode != this.tree.getRootNode())){
16978                      this.select(s.parentNode, e);
16979                  }
16980              break;
16981         };
16982     }
16983 });
16984
16985 /**
16986  * @class Roo.tree.MultiSelectionModel
16987  * @extends Roo.util.Observable
16988  * Multi selection for a TreePanel.
16989  */
16990 Roo.tree.MultiSelectionModel = function(){
16991    this.selNodes = [];
16992    this.selMap = {};
16993    this.addEvents({
16994        /**
16995         * @event selectionchange
16996         * Fires when the selected nodes change
16997         * @param {MultiSelectionModel} this
16998         * @param {Array} nodes Array of the selected nodes
16999         */
17000        "selectionchange" : true
17001    });
17002 };
17003
17004 Roo.extend(Roo.tree.MultiSelectionModel, Roo.util.Observable, {
17005     init : function(tree){
17006         this.tree = tree;
17007         tree.getTreeEl().on("keydown", this.onKeyDown, this);
17008         tree.on("click", this.onNodeClick, this);
17009     },
17010     
17011     onNodeClick : function(node, e){
17012         this.select(node, e, e.ctrlKey);
17013     },
17014     
17015     /**
17016      * Select a node.
17017      * @param {TreeNode} node The node to select
17018      * @param {EventObject} e (optional) An event associated with the selection
17019      * @param {Boolean} keepExisting True to retain existing selections
17020      * @return {TreeNode} The selected node
17021      */
17022     select : function(node, e, keepExisting){
17023         if(keepExisting !== true){
17024             this.clearSelections(true);
17025         }
17026         if(this.isSelected(node)){
17027             this.lastSelNode = node;
17028             return node;
17029         }
17030         this.selNodes.push(node);
17031         this.selMap[node.id] = node;
17032         this.lastSelNode = node;
17033         node.ui.onSelectedChange(true);
17034         this.fireEvent("selectionchange", this, this.selNodes);
17035         return node;
17036     },
17037     
17038     /**
17039      * Deselect a node.
17040      * @param {TreeNode} node The node to unselect
17041      */
17042     unselect : function(node){
17043         if(this.selMap[node.id]){
17044             node.ui.onSelectedChange(false);
17045             var sn = this.selNodes;
17046             var index = -1;
17047             if(sn.indexOf){
17048                 index = sn.indexOf(node);
17049             }else{
17050                 for(var i = 0, len = sn.length; i < len; i++){
17051                     if(sn[i] == node){
17052                         index = i;
17053                         break;
17054                     }
17055                 }
17056             }
17057             if(index != -1){
17058                 this.selNodes.splice(index, 1);
17059             }
17060             delete this.selMap[node.id];
17061             this.fireEvent("selectionchange", this, this.selNodes);
17062         }
17063     },
17064     
17065     /**
17066      * Clear all selections
17067      */
17068     clearSelections : function(suppressEvent){
17069         var sn = this.selNodes;
17070         if(sn.length > 0){
17071             for(var i = 0, len = sn.length; i < len; i++){
17072                 sn[i].ui.onSelectedChange(false);
17073             }
17074             this.selNodes = [];
17075             this.selMap = {};
17076             if(suppressEvent !== true){
17077                 this.fireEvent("selectionchange", this, this.selNodes);
17078             }
17079         }
17080     },
17081     
17082     /**
17083      * Returns true if the node is selected
17084      * @param {TreeNode} node The node to check
17085      * @return {Boolean}
17086      */
17087     isSelected : function(node){
17088         return this.selMap[node.id] ? true : false;  
17089     },
17090     
17091     /**
17092      * Returns an array of the selected nodes
17093      * @return {Array}
17094      */
17095     getSelectedNodes : function(){
17096         return this.selNodes;    
17097     },
17098
17099     onKeyDown : Roo.tree.DefaultSelectionModel.prototype.onKeyDown,
17100
17101     selectNext : Roo.tree.DefaultSelectionModel.prototype.selectNext,
17102
17103     selectPrevious : Roo.tree.DefaultSelectionModel.prototype.selectPrevious
17104 });/*
17105  * Based on:
17106  * Ext JS Library 1.1.1
17107  * Copyright(c) 2006-2007, Ext JS, LLC.
17108  *
17109  * Originally Released Under LGPL - original licence link has changed is not relivant.
17110  *
17111  * Fork - LGPL
17112  * <script type="text/javascript">
17113  */
17114  
17115 /**
17116  * @class Roo.tree.TreeNode
17117  * @extends Roo.data.Node
17118  * @cfg {String} text The text for this node
17119  * @cfg {Boolean} expanded true to start the node expanded
17120  * @cfg {Boolean} allowDrag false to make this node undraggable if DD is on (defaults to true)
17121  * @cfg {Boolean} allowDrop false if this node cannot be drop on
17122  * @cfg {Boolean} disabled true to start the node disabled
17123  * @cfg {String} icon The path to an icon for the node. The preferred way to do this
17124  * is to use the cls or iconCls attributes and add the icon via a CSS background image.
17125  * @cfg {String} cls A css class to be added to the node
17126  * @cfg {String} iconCls A css class to be added to the nodes icon element for applying css background images
17127  * @cfg {String} href URL of the link used for the node (defaults to #)
17128  * @cfg {String} hrefTarget target frame for the link
17129  * @cfg {String} qtip An Ext QuickTip for the node
17130  * @cfg {String} qtipCfg An Ext QuickTip config for the node (used instead of qtip)
17131  * @cfg {Boolean} singleClickExpand True for single click expand on this node
17132  * @cfg {Function} uiProvider A UI <b>class</b> to use for this node (defaults to Roo.tree.TreeNodeUI)
17133  * @cfg {Boolean} checked True to render a checked checkbox for this node, false to render an unchecked checkbox
17134  * (defaults to undefined with no checkbox rendered)
17135  * @constructor
17136  * @param {Object/String} attributes The attributes/config for the node or just a string with the text for the node
17137  */
17138 Roo.tree.TreeNode = function(attributes){
17139     attributes = attributes || {};
17140     if(typeof attributes == "string"){
17141         attributes = {text: attributes};
17142     }
17143     this.childrenRendered = false;
17144     this.rendered = false;
17145     Roo.tree.TreeNode.superclass.constructor.call(this, attributes);
17146     this.expanded = attributes.expanded === true;
17147     this.isTarget = attributes.isTarget !== false;
17148     this.draggable = attributes.draggable !== false && attributes.allowDrag !== false;
17149     this.allowChildren = attributes.allowChildren !== false && attributes.allowDrop !== false;
17150
17151     /**
17152      * Read-only. The text for this node. To change it use setText().
17153      * @type String
17154      */
17155     this.text = attributes.text;
17156     /**
17157      * True if this node is disabled.
17158      * @type Boolean
17159      */
17160     this.disabled = attributes.disabled === true;
17161
17162     this.addEvents({
17163         /**
17164         * @event textchange
17165         * Fires when the text for this node is changed
17166         * @param {Node} this This node
17167         * @param {String} text The new text
17168         * @param {String} oldText The old text
17169         */
17170         "textchange" : true,
17171         /**
17172         * @event beforeexpand
17173         * Fires before this node is expanded, return false to cancel.
17174         * @param {Node} this This node
17175         * @param {Boolean} deep
17176         * @param {Boolean} anim
17177         */
17178         "beforeexpand" : true,
17179         /**
17180         * @event beforecollapse
17181         * Fires before this node is collapsed, return false to cancel.
17182         * @param {Node} this This node
17183         * @param {Boolean} deep
17184         * @param {Boolean} anim
17185         */
17186         "beforecollapse" : true,
17187         /**
17188         * @event expand
17189         * Fires when this node is expanded
17190         * @param {Node} this This node
17191         */
17192         "expand" : true,
17193         /**
17194         * @event disabledchange
17195         * Fires when the disabled status of this node changes
17196         * @param {Node} this This node
17197         * @param {Boolean} disabled
17198         */
17199         "disabledchange" : true,
17200         /**
17201         * @event collapse
17202         * Fires when this node is collapsed
17203         * @param {Node} this This node
17204         */
17205         "collapse" : true,
17206         /**
17207         * @event beforeclick
17208         * Fires before click processing. Return false to cancel the default action.
17209         * @param {Node} this This node
17210         * @param {Roo.EventObject} e The event object
17211         */
17212         "beforeclick":true,
17213         /**
17214         * @event checkchange
17215         * Fires when a node with a checkbox's checked property changes
17216         * @param {Node} this This node
17217         * @param {Boolean} checked
17218         */
17219         "checkchange":true,
17220         /**
17221         * @event click
17222         * Fires when this node is clicked
17223         * @param {Node} this This node
17224         * @param {Roo.EventObject} e The event object
17225         */
17226         "click":true,
17227         /**
17228         * @event dblclick
17229         * Fires when this node is double clicked
17230         * @param {Node} this This node
17231         * @param {Roo.EventObject} e The event object
17232         */
17233         "dblclick":true,
17234         /**
17235         * @event contextmenu
17236         * Fires when this node is right clicked
17237         * @param {Node} this This node
17238         * @param {Roo.EventObject} e The event object
17239         */
17240         "contextmenu":true,
17241         /**
17242         * @event beforechildrenrendered
17243         * Fires right before the child nodes for this node are rendered
17244         * @param {Node} this This node
17245         */
17246         "beforechildrenrendered":true
17247     });
17248
17249     var uiClass = this.attributes.uiProvider || Roo.tree.TreeNodeUI;
17250
17251     /**
17252      * Read-only. The UI for this node
17253      * @type TreeNodeUI
17254      */
17255     this.ui = new uiClass(this);
17256 };
17257 Roo.extend(Roo.tree.TreeNode, Roo.data.Node, {
17258     preventHScroll: true,
17259     /**
17260      * Returns true if this node is expanded
17261      * @return {Boolean}
17262      */
17263     isExpanded : function(){
17264         return this.expanded;
17265     },
17266
17267     /**
17268      * Returns the UI object for this node
17269      * @return {TreeNodeUI}
17270      */
17271     getUI : function(){
17272         return this.ui;
17273     },
17274
17275     // private override
17276     setFirstChild : function(node){
17277         var of = this.firstChild;
17278         Roo.tree.TreeNode.superclass.setFirstChild.call(this, node);
17279         if(this.childrenRendered && of && node != of){
17280             of.renderIndent(true, true);
17281         }
17282         if(this.rendered){
17283             this.renderIndent(true, true);
17284         }
17285     },
17286
17287     // private override
17288     setLastChild : function(node){
17289         var ol = this.lastChild;
17290         Roo.tree.TreeNode.superclass.setLastChild.call(this, node);
17291         if(this.childrenRendered && ol && node != ol){
17292             ol.renderIndent(true, true);
17293         }
17294         if(this.rendered){
17295             this.renderIndent(true, true);
17296         }
17297     },
17298
17299     // these methods are overridden to provide lazy rendering support
17300     // private override
17301     appendChild : function(){
17302         var node = Roo.tree.TreeNode.superclass.appendChild.apply(this, arguments);
17303         if(node && this.childrenRendered){
17304             node.render();
17305         }
17306         this.ui.updateExpandIcon();
17307         return node;
17308     },
17309
17310     // private override
17311     removeChild : function(node){
17312         this.ownerTree.getSelectionModel().unselect(node);
17313         Roo.tree.TreeNode.superclass.removeChild.apply(this, arguments);
17314         // if it's been rendered remove dom node
17315         if(this.childrenRendered){
17316             node.ui.remove();
17317         }
17318         if(this.childNodes.length < 1){
17319             this.collapse(false, false);
17320         }else{
17321             this.ui.updateExpandIcon();
17322         }
17323         if(!this.firstChild) {
17324             this.childrenRendered = false;
17325         }
17326         return node;
17327     },
17328
17329     // private override
17330     insertBefore : function(node, refNode){
17331         var newNode = Roo.tree.TreeNode.superclass.insertBefore.apply(this, arguments);
17332         if(newNode && refNode && this.childrenRendered){
17333             node.render();
17334         }
17335         this.ui.updateExpandIcon();
17336         return newNode;
17337     },
17338
17339     /**
17340      * Sets the text for this node
17341      * @param {String} text
17342      */
17343     setText : function(text){
17344         var oldText = this.text;
17345         this.text = text;
17346         this.attributes.text = text;
17347         if(this.rendered){ // event without subscribing
17348             this.ui.onTextChange(this, text, oldText);
17349         }
17350         this.fireEvent("textchange", this, text, oldText);
17351     },
17352
17353     /**
17354      * Triggers selection of this node
17355      */
17356     select : function(){
17357         this.getOwnerTree().getSelectionModel().select(this);
17358     },
17359
17360     /**
17361      * Triggers deselection of this node
17362      */
17363     unselect : function(){
17364         this.getOwnerTree().getSelectionModel().unselect(this);
17365     },
17366
17367     /**
17368      * Returns true if this node is selected
17369      * @return {Boolean}
17370      */
17371     isSelected : function(){
17372         return this.getOwnerTree().getSelectionModel().isSelected(this);
17373     },
17374
17375     /**
17376      * Expand this node.
17377      * @param {Boolean} deep (optional) True to expand all children as well
17378      * @param {Boolean} anim (optional) false to cancel the default animation
17379      * @param {Function} callback (optional) A callback to be called when
17380      * expanding this node completes (does not wait for deep expand to complete).
17381      * Called with 1 parameter, this node.
17382      */
17383     expand : function(deep, anim, callback){
17384         if(!this.expanded){
17385             if(this.fireEvent("beforeexpand", this, deep, anim) === false){
17386                 return;
17387             }
17388             if(!this.childrenRendered){
17389                 this.renderChildren();
17390             }
17391             this.expanded = true;
17392             if(!this.isHiddenRoot() && (this.getOwnerTree().animate && anim !== false) || anim){
17393                 this.ui.animExpand(function(){
17394                     this.fireEvent("expand", this);
17395                     if(typeof callback == "function"){
17396                         callback(this);
17397                     }
17398                     if(deep === true){
17399                         this.expandChildNodes(true);
17400                     }
17401                 }.createDelegate(this));
17402                 return;
17403             }else{
17404                 this.ui.expand();
17405                 this.fireEvent("expand", this);
17406                 if(typeof callback == "function"){
17407                     callback(this);
17408                 }
17409             }
17410         }else{
17411            if(typeof callback == "function"){
17412                callback(this);
17413            }
17414         }
17415         if(deep === true){
17416             this.expandChildNodes(true);
17417         }
17418     },
17419
17420     isHiddenRoot : function(){
17421         return this.isRoot && !this.getOwnerTree().rootVisible;
17422     },
17423
17424     /**
17425      * Collapse this node.
17426      * @param {Boolean} deep (optional) True to collapse all children as well
17427      * @param {Boolean} anim (optional) false to cancel the default animation
17428      */
17429     collapse : function(deep, anim){
17430         if(this.expanded && !this.isHiddenRoot()){
17431             if(this.fireEvent("beforecollapse", this, deep, anim) === false){
17432                 return;
17433             }
17434             this.expanded = false;
17435             if((this.getOwnerTree().animate && anim !== false) || anim){
17436                 this.ui.animCollapse(function(){
17437                     this.fireEvent("collapse", this);
17438                     if(deep === true){
17439                         this.collapseChildNodes(true);
17440                     }
17441                 }.createDelegate(this));
17442                 return;
17443             }else{
17444                 this.ui.collapse();
17445                 this.fireEvent("collapse", this);
17446             }
17447         }
17448         if(deep === true){
17449             var cs = this.childNodes;
17450             for(var i = 0, len = cs.length; i < len; i++) {
17451                 cs[i].collapse(true, false);
17452             }
17453         }
17454     },
17455
17456     // private
17457     delayedExpand : function(delay){
17458         if(!this.expandProcId){
17459             this.expandProcId = this.expand.defer(delay, this);
17460         }
17461     },
17462
17463     // private
17464     cancelExpand : function(){
17465         if(this.expandProcId){
17466             clearTimeout(this.expandProcId);
17467         }
17468         this.expandProcId = false;
17469     },
17470
17471     /**
17472      * Toggles expanded/collapsed state of the node
17473      */
17474     toggle : function(){
17475         if(this.expanded){
17476             this.collapse();
17477         }else{
17478             this.expand();
17479         }
17480     },
17481
17482     /**
17483      * Ensures all parent nodes are expanded
17484      */
17485     ensureVisible : function(callback){
17486         var tree = this.getOwnerTree();
17487         tree.expandPath(this.parentNode.getPath(), false, function(){
17488             tree.getTreeEl().scrollChildIntoView(this.ui.anchor);
17489             Roo.callback(callback);
17490         }.createDelegate(this));
17491     },
17492
17493     /**
17494      * Expand all child nodes
17495      * @param {Boolean} deep (optional) true if the child nodes should also expand their child nodes
17496      */
17497     expandChildNodes : function(deep){
17498         var cs = this.childNodes;
17499         for(var i = 0, len = cs.length; i < len; i++) {
17500                 cs[i].expand(deep);
17501         }
17502     },
17503
17504     /**
17505      * Collapse all child nodes
17506      * @param {Boolean} deep (optional) true if the child nodes should also collapse their child nodes
17507      */
17508     collapseChildNodes : function(deep){
17509         var cs = this.childNodes;
17510         for(var i = 0, len = cs.length; i < len; i++) {
17511                 cs[i].collapse(deep);
17512         }
17513     },
17514
17515     /**
17516      * Disables this node
17517      */
17518     disable : function(){
17519         this.disabled = true;
17520         this.unselect();
17521         if(this.rendered && this.ui.onDisableChange){ // event without subscribing
17522             this.ui.onDisableChange(this, true);
17523         }
17524         this.fireEvent("disabledchange", this, true);
17525     },
17526
17527     /**
17528      * Enables this node
17529      */
17530     enable : function(){
17531         this.disabled = false;
17532         if(this.rendered && this.ui.onDisableChange){ // event without subscribing
17533             this.ui.onDisableChange(this, false);
17534         }
17535         this.fireEvent("disabledchange", this, false);
17536     },
17537
17538     // private
17539     renderChildren : function(suppressEvent){
17540         if(suppressEvent !== false){
17541             this.fireEvent("beforechildrenrendered", this);
17542         }
17543         var cs = this.childNodes;
17544         for(var i = 0, len = cs.length; i < len; i++){
17545             cs[i].render(true);
17546         }
17547         this.childrenRendered = true;
17548     },
17549
17550     // private
17551     sort : function(fn, scope){
17552         Roo.tree.TreeNode.superclass.sort.apply(this, arguments);
17553         if(this.childrenRendered){
17554             var cs = this.childNodes;
17555             for(var i = 0, len = cs.length; i < len; i++){
17556                 cs[i].render(true);
17557             }
17558         }
17559     },
17560
17561     // private
17562     render : function(bulkRender){
17563         this.ui.render(bulkRender);
17564         if(!this.rendered){
17565             this.rendered = true;
17566             if(this.expanded){
17567                 this.expanded = false;
17568                 this.expand(false, false);
17569             }
17570         }
17571     },
17572
17573     // private
17574     renderIndent : function(deep, refresh){
17575         if(refresh){
17576             this.ui.childIndent = null;
17577         }
17578         this.ui.renderIndent();
17579         if(deep === true && this.childrenRendered){
17580             var cs = this.childNodes;
17581             for(var i = 0, len = cs.length; i < len; i++){
17582                 cs[i].renderIndent(true, refresh);
17583             }
17584         }
17585     }
17586 });/*
17587  * Based on:
17588  * Ext JS Library 1.1.1
17589  * Copyright(c) 2006-2007, Ext JS, LLC.
17590  *
17591  * Originally Released Under LGPL - original licence link has changed is not relivant.
17592  *
17593  * Fork - LGPL
17594  * <script type="text/javascript">
17595  */
17596  
17597 /**
17598  * @class Roo.tree.AsyncTreeNode
17599  * @extends Roo.tree.TreeNode
17600  * @cfg {TreeLoader} loader A TreeLoader to be used by this node (defaults to the loader defined on the tree)
17601  * @constructor
17602  * @param {Object/String} attributes The attributes/config for the node or just a string with the text for the node 
17603  */
17604  Roo.tree.AsyncTreeNode = function(config){
17605     this.loaded = false;
17606     this.loading = false;
17607     Roo.tree.AsyncTreeNode.superclass.constructor.apply(this, arguments);
17608     /**
17609     * @event beforeload
17610     * Fires before this node is loaded, return false to cancel
17611     * @param {Node} this This node
17612     */
17613     this.addEvents({'beforeload':true, 'load': true});
17614     /**
17615     * @event load
17616     * Fires when this node is loaded
17617     * @param {Node} this This node
17618     */
17619     /**
17620      * The loader used by this node (defaults to using the tree's defined loader)
17621      * @type TreeLoader
17622      * @property loader
17623      */
17624 };
17625 Roo.extend(Roo.tree.AsyncTreeNode, Roo.tree.TreeNode, {
17626     expand : function(deep, anim, callback){
17627         if(this.loading){ // if an async load is already running, waiting til it's done
17628             var timer;
17629             var f = function(){
17630                 if(!this.loading){ // done loading
17631                     clearInterval(timer);
17632                     this.expand(deep, anim, callback);
17633                 }
17634             }.createDelegate(this);
17635             timer = setInterval(f, 200);
17636             return;
17637         }
17638         if(!this.loaded){
17639             if(this.fireEvent("beforeload", this) === false){
17640                 return;
17641             }
17642             this.loading = true;
17643             this.ui.beforeLoad(this);
17644             var loader = this.loader || this.attributes.loader || this.getOwnerTree().getLoader();
17645             if(loader){
17646                 loader.load(this, this.loadComplete.createDelegate(this, [deep, anim, callback]));
17647                 return;
17648             }
17649         }
17650         Roo.tree.AsyncTreeNode.superclass.expand.call(this, deep, anim, callback);
17651     },
17652     
17653     /**
17654      * Returns true if this node is currently loading
17655      * @return {Boolean}
17656      */
17657     isLoading : function(){
17658         return this.loading;  
17659     },
17660     
17661     loadComplete : function(deep, anim, callback){
17662         this.loading = false;
17663         this.loaded = true;
17664         this.ui.afterLoad(this);
17665         this.fireEvent("load", this);
17666         this.expand(deep, anim, callback);
17667     },
17668     
17669     /**
17670      * Returns true if this node has been loaded
17671      * @return {Boolean}
17672      */
17673     isLoaded : function(){
17674         return this.loaded;
17675     },
17676     
17677     hasChildNodes : function(){
17678         if(!this.isLeaf() && !this.loaded){
17679             return true;
17680         }else{
17681             return Roo.tree.AsyncTreeNode.superclass.hasChildNodes.call(this);
17682         }
17683     },
17684
17685     /**
17686      * Trigger a reload for this node
17687      * @param {Function} callback
17688      */
17689     reload : function(callback){
17690         this.collapse(false, false);
17691         while(this.firstChild){
17692             this.removeChild(this.firstChild);
17693         }
17694         this.childrenRendered = false;
17695         this.loaded = false;
17696         if(this.isHiddenRoot()){
17697             this.expanded = false;
17698         }
17699         this.expand(false, false, callback);
17700     }
17701 });/*
17702  * Based on:
17703  * Ext JS Library 1.1.1
17704  * Copyright(c) 2006-2007, Ext JS, LLC.
17705  *
17706  * Originally Released Under LGPL - original licence link has changed is not relivant.
17707  *
17708  * Fork - LGPL
17709  * <script type="text/javascript">
17710  */
17711  
17712 /**
17713  * @class Roo.tree.TreeNodeUI
17714  * @constructor
17715  * @param {Object} node The node to render
17716  * The TreeNode UI implementation is separate from the
17717  * tree implementation. Unless you are customizing the tree UI,
17718  * you should never have to use this directly.
17719  */
17720 Roo.tree.TreeNodeUI = function(node){
17721     this.node = node;
17722     this.rendered = false;
17723     this.animating = false;
17724     this.emptyIcon = Roo.BLANK_IMAGE_URL;
17725 };
17726
17727 Roo.tree.TreeNodeUI.prototype = {
17728     removeChild : function(node){
17729         if(this.rendered){
17730             this.ctNode.removeChild(node.ui.getEl());
17731         }
17732     },
17733
17734     beforeLoad : function(){
17735          this.addClass("x-tree-node-loading");
17736     },
17737
17738     afterLoad : function(){
17739          this.removeClass("x-tree-node-loading");
17740     },
17741
17742     onTextChange : function(node, text, oldText){
17743         if(this.rendered){
17744             this.textNode.innerHTML = text;
17745         }
17746     },
17747
17748     onDisableChange : function(node, state){
17749         this.disabled = state;
17750         if(state){
17751             this.addClass("x-tree-node-disabled");
17752         }else{
17753             this.removeClass("x-tree-node-disabled");
17754         }
17755     },
17756
17757     onSelectedChange : function(state){
17758         if(state){
17759             this.focus();
17760             this.addClass("x-tree-selected");
17761         }else{
17762             //this.blur();
17763             this.removeClass("x-tree-selected");
17764         }
17765     },
17766
17767     onMove : function(tree, node, oldParent, newParent, index, refNode){
17768         this.childIndent = null;
17769         if(this.rendered){
17770             var targetNode = newParent.ui.getContainer();
17771             if(!targetNode){//target not rendered
17772                 this.holder = document.createElement("div");
17773                 this.holder.appendChild(this.wrap);
17774                 return;
17775             }
17776             var insertBefore = refNode ? refNode.ui.getEl() : null;
17777             if(insertBefore){
17778                 targetNode.insertBefore(this.wrap, insertBefore);
17779             }else{
17780                 targetNode.appendChild(this.wrap);
17781             }
17782             this.node.renderIndent(true);
17783         }
17784     },
17785
17786     addClass : function(cls){
17787         if(this.elNode){
17788             Roo.fly(this.elNode).addClass(cls);
17789         }
17790     },
17791
17792     removeClass : function(cls){
17793         if(this.elNode){
17794             Roo.fly(this.elNode).removeClass(cls);
17795         }
17796     },
17797
17798     remove : function(){
17799         if(this.rendered){
17800             this.holder = document.createElement("div");
17801             this.holder.appendChild(this.wrap);
17802         }
17803     },
17804
17805     fireEvent : function(){
17806         return this.node.fireEvent.apply(this.node, arguments);
17807     },
17808
17809     initEvents : function(){
17810         this.node.on("move", this.onMove, this);
17811         var E = Roo.EventManager;
17812         var a = this.anchor;
17813
17814         var el = Roo.fly(a, '_treeui');
17815
17816         if(Roo.isOpera){ // opera render bug ignores the CSS
17817             el.setStyle("text-decoration", "none");
17818         }
17819
17820         el.on("click", this.onClick, this);
17821         el.on("dblclick", this.onDblClick, this);
17822
17823         if(this.checkbox){
17824             Roo.EventManager.on(this.checkbox,
17825                     Roo.isIE ? 'click' : 'change', this.onCheckChange, this);
17826         }
17827
17828         el.on("contextmenu", this.onContextMenu, this);
17829
17830         var icon = Roo.fly(this.iconNode);
17831         icon.on("click", this.onClick, this);
17832         icon.on("dblclick", this.onDblClick, this);
17833         icon.on("contextmenu", this.onContextMenu, this);
17834         E.on(this.ecNode, "click", this.ecClick, this, true);
17835
17836         if(this.node.disabled){
17837             this.addClass("x-tree-node-disabled");
17838         }
17839         if(this.node.hidden){
17840             this.addClass("x-tree-node-disabled");
17841         }
17842         var ot = this.node.getOwnerTree();
17843         var dd = ot.enableDD || ot.enableDrag || ot.enableDrop;
17844         if(dd && (!this.node.isRoot || ot.rootVisible)){
17845             Roo.dd.Registry.register(this.elNode, {
17846                 node: this.node,
17847                 handles: this.getDDHandles(),
17848                 isHandle: false
17849             });
17850         }
17851     },
17852
17853     getDDHandles : function(){
17854         return [this.iconNode, this.textNode];
17855     },
17856
17857     hide : function(){
17858         if(this.rendered){
17859             this.wrap.style.display = "none";
17860         }
17861     },
17862
17863     show : function(){
17864         if(this.rendered){
17865             this.wrap.style.display = "";
17866         }
17867     },
17868
17869     onContextMenu : function(e){
17870         if (this.node.hasListener("contextmenu") || this.node.getOwnerTree().hasListener("contextmenu")) {
17871             e.preventDefault();
17872             this.focus();
17873             this.fireEvent("contextmenu", this.node, e);
17874         }
17875     },
17876
17877     onClick : function(e){
17878         if(this.dropping){
17879             e.stopEvent();
17880             return;
17881         }
17882         if(this.fireEvent("beforeclick", this.node, e) !== false){
17883             if(!this.disabled && this.node.attributes.href){
17884                 this.fireEvent("click", this.node, e);
17885                 return;
17886             }
17887             e.preventDefault();
17888             if(this.disabled){
17889                 return;
17890             }
17891
17892             if(this.node.attributes.singleClickExpand && !this.animating && this.node.hasChildNodes()){
17893                 this.node.toggle();
17894             }
17895
17896             this.fireEvent("click", this.node, e);
17897         }else{
17898             e.stopEvent();
17899         }
17900     },
17901
17902     onDblClick : function(e){
17903         e.preventDefault();
17904         if(this.disabled){
17905             return;
17906         }
17907         if(this.checkbox){
17908             this.toggleCheck();
17909         }
17910         if(!this.animating && this.node.hasChildNodes()){
17911             this.node.toggle();
17912         }
17913         this.fireEvent("dblclick", this.node, e);
17914     },
17915
17916     onCheckChange : function(){
17917         var checked = this.checkbox.checked;
17918         this.node.attributes.checked = checked;
17919         this.fireEvent('checkchange', this.node, checked);
17920     },
17921
17922     ecClick : function(e){
17923         if(!this.animating && this.node.hasChildNodes()){
17924             this.node.toggle();
17925         }
17926     },
17927
17928     startDrop : function(){
17929         this.dropping = true;
17930     },
17931
17932     // delayed drop so the click event doesn't get fired on a drop
17933     endDrop : function(){
17934        setTimeout(function(){
17935            this.dropping = false;
17936        }.createDelegate(this), 50);
17937     },
17938
17939     expand : function(){
17940         this.updateExpandIcon();
17941         this.ctNode.style.display = "";
17942     },
17943
17944     focus : function(){
17945         if(!this.node.preventHScroll){
17946             try{this.anchor.focus();
17947             }catch(e){}
17948         }else if(!Roo.isIE){
17949             try{
17950                 var noscroll = this.node.getOwnerTree().getTreeEl().dom;
17951                 var l = noscroll.scrollLeft;
17952                 this.anchor.focus();
17953                 noscroll.scrollLeft = l;
17954             }catch(e){}
17955         }
17956     },
17957
17958     toggleCheck : function(value){
17959         var cb = this.checkbox;
17960         if(cb){
17961             cb.checked = (value === undefined ? !cb.checked : value);
17962         }
17963     },
17964
17965     blur : function(){
17966         try{
17967             this.anchor.blur();
17968         }catch(e){}
17969     },
17970
17971     animExpand : function(callback){
17972         var ct = Roo.get(this.ctNode);
17973         ct.stopFx();
17974         if(!this.node.hasChildNodes()){
17975             this.updateExpandIcon();
17976             this.ctNode.style.display = "";
17977             Roo.callback(callback);
17978             return;
17979         }
17980         this.animating = true;
17981         this.updateExpandIcon();
17982
17983         ct.slideIn('t', {
17984            callback : function(){
17985                this.animating = false;
17986                Roo.callback(callback);
17987             },
17988             scope: this,
17989             duration: this.node.ownerTree.duration || .25
17990         });
17991     },
17992
17993     highlight : function(){
17994         var tree = this.node.getOwnerTree();
17995         Roo.fly(this.wrap).highlight(
17996             tree.hlColor || "C3DAF9",
17997             {endColor: tree.hlBaseColor}
17998         );
17999     },
18000
18001     collapse : function(){
18002         this.updateExpandIcon();
18003         this.ctNode.style.display = "none";
18004     },
18005
18006     animCollapse : function(callback){
18007         var ct = Roo.get(this.ctNode);
18008         ct.enableDisplayMode('block');
18009         ct.stopFx();
18010
18011         this.animating = true;
18012         this.updateExpandIcon();
18013
18014         ct.slideOut('t', {
18015             callback : function(){
18016                this.animating = false;
18017                Roo.callback(callback);
18018             },
18019             scope: this,
18020             duration: this.node.ownerTree.duration || .25
18021         });
18022     },
18023
18024     getContainer : function(){
18025         return this.ctNode;
18026     },
18027
18028     getEl : function(){
18029         return this.wrap;
18030     },
18031
18032     appendDDGhost : function(ghostNode){
18033         ghostNode.appendChild(this.elNode.cloneNode(true));
18034     },
18035
18036     getDDRepairXY : function(){
18037         return Roo.lib.Dom.getXY(this.iconNode);
18038     },
18039
18040     onRender : function(){
18041         this.render();
18042     },
18043
18044     render : function(bulkRender){
18045         var n = this.node, a = n.attributes;
18046         var targetNode = n.parentNode ?
18047               n.parentNode.ui.getContainer() : n.ownerTree.innerCt.dom;
18048
18049         if(!this.rendered){
18050             this.rendered = true;
18051
18052             this.renderElements(n, a, targetNode, bulkRender);
18053
18054             if(a.qtip){
18055                if(this.textNode.setAttributeNS){
18056                    this.textNode.setAttributeNS("ext", "qtip", a.qtip);
18057                    if(a.qtipTitle){
18058                        this.textNode.setAttributeNS("ext", "qtitle", a.qtipTitle);
18059                    }
18060                }else{
18061                    this.textNode.setAttribute("ext:qtip", a.qtip);
18062                    if(a.qtipTitle){
18063                        this.textNode.setAttribute("ext:qtitle", a.qtipTitle);
18064                    }
18065                }
18066             }else if(a.qtipCfg){
18067                 a.qtipCfg.target = Roo.id(this.textNode);
18068                 Roo.QuickTips.register(a.qtipCfg);
18069             }
18070             this.initEvents();
18071             if(!this.node.expanded){
18072                 this.updateExpandIcon();
18073             }
18074         }else{
18075             if(bulkRender === true) {
18076                 targetNode.appendChild(this.wrap);
18077             }
18078         }
18079     },
18080
18081     renderElements : function(n, a, targetNode, bulkRender){
18082         // add some indent caching, this helps performance when rendering a large tree
18083         this.indentMarkup = n.parentNode ? n.parentNode.ui.getChildIndent() : '';
18084         var t = n.getOwnerTree();
18085         var txt = t.renderer ? t.renderer(n.attributes) : Roo.util.Format.htmlEncode(n.text);
18086         var tip = t.rendererTip ? t.rendererTip(n.attributes) : txt;
18087         var cb = typeof a.checked == 'boolean';
18088         var href = a.href ? a.href : Roo.isGecko ? "" : "#";
18089         var buf = ['<li class="x-tree-node"><div class="x-tree-node-el ', a.cls,'">',
18090             '<span class="x-tree-node-indent">',this.indentMarkup,"</span>",
18091             '<img src="', this.emptyIcon, '" class="x-tree-ec-icon" />',
18092             '<img src="', a.icon || this.emptyIcon, '" class="x-tree-node-icon',(a.icon ? " x-tree-node-inline-icon" : ""),(a.iconCls ? " "+a.iconCls : ""),'" unselectable="on" />',
18093             cb ? ('<input class="x-tree-node-cb" type="checkbox" ' + (a.checked ? 'checked="checked" />' : ' />')) : '',
18094             '<a hidefocus="on" href="',href,'" tabIndex="1" ',
18095              a.hrefTarget ? ' target="'+a.hrefTarget+'"' : "", 
18096                 '><span unselectable="on" qtip="' , tip ,'">',txt,"</span></a></div>",
18097             '<ul class="x-tree-node-ct" style="display:none;"></ul>',
18098             "</li>"];
18099
18100         if(bulkRender !== true && n.nextSibling && n.nextSibling.ui.getEl()){
18101             this.wrap = Roo.DomHelper.insertHtml("beforeBegin",
18102                                 n.nextSibling.ui.getEl(), buf.join(""));
18103         }else{
18104             this.wrap = Roo.DomHelper.insertHtml("beforeEnd", targetNode, buf.join(""));
18105         }
18106
18107         this.elNode = this.wrap.childNodes[0];
18108         this.ctNode = this.wrap.childNodes[1];
18109         var cs = this.elNode.childNodes;
18110         this.indentNode = cs[0];
18111         this.ecNode = cs[1];
18112         this.iconNode = cs[2];
18113         var index = 3;
18114         if(cb){
18115             this.checkbox = cs[3];
18116             index++;
18117         }
18118         this.anchor = cs[index];
18119         this.textNode = cs[index].firstChild;
18120     },
18121
18122     getAnchor : function(){
18123         return this.anchor;
18124     },
18125
18126     getTextEl : function(){
18127         return this.textNode;
18128     },
18129
18130     getIconEl : function(){
18131         return this.iconNode;
18132     },
18133
18134     isChecked : function(){
18135         return this.checkbox ? this.checkbox.checked : false;
18136     },
18137
18138     updateExpandIcon : function(){
18139         if(this.rendered){
18140             var n = this.node, c1, c2;
18141             var cls = n.isLast() ? "x-tree-elbow-end" : "x-tree-elbow";
18142             var hasChild = n.hasChildNodes();
18143             if(hasChild){
18144                 if(n.expanded){
18145                     cls += "-minus";
18146                     c1 = "x-tree-node-collapsed";
18147                     c2 = "x-tree-node-expanded";
18148                 }else{
18149                     cls += "-plus";
18150                     c1 = "x-tree-node-expanded";
18151                     c2 = "x-tree-node-collapsed";
18152                 }
18153                 if(this.wasLeaf){
18154                     this.removeClass("x-tree-node-leaf");
18155                     this.wasLeaf = false;
18156                 }
18157                 if(this.c1 != c1 || this.c2 != c2){
18158                     Roo.fly(this.elNode).replaceClass(c1, c2);
18159                     this.c1 = c1; this.c2 = c2;
18160                 }
18161             }else{
18162                 if(!this.wasLeaf){
18163                     Roo.fly(this.elNode).replaceClass("x-tree-node-expanded", "x-tree-node-leaf");
18164                     delete this.c1;
18165                     delete this.c2;
18166                     this.wasLeaf = true;
18167                 }
18168             }
18169             var ecc = "x-tree-ec-icon "+cls;
18170             if(this.ecc != ecc){
18171                 this.ecNode.className = ecc;
18172                 this.ecc = ecc;
18173             }
18174         }
18175     },
18176
18177     getChildIndent : function(){
18178         if(!this.childIndent){
18179             var buf = [];
18180             var p = this.node;
18181             while(p){
18182                 if(!p.isRoot || (p.isRoot && p.ownerTree.rootVisible)){
18183                     if(!p.isLast()) {
18184                         buf.unshift('<img src="'+this.emptyIcon+'" class="x-tree-elbow-line" />');
18185                     } else {
18186                         buf.unshift('<img src="'+this.emptyIcon+'" class="x-tree-icon" />');
18187                     }
18188                 }
18189                 p = p.parentNode;
18190             }
18191             this.childIndent = buf.join("");
18192         }
18193         return this.childIndent;
18194     },
18195
18196     renderIndent : function(){
18197         if(this.rendered){
18198             var indent = "";
18199             var p = this.node.parentNode;
18200             if(p){
18201                 indent = p.ui.getChildIndent();
18202             }
18203             if(this.indentMarkup != indent){ // don't rerender if not required
18204                 this.indentNode.innerHTML = indent;
18205                 this.indentMarkup = indent;
18206             }
18207             this.updateExpandIcon();
18208         }
18209     }
18210 };
18211
18212 Roo.tree.RootTreeNodeUI = function(){
18213     Roo.tree.RootTreeNodeUI.superclass.constructor.apply(this, arguments);
18214 };
18215 Roo.extend(Roo.tree.RootTreeNodeUI, Roo.tree.TreeNodeUI, {
18216     render : function(){
18217         if(!this.rendered){
18218             var targetNode = this.node.ownerTree.innerCt.dom;
18219             this.node.expanded = true;
18220             targetNode.innerHTML = '<div class="x-tree-root-node"></div>';
18221             this.wrap = this.ctNode = targetNode.firstChild;
18222         }
18223     },
18224     collapse : function(){
18225     },
18226     expand : function(){
18227     }
18228 });/*
18229  * Based on:
18230  * Ext JS Library 1.1.1
18231  * Copyright(c) 2006-2007, Ext JS, LLC.
18232  *
18233  * Originally Released Under LGPL - original licence link has changed is not relivant.
18234  *
18235  * Fork - LGPL
18236  * <script type="text/javascript">
18237  */
18238 /**
18239  * @class Roo.tree.TreeLoader
18240  * @extends Roo.util.Observable
18241  * A TreeLoader provides for lazy loading of an {@link Roo.tree.TreeNode}'s child
18242  * nodes from a specified URL. The response must be a javascript Array definition
18243  * who's elements are node definition objects. eg:
18244  * <pre><code>
18245    [{ 'id': 1, 'text': 'A folder Node', 'leaf': false },
18246     { 'id': 2, 'text': 'A leaf Node', 'leaf': true }]
18247 </code></pre>
18248  * <br><br>
18249  * A server request is sent, and child nodes are loaded only when a node is expanded.
18250  * The loading node's id is passed to the server under the parameter name "node" to
18251  * enable the server to produce the correct child nodes.
18252  * <br><br>
18253  * To pass extra parameters, an event handler may be attached to the "beforeload"
18254  * event, and the parameters specified in the TreeLoader's baseParams property:
18255  * <pre><code>
18256     myTreeLoader.on("beforeload", function(treeLoader, node) {
18257         this.baseParams.category = node.attributes.category;
18258     }, this);
18259 </code></pre><
18260  * This would pass an HTTP parameter called "category" to the server containing
18261  * the value of the Node's "category" attribute.
18262  * @constructor
18263  * Creates a new Treeloader.
18264  * @param {Object} config A config object containing config properties.
18265  */
18266 Roo.tree.TreeLoader = function(config){
18267     this.baseParams = {};
18268     this.requestMethod = "POST";
18269     Roo.apply(this, config);
18270
18271     this.addEvents({
18272     
18273         /**
18274          * @event beforeload
18275          * Fires before a network request is made to retrieve the Json text which specifies a node's children.
18276          * @param {Object} This TreeLoader object.
18277          * @param {Object} node The {@link Roo.tree.TreeNode} object being loaded.
18278          * @param {Object} callback The callback function specified in the {@link #load} call.
18279          */
18280         beforeload : true,
18281         /**
18282          * @event load
18283          * Fires when the node has been successfuly loaded.
18284          * @param {Object} This TreeLoader object.
18285          * @param {Object} node The {@link Roo.tree.TreeNode} object being loaded.
18286          * @param {Object} response The response object containing the data from the server.
18287          */
18288         load : true,
18289         /**
18290          * @event loadexception
18291          * Fires if the network request failed.
18292          * @param {Object} This TreeLoader object.
18293          * @param {Object} node The {@link Roo.tree.TreeNode} object being loaded.
18294          * @param {Object} response The response object containing the data from the server.
18295          */
18296         loadexception : true,
18297         /**
18298          * @event create
18299          * Fires before a node is created, enabling you to return custom Node types 
18300          * @param {Object} This TreeLoader object.
18301          * @param {Object} attr - the data returned from the AJAX call (modify it to suit)
18302          */
18303         create : true
18304     });
18305
18306     Roo.tree.TreeLoader.superclass.constructor.call(this);
18307 };
18308
18309 Roo.extend(Roo.tree.TreeLoader, Roo.util.Observable, {
18310     /**
18311     * @cfg {String} dataUrl The URL from which to request a Json string which
18312     * specifies an array of node definition object representing the child nodes
18313     * to be loaded.
18314     */
18315     /**
18316     * @cfg {Object} baseParams (optional) An object containing properties which
18317     * specify HTTP parameters to be passed to each request for child nodes.
18318     */
18319     /**
18320     * @cfg {Object} baseAttrs (optional) An object containing attributes to be added to all nodes
18321     * created by this loader. If the attributes sent by the server have an attribute in this object,
18322     * they take priority.
18323     */
18324     /**
18325     * @cfg {Object} uiProviders (optional) An object containing properties which
18326     * 
18327     * DEPRECIATED - use 'create' event handler to modify attributes - which affect creation.
18328     * specify custom {@link Roo.tree.TreeNodeUI} implementations. If the optional
18329     * <i>uiProvider</i> attribute of a returned child node is a string rather
18330     * than a reference to a TreeNodeUI implementation, this that string value
18331     * is used as a property name in the uiProviders object. You can define the provider named
18332     * 'default' , and this will be used for all nodes (if no uiProvider is delivered by the node data)
18333     */
18334     uiProviders : {},
18335
18336     /**
18337     * @cfg {Boolean} clearOnLoad (optional) Default to true. Remove previously existing
18338     * child nodes before loading.
18339     */
18340     clearOnLoad : true,
18341
18342     /**
18343     * @cfg {String} root (optional) Default to false. Use this to read data from an object 
18344     * property on loading, rather than expecting an array. (eg. more compatible to a standard
18345     * Grid query { data : [ .....] }
18346     */
18347     
18348     root : false,
18349      /**
18350     * @cfg {String} queryParam (optional) 
18351     * Name of the query as it will be passed on the querystring (defaults to 'node')
18352     * eg. the request will be ?node=[id]
18353     */
18354     
18355     
18356     queryParam: false,
18357     
18358     /**
18359      * Load an {@link Roo.tree.TreeNode} from the URL specified in the constructor.
18360      * This is called automatically when a node is expanded, but may be used to reload
18361      * a node (or append new children if the {@link #clearOnLoad} option is false.)
18362      * @param {Roo.tree.TreeNode} node
18363      * @param {Function} callback
18364      */
18365     load : function(node, callback){
18366         if(this.clearOnLoad){
18367             while(node.firstChild){
18368                 node.removeChild(node.firstChild);
18369             }
18370         }
18371         if(node.attributes.children){ // preloaded json children
18372             var cs = node.attributes.children;
18373             for(var i = 0, len = cs.length; i < len; i++){
18374                 node.appendChild(this.createNode(cs[i]));
18375             }
18376             if(typeof callback == "function"){
18377                 callback();
18378             }
18379         }else if(this.dataUrl){
18380             this.requestData(node, callback);
18381         }
18382     },
18383
18384     getParams: function(node){
18385         var buf = [], bp = this.baseParams;
18386         for(var key in bp){
18387             if(typeof bp[key] != "function"){
18388                 buf.push(encodeURIComponent(key), "=", encodeURIComponent(bp[key]), "&");
18389             }
18390         }
18391         var n = this.queryParam === false ? 'node' : this.queryParam;
18392         buf.push(n + "=", encodeURIComponent(node.id));
18393         return buf.join("");
18394     },
18395
18396     requestData : function(node, callback){
18397         if(this.fireEvent("beforeload", this, node, callback) !== false){
18398             this.transId = Roo.Ajax.request({
18399                 method:this.requestMethod,
18400                 url: this.dataUrl||this.url,
18401                 success: this.handleResponse,
18402                 failure: this.handleFailure,
18403                 scope: this,
18404                 argument: {callback: callback, node: node},
18405                 params: this.getParams(node)
18406             });
18407         }else{
18408             // if the load is cancelled, make sure we notify
18409             // the node that we are done
18410             if(typeof callback == "function"){
18411                 callback();
18412             }
18413         }
18414     },
18415
18416     isLoading : function(){
18417         return this.transId ? true : false;
18418     },
18419
18420     abort : function(){
18421         if(this.isLoading()){
18422             Roo.Ajax.abort(this.transId);
18423         }
18424     },
18425
18426     // private
18427     createNode : function(attr){
18428         // apply baseAttrs, nice idea Corey!
18429         if(this.baseAttrs){
18430             Roo.applyIf(attr, this.baseAttrs);
18431         }
18432         if(this.applyLoader !== false){
18433             attr.loader = this;
18434         }
18435         // uiProvider = depreciated..
18436         
18437         if(typeof(attr.uiProvider) == 'string'){
18438            attr.uiProvider = this.uiProviders[attr.uiProvider] || 
18439                 /**  eval:var:attr */ eval(attr.uiProvider);
18440         }
18441         if(typeof(this.uiProviders['default']) != 'undefined') {
18442             attr.uiProvider = this.uiProviders['default'];
18443         }
18444         
18445         this.fireEvent('create', this, attr);
18446         
18447         attr.leaf  = typeof(attr.leaf) == 'string' ? attr.leaf * 1 : attr.leaf;
18448         return(attr.leaf ?
18449                         new Roo.tree.TreeNode(attr) :
18450                         new Roo.tree.AsyncTreeNode(attr));
18451     },
18452
18453     processResponse : function(response, node, callback){
18454         var json = response.responseText;
18455         try {
18456             
18457             var o = /**  eval:var:zzzzzzzzzz */ eval("("+json+")");
18458             if (this.root !== false) {
18459                 o = o[this.root];
18460             }
18461             
18462             for(var i = 0, len = o.length; i < len; i++){
18463                 var n = this.createNode(o[i]);
18464                 if(n){
18465                     node.appendChild(n);
18466                 }
18467             }
18468             if(typeof callback == "function"){
18469                 callback(this, node);
18470             }
18471         }catch(e){
18472             this.handleFailure(response);
18473         }
18474     },
18475
18476     handleResponse : function(response){
18477         this.transId = false;
18478         var a = response.argument;
18479         this.processResponse(response, a.node, a.callback);
18480         this.fireEvent("load", this, a.node, response);
18481     },
18482
18483     handleFailure : function(response){
18484         this.transId = false;
18485         var a = response.argument;
18486         this.fireEvent("loadexception", this, a.node, response);
18487         if(typeof a.callback == "function"){
18488             a.callback(this, a.node);
18489         }
18490     }
18491 });/*
18492  * Based on:
18493  * Ext JS Library 1.1.1
18494  * Copyright(c) 2006-2007, Ext JS, LLC.
18495  *
18496  * Originally Released Under LGPL - original licence link has changed is not relivant.
18497  *
18498  * Fork - LGPL
18499  * <script type="text/javascript">
18500  */
18501
18502 /**
18503 * @class Roo.tree.TreeFilter
18504 * Note this class is experimental and doesn't update the indent (lines) or expand collapse icons of the nodes
18505 * @param {TreePanel} tree
18506 * @param {Object} config (optional)
18507  */
18508 Roo.tree.TreeFilter = function(tree, config){
18509     this.tree = tree;
18510     this.filtered = {};
18511     Roo.apply(this, config);
18512 };
18513
18514 Roo.tree.TreeFilter.prototype = {
18515     clearBlank:false,
18516     reverse:false,
18517     autoClear:false,
18518     remove:false,
18519
18520      /**
18521      * Filter the data by a specific attribute.
18522      * @param {String/RegExp} value Either string that the attribute value
18523      * should start with or a RegExp to test against the attribute
18524      * @param {String} attr (optional) The attribute passed in your node's attributes collection. Defaults to "text".
18525      * @param {TreeNode} startNode (optional) The node to start the filter at.
18526      */
18527     filter : function(value, attr, startNode){
18528         attr = attr || "text";
18529         var f;
18530         if(typeof value == "string"){
18531             var vlen = value.length;
18532             // auto clear empty filter
18533             if(vlen == 0 && this.clearBlank){
18534                 this.clear();
18535                 return;
18536             }
18537             value = value.toLowerCase();
18538             f = function(n){
18539                 return n.attributes[attr].substr(0, vlen).toLowerCase() == value;
18540             };
18541         }else if(value.exec){ // regex?
18542             f = function(n){
18543                 return value.test(n.attributes[attr]);
18544             };
18545         }else{
18546             throw 'Illegal filter type, must be string or regex';
18547         }
18548         this.filterBy(f, null, startNode);
18549         },
18550
18551     /**
18552      * Filter by a function. The passed function will be called with each
18553      * node in the tree (or from the startNode). If the function returns true, the node is kept
18554      * otherwise it is filtered. If a node is filtered, its children are also filtered.
18555      * @param {Function} fn The filter function
18556      * @param {Object} scope (optional) The scope of the function (defaults to the current node)
18557      */
18558     filterBy : function(fn, scope, startNode){
18559         startNode = startNode || this.tree.root;
18560         if(this.autoClear){
18561             this.clear();
18562         }
18563         var af = this.filtered, rv = this.reverse;
18564         var f = function(n){
18565             if(n == startNode){
18566                 return true;
18567             }
18568             if(af[n.id]){
18569                 return false;
18570             }
18571             var m = fn.call(scope || n, n);
18572             if(!m || rv){
18573                 af[n.id] = n;
18574                 n.ui.hide();
18575                 return false;
18576             }
18577             return true;
18578         };
18579         startNode.cascade(f);
18580         if(this.remove){
18581            for(var id in af){
18582                if(typeof id != "function"){
18583                    var n = af[id];
18584                    if(n && n.parentNode){
18585                        n.parentNode.removeChild(n);
18586                    }
18587                }
18588            }
18589         }
18590     },
18591
18592     /**
18593      * Clears the current filter. Note: with the "remove" option
18594      * set a filter cannot be cleared.
18595      */
18596     clear : function(){
18597         var t = this.tree;
18598         var af = this.filtered;
18599         for(var id in af){
18600             if(typeof id != "function"){
18601                 var n = af[id];
18602                 if(n){
18603                     n.ui.show();
18604                 }
18605             }
18606         }
18607         this.filtered = {};
18608     }
18609 };
18610 /*
18611  * Based on:
18612  * Ext JS Library 1.1.1
18613  * Copyright(c) 2006-2007, Ext JS, LLC.
18614  *
18615  * Originally Released Under LGPL - original licence link has changed is not relivant.
18616  *
18617  * Fork - LGPL
18618  * <script type="text/javascript">
18619  */
18620  
18621
18622 /**
18623  * @class Roo.tree.TreeSorter
18624  * Provides sorting of nodes in a TreePanel
18625  * 
18626  * @cfg {Boolean} folderSort True to sort leaf nodes under non leaf nodes
18627  * @cfg {String} property The named attribute on the node to sort by (defaults to text)
18628  * @cfg {String} dir The direction to sort (asc or desc) (defaults to asc)
18629  * @cfg {String} leafAttr The attribute used to determine leaf nodes in folder sort (defaults to "leaf")
18630  * @cfg {Boolean} caseSensitive true for case sensitive sort (defaults to false)
18631  * @cfg {Function} sortType A custom "casting" function used to convert node values before sorting
18632  * @constructor
18633  * @param {TreePanel} tree
18634  * @param {Object} config
18635  */
18636 Roo.tree.TreeSorter = function(tree, config){
18637     Roo.apply(this, config);
18638     tree.on("beforechildrenrendered", this.doSort, this);
18639     tree.on("append", this.updateSort, this);
18640     tree.on("insert", this.updateSort, this);
18641     
18642     var dsc = this.dir && this.dir.toLowerCase() == "desc";
18643     var p = this.property || "text";
18644     var sortType = this.sortType;
18645     var fs = this.folderSort;
18646     var cs = this.caseSensitive === true;
18647     var leafAttr = this.leafAttr || 'leaf';
18648
18649     this.sortFn = function(n1, n2){
18650         if(fs){
18651             if(n1.attributes[leafAttr] && !n2.attributes[leafAttr]){
18652                 return 1;
18653             }
18654             if(!n1.attributes[leafAttr] && n2.attributes[leafAttr]){
18655                 return -1;
18656             }
18657         }
18658         var v1 = sortType ? sortType(n1) : (cs ? n1.attributes[p] : n1.attributes[p].toUpperCase());
18659         var v2 = sortType ? sortType(n2) : (cs ? n2.attributes[p] : n2.attributes[p].toUpperCase());
18660         if(v1 < v2){
18661                         return dsc ? +1 : -1;
18662                 }else if(v1 > v2){
18663                         return dsc ? -1 : +1;
18664         }else{
18665                 return 0;
18666         }
18667     };
18668 };
18669
18670 Roo.tree.TreeSorter.prototype = {
18671     doSort : function(node){
18672         node.sort(this.sortFn);
18673     },
18674     
18675     compareNodes : function(n1, n2){
18676         return (n1.text.toUpperCase() > n2.text.toUpperCase() ? 1 : -1);
18677     },
18678     
18679     updateSort : function(tree, node){
18680         if(node.childrenRendered){
18681             this.doSort.defer(1, this, [node]);
18682         }
18683     }
18684 };/*
18685  * Based on:
18686  * Ext JS Library 1.1.1
18687  * Copyright(c) 2006-2007, Ext JS, LLC.
18688  *
18689  * Originally Released Under LGPL - original licence link has changed is not relivant.
18690  *
18691  * Fork - LGPL
18692  * <script type="text/javascript">
18693  */
18694
18695 if(Roo.dd.DropZone){
18696     
18697 Roo.tree.TreeDropZone = function(tree, config){
18698     this.allowParentInsert = false;
18699     this.allowContainerDrop = false;
18700     this.appendOnly = false;
18701     Roo.tree.TreeDropZone.superclass.constructor.call(this, tree.innerCt, config);
18702     this.tree = tree;
18703     this.lastInsertClass = "x-tree-no-status";
18704     this.dragOverData = {};
18705 };
18706
18707 Roo.extend(Roo.tree.TreeDropZone, Roo.dd.DropZone, {
18708     ddGroup : "TreeDD",
18709     
18710     expandDelay : 1000,
18711     
18712     expandNode : function(node){
18713         if(node.hasChildNodes() && !node.isExpanded()){
18714             node.expand(false, null, this.triggerCacheRefresh.createDelegate(this));
18715         }
18716     },
18717     
18718     queueExpand : function(node){
18719         this.expandProcId = this.expandNode.defer(this.expandDelay, this, [node]);
18720     },
18721     
18722     cancelExpand : function(){
18723         if(this.expandProcId){
18724             clearTimeout(this.expandProcId);
18725             this.expandProcId = false;
18726         }
18727     },
18728     
18729     isValidDropPoint : function(n, pt, dd, e, data){
18730         if(!n || !data){ return false; }
18731         var targetNode = n.node;
18732         var dropNode = data.node;
18733         // default drop rules
18734         if(!(targetNode && targetNode.isTarget && pt)){
18735             return false;
18736         }
18737         if(pt == "append" && targetNode.allowChildren === false){
18738             return false;
18739         }
18740         if((pt == "above" || pt == "below") && (targetNode.parentNode && targetNode.parentNode.allowChildren === false)){
18741             return false;
18742         }
18743         if(dropNode && (targetNode == dropNode || dropNode.contains(targetNode))){
18744             return false;
18745         }
18746         // reuse the object
18747         var overEvent = this.dragOverData;
18748         overEvent.tree = this.tree;
18749         overEvent.target = targetNode;
18750         overEvent.data = data;
18751         overEvent.point = pt;
18752         overEvent.source = dd;
18753         overEvent.rawEvent = e;
18754         overEvent.dropNode = dropNode;
18755         overEvent.cancel = false;  
18756         var result = this.tree.fireEvent("nodedragover", overEvent);
18757         return overEvent.cancel === false && result !== false;
18758     },
18759     
18760     getDropPoint : function(e, n, dd){
18761         var tn = n.node;
18762         if(tn.isRoot){
18763             return tn.allowChildren !== false ? "append" : false; // always append for root
18764         }
18765         var dragEl = n.ddel;
18766         var t = Roo.lib.Dom.getY(dragEl), b = t + dragEl.offsetHeight;
18767         var y = Roo.lib.Event.getPageY(e);
18768         //var noAppend = tn.allowChildren === false || tn.isLeaf();
18769         
18770         // we may drop nodes anywhere, as long as allowChildren has not been set to false..
18771         var noAppend = tn.allowChildren === false;
18772         if(this.appendOnly || tn.parentNode.allowChildren === false){
18773             return noAppend ? false : "append";
18774         }
18775         var noBelow = false;
18776         if(!this.allowParentInsert){
18777             noBelow = tn.hasChildNodes() && tn.isExpanded();
18778         }
18779         var q = (b - t) / (noAppend ? 2 : 3);
18780         if(y >= t && y < (t + q)){
18781             return "above";
18782         }else if(!noBelow && (noAppend || y >= b-q && y <= b)){
18783             return "below";
18784         }else{
18785             return "append";
18786         }
18787     },
18788     
18789     onNodeEnter : function(n, dd, e, data){
18790         this.cancelExpand();
18791     },
18792     
18793     onNodeOver : function(n, dd, e, data){
18794         var pt = this.getDropPoint(e, n, dd);
18795         var node = n.node;
18796         
18797         // auto node expand check
18798         if(!this.expandProcId && pt == "append" && node.hasChildNodes() && !n.node.isExpanded()){
18799             this.queueExpand(node);
18800         }else if(pt != "append"){
18801             this.cancelExpand();
18802         }
18803         
18804         // set the insert point style on the target node
18805         var returnCls = this.dropNotAllowed;
18806         if(this.isValidDropPoint(n, pt, dd, e, data)){
18807            if(pt){
18808                var el = n.ddel;
18809                var cls;
18810                if(pt == "above"){
18811                    returnCls = n.node.isFirst() ? "x-tree-drop-ok-above" : "x-tree-drop-ok-between";
18812                    cls = "x-tree-drag-insert-above";
18813                }else if(pt == "below"){
18814                    returnCls = n.node.isLast() ? "x-tree-drop-ok-below" : "x-tree-drop-ok-between";
18815                    cls = "x-tree-drag-insert-below";
18816                }else{
18817                    returnCls = "x-tree-drop-ok-append";
18818                    cls = "x-tree-drag-append";
18819                }
18820                if(this.lastInsertClass != cls){
18821                    Roo.fly(el).replaceClass(this.lastInsertClass, cls);
18822                    this.lastInsertClass = cls;
18823                }
18824            }
18825        }
18826        return returnCls;
18827     },
18828     
18829     onNodeOut : function(n, dd, e, data){
18830         this.cancelExpand();
18831         this.removeDropIndicators(n);
18832     },
18833     
18834     onNodeDrop : function(n, dd, e, data){
18835         var point = this.getDropPoint(e, n, dd);
18836         var targetNode = n.node;
18837         targetNode.ui.startDrop();
18838         if(!this.isValidDropPoint(n, point, dd, e, data)){
18839             targetNode.ui.endDrop();
18840             return false;
18841         }
18842         // first try to find the drop node
18843         var dropNode = data.node || (dd.getTreeNode ? dd.getTreeNode(data, targetNode, point, e) : null);
18844         var dropEvent = {
18845             tree : this.tree,
18846             target: targetNode,
18847             data: data,
18848             point: point,
18849             source: dd,
18850             rawEvent: e,
18851             dropNode: dropNode,
18852             cancel: !dropNode   
18853         };
18854         var retval = this.tree.fireEvent("beforenodedrop", dropEvent);
18855         if(retval === false || dropEvent.cancel === true || !dropEvent.dropNode){
18856             targetNode.ui.endDrop();
18857             return false;
18858         }
18859         // allow target changing
18860         targetNode = dropEvent.target;
18861         if(point == "append" && !targetNode.isExpanded()){
18862             targetNode.expand(false, null, function(){
18863                 this.completeDrop(dropEvent);
18864             }.createDelegate(this));
18865         }else{
18866             this.completeDrop(dropEvent);
18867         }
18868         return true;
18869     },
18870     
18871     completeDrop : function(de){
18872         var ns = de.dropNode, p = de.point, t = de.target;
18873         if(!(ns instanceof Array)){
18874             ns = [ns];
18875         }
18876         var n;
18877         for(var i = 0, len = ns.length; i < len; i++){
18878             n = ns[i];
18879             if(p == "above"){
18880                 t.parentNode.insertBefore(n, t);
18881             }else if(p == "below"){
18882                 t.parentNode.insertBefore(n, t.nextSibling);
18883             }else{
18884                 t.appendChild(n);
18885             }
18886         }
18887         n.ui.focus();
18888         if(this.tree.hlDrop){
18889             n.ui.highlight();
18890         }
18891         t.ui.endDrop();
18892         this.tree.fireEvent("nodedrop", de);
18893     },
18894     
18895     afterNodeMoved : function(dd, data, e, targetNode, dropNode){
18896         if(this.tree.hlDrop){
18897             dropNode.ui.focus();
18898             dropNode.ui.highlight();
18899         }
18900         this.tree.fireEvent("nodedrop", this.tree, targetNode, data, dd, e);
18901     },
18902     
18903     getTree : function(){
18904         return this.tree;
18905     },
18906     
18907     removeDropIndicators : function(n){
18908         if(n && n.ddel){
18909             var el = n.ddel;
18910             Roo.fly(el).removeClass([
18911                     "x-tree-drag-insert-above",
18912                     "x-tree-drag-insert-below",
18913                     "x-tree-drag-append"]);
18914             this.lastInsertClass = "_noclass";
18915         }
18916     },
18917     
18918     beforeDragDrop : function(target, e, id){
18919         this.cancelExpand();
18920         return true;
18921     },
18922     
18923     afterRepair : function(data){
18924         if(data && Roo.enableFx){
18925             data.node.ui.highlight();
18926         }
18927         this.hideProxy();
18928     }    
18929 });
18930
18931 }
18932 /*
18933  * Based on:
18934  * Ext JS Library 1.1.1
18935  * Copyright(c) 2006-2007, Ext JS, LLC.
18936  *
18937  * Originally Released Under LGPL - original licence link has changed is not relivant.
18938  *
18939  * Fork - LGPL
18940  * <script type="text/javascript">
18941  */
18942  
18943
18944 if(Roo.dd.DragZone){
18945 Roo.tree.TreeDragZone = function(tree, config){
18946     Roo.tree.TreeDragZone.superclass.constructor.call(this, tree.getTreeEl(), config);
18947     this.tree = tree;
18948 };
18949
18950 Roo.extend(Roo.tree.TreeDragZone, Roo.dd.DragZone, {
18951     ddGroup : "TreeDD",
18952     
18953     onBeforeDrag : function(data, e){
18954         var n = data.node;
18955         return n && n.draggable && !n.disabled;
18956     },
18957     
18958     onInitDrag : function(e){
18959         var data = this.dragData;
18960         this.tree.getSelectionModel().select(data.node);
18961         this.proxy.update("");
18962         data.node.ui.appendDDGhost(this.proxy.ghost.dom);
18963         this.tree.fireEvent("startdrag", this.tree, data.node, e);
18964     },
18965     
18966     getRepairXY : function(e, data){
18967         return data.node.ui.getDDRepairXY();
18968     },
18969     
18970     onEndDrag : function(data, e){
18971         this.tree.fireEvent("enddrag", this.tree, data.node, e);
18972     },
18973     
18974     onValidDrop : function(dd, e, id){
18975         this.tree.fireEvent("dragdrop", this.tree, this.dragData.node, dd, e);
18976         this.hideProxy();
18977     },
18978     
18979     beforeInvalidDrop : function(e, id){
18980         // this scrolls the original position back into view
18981         var sm = this.tree.getSelectionModel();
18982         sm.clearSelections();
18983         sm.select(this.dragData.node);
18984     }
18985 });
18986 }/*
18987  * Based on:
18988  * Ext JS Library 1.1.1
18989  * Copyright(c) 2006-2007, Ext JS, LLC.
18990  *
18991  * Originally Released Under LGPL - original licence link has changed is not relivant.
18992  *
18993  * Fork - LGPL
18994  * <script type="text/javascript">
18995  */
18996 /**
18997  * @class Roo.tree.TreeEditor
18998  * @extends Roo.Editor
18999  * Provides editor functionality for inline tree node editing.  Any valid {@link Roo.form.Field} can be used
19000  * as the editor field.
19001  * @constructor
19002  * @param {TreePanel} tree
19003  * @param {Object} config Either a prebuilt {@link Roo.form.Field} instance or a Field config object
19004  */
19005 Roo.tree.TreeEditor = function(tree, config){
19006     config = config || {};
19007     var field = config.events ? config : new Roo.form.TextField(config);
19008     Roo.tree.TreeEditor.superclass.constructor.call(this, field);
19009
19010     this.tree = tree;
19011
19012     tree.on('beforeclick', this.beforeNodeClick, this);
19013     tree.getTreeEl().on('mousedown', this.hide, this);
19014     this.on('complete', this.updateNode, this);
19015     this.on('beforestartedit', this.fitToTree, this);
19016     this.on('startedit', this.bindScroll, this, {delay:10});
19017     this.on('specialkey', this.onSpecialKey, this);
19018 };
19019
19020 Roo.extend(Roo.tree.TreeEditor, Roo.Editor, {
19021     /**
19022      * @cfg {String} alignment
19023      * The position to align to (see {@link Roo.Element#alignTo} for more details, defaults to "l-l").
19024      */
19025     alignment: "l-l",
19026     // inherit
19027     autoSize: false,
19028     /**
19029      * @cfg {Boolean} hideEl
19030      * True to hide the bound element while the editor is displayed (defaults to false)
19031      */
19032     hideEl : false,
19033     /**
19034      * @cfg {String} cls
19035      * CSS class to apply to the editor (defaults to "x-small-editor x-tree-editor")
19036      */
19037     cls: "x-small-editor x-tree-editor",
19038     /**
19039      * @cfg {Boolean} shim
19040      * True to shim the editor if selects/iframes could be displayed beneath it (defaults to false)
19041      */
19042     shim:false,
19043     // inherit
19044     shadow:"frame",
19045     /**
19046      * @cfg {Number} maxWidth
19047      * The maximum width in pixels of the editor field (defaults to 250).  Note that if the maxWidth would exceed
19048      * the containing tree element's size, it will be automatically limited for you to the container width, taking
19049      * scroll and client offsets into account prior to each edit.
19050      */
19051     maxWidth: 250,
19052
19053     editDelay : 350,
19054
19055     // private
19056     fitToTree : function(ed, el){
19057         var td = this.tree.getTreeEl().dom, nd = el.dom;
19058         if(td.scrollLeft >  nd.offsetLeft){ // ensure the node left point is visible
19059             td.scrollLeft = nd.offsetLeft;
19060         }
19061         var w = Math.min(
19062                 this.maxWidth,
19063                 (td.clientWidth > 20 ? td.clientWidth : td.offsetWidth) - Math.max(0, nd.offsetLeft-td.scrollLeft) - /*cushion*/5);
19064         this.setSize(w, '');
19065     },
19066
19067     // private
19068     triggerEdit : function(node){
19069         this.completeEdit();
19070         this.editNode = node;
19071         this.startEdit(node.ui.textNode, node.text);
19072     },
19073
19074     // private
19075     bindScroll : function(){
19076         this.tree.getTreeEl().on('scroll', this.cancelEdit, this);
19077     },
19078
19079     // private
19080     beforeNodeClick : function(node, e){
19081         var sinceLast = (this.lastClick ? this.lastClick.getElapsed() : 0);
19082         this.lastClick = new Date();
19083         if(sinceLast > this.editDelay && this.tree.getSelectionModel().isSelected(node)){
19084             e.stopEvent();
19085             this.triggerEdit(node);
19086             return false;
19087         }
19088     },
19089
19090     // private
19091     updateNode : function(ed, value){
19092         this.tree.getTreeEl().un('scroll', this.cancelEdit, this);
19093         this.editNode.setText(value);
19094     },
19095
19096     // private
19097     onHide : function(){
19098         Roo.tree.TreeEditor.superclass.onHide.call(this);
19099         if(this.editNode){
19100             this.editNode.ui.focus();
19101         }
19102     },
19103
19104     // private
19105     onSpecialKey : function(field, e){
19106         var k = e.getKey();
19107         if(k == e.ESC){
19108             e.stopEvent();
19109             this.cancelEdit();
19110         }else if(k == e.ENTER && !e.hasModifier()){
19111             e.stopEvent();
19112             this.completeEdit();
19113         }
19114     }
19115 });//<Script type="text/javascript">
19116 /*
19117  * Based on:
19118  * Ext JS Library 1.1.1
19119  * Copyright(c) 2006-2007, Ext JS, LLC.
19120  *
19121  * Originally Released Under LGPL - original licence link has changed is not relivant.
19122  *
19123  * Fork - LGPL
19124  * <script type="text/javascript">
19125  */
19126  
19127 /**
19128  * Not documented??? - probably should be...
19129  */
19130
19131 Roo.tree.ColumnNodeUI = Roo.extend(Roo.tree.TreeNodeUI, {
19132     //focus: Roo.emptyFn, // prevent odd scrolling behavior
19133     
19134     renderElements : function(n, a, targetNode, bulkRender){
19135         //consel.log("renderElements?");
19136         this.indentMarkup = n.parentNode ? n.parentNode.ui.getChildIndent() : '';
19137
19138         var t = n.getOwnerTree();
19139         var tid = Pman.Tab.Document_TypesTree.tree.el.id;
19140         
19141         var cols = t.columns;
19142         var bw = t.borderWidth;
19143         var c = cols[0];
19144         var href = a.href ? a.href : Roo.isGecko ? "" : "#";
19145          var cb = typeof a.checked == "boolean";
19146         var tx = String.format('{0}',n.text || (c.renderer ? c.renderer(a[c.dataIndex], n, a) : a[c.dataIndex]));
19147         var colcls = 'x-t-' + tid + '-c0';
19148         var buf = [
19149             '<li class="x-tree-node">',
19150             
19151                 
19152                 '<div class="x-tree-node-el ', a.cls,'">',
19153                     // extran...
19154                     '<div class="x-tree-col ', colcls, '" style="width:', c.width-bw, 'px;">',
19155                 
19156                 
19157                         '<span class="x-tree-node-indent">',this.indentMarkup,'</span>',
19158                         '<img src="', this.emptyIcon, '" class="x-tree-ec-icon  " />',
19159                         '<img src="', a.icon || this.emptyIcon, '" class="x-tree-node-icon',
19160                            (a.icon ? ' x-tree-node-inline-icon' : ''),
19161                            (a.iconCls ? ' '+a.iconCls : ''),
19162                            '" unselectable="on" />',
19163                         (cb ? ('<input class="x-tree-node-cb" type="checkbox" ' + 
19164                              (a.checked ? 'checked="checked" />' : ' />')) : ''),
19165                              
19166                         '<a class="x-tree-node-anchor" hidefocus="on" href="',href,'" tabIndex="1" ',
19167                             (a.hrefTarget ? ' target="' +a.hrefTarget + '"' : ''), '>',
19168                             '<span unselectable="on" qtip="' + tx + '">',
19169                              tx,
19170                              '</span></a>' ,
19171                     '</div>',
19172                      '<a class="x-tree-node-anchor" hidefocus="on" href="',href,'" tabIndex="1" ',
19173                             (a.hrefTarget ? ' target="' +a.hrefTarget + '"' : ''), '>'
19174                  ];
19175         for(var i = 1, len = cols.length; i < len; i++){
19176             c = cols[i];
19177             colcls = 'x-t-' + tid + '-c' +i;
19178             tx = String.format('{0}', (c.renderer ? c.renderer(a[c.dataIndex], n, a) : a[c.dataIndex]));
19179             buf.push('<div class="x-tree-col ', colcls, ' ' ,(c.cls?c.cls:''),'" style="width:',c.width-bw,'px;">',
19180                         '<div class="x-tree-col-text" qtip="' + tx +'">',tx,"</div>",
19181                       "</div>");
19182          }
19183          
19184          buf.push(
19185             '</a>',
19186             '<div class="x-clear"></div></div>',
19187             '<ul class="x-tree-node-ct" style="display:none;"></ul>',
19188             "</li>");
19189         
19190         if(bulkRender !== true && n.nextSibling && n.nextSibling.ui.getEl()){
19191             this.wrap = Roo.DomHelper.insertHtml("beforeBegin",
19192                                 n.nextSibling.ui.getEl(), buf.join(""));
19193         }else{
19194             this.wrap = Roo.DomHelper.insertHtml("beforeEnd", targetNode, buf.join(""));
19195         }
19196         var el = this.wrap.firstChild;
19197         this.elRow = el;
19198         this.elNode = el.firstChild;
19199         this.ranchor = el.childNodes[1];
19200         this.ctNode = this.wrap.childNodes[1];
19201         var cs = el.firstChild.childNodes;
19202         this.indentNode = cs[0];
19203         this.ecNode = cs[1];
19204         this.iconNode = cs[2];
19205         var index = 3;
19206         if(cb){
19207             this.checkbox = cs[3];
19208             index++;
19209         }
19210         this.anchor = cs[index];
19211         
19212         this.textNode = cs[index].firstChild;
19213         
19214         //el.on("click", this.onClick, this);
19215         //el.on("dblclick", this.onDblClick, this);
19216         
19217         
19218        // console.log(this);
19219     },
19220     initEvents : function(){
19221         Roo.tree.ColumnNodeUI.superclass.initEvents.call(this);
19222         
19223             
19224         var a = this.ranchor;
19225
19226         var el = Roo.get(a);
19227
19228         if(Roo.isOpera){ // opera render bug ignores the CSS
19229             el.setStyle("text-decoration", "none");
19230         }
19231
19232         el.on("click", this.onClick, this);
19233         el.on("dblclick", this.onDblClick, this);
19234         el.on("contextmenu", this.onContextMenu, this);
19235         
19236     },
19237     
19238     /*onSelectedChange : function(state){
19239         if(state){
19240             this.focus();
19241             this.addClass("x-tree-selected");
19242         }else{
19243             //this.blur();
19244             this.removeClass("x-tree-selected");
19245         }
19246     },*/
19247     addClass : function(cls){
19248         if(this.elRow){
19249             Roo.fly(this.elRow).addClass(cls);
19250         }
19251         
19252     },
19253     
19254     
19255     removeClass : function(cls){
19256         if(this.elRow){
19257             Roo.fly(this.elRow).removeClass(cls);
19258         }
19259     }
19260
19261     
19262     
19263 });//<Script type="text/javascript">
19264
19265 /*
19266  * Based on:
19267  * Ext JS Library 1.1.1
19268  * Copyright(c) 2006-2007, Ext JS, LLC.
19269  *
19270  * Originally Released Under LGPL - original licence link has changed is not relivant.
19271  *
19272  * Fork - LGPL
19273  * <script type="text/javascript">
19274  */
19275  
19276
19277 /**
19278  * @class Roo.tree.ColumnTree
19279  * @extends Roo.data.TreePanel
19280  * @cfg {Object} columns  Including width, header, renderer, cls, dataIndex 
19281  * @cfg {int} borderWidth  compined right/left border allowance
19282  * @constructor
19283  * @param {String/HTMLElement/Element} el The container element
19284  * @param {Object} config
19285  */
19286 Roo.tree.ColumnTree =  function(el, config)
19287 {
19288    Roo.tree.ColumnTree.superclass.constructor.call(this, el , config);
19289    this.addEvents({
19290         /**
19291         * @event resize
19292         * Fire this event on a container when it resizes
19293         * @param {int} w Width
19294         * @param {int} h Height
19295         */
19296        "resize" : true
19297     });
19298     this.on('resize', this.onResize, this);
19299 };
19300
19301 Roo.extend(Roo.tree.ColumnTree, Roo.tree.TreePanel, {
19302     //lines:false,
19303     
19304     
19305     borderWidth: Roo.isBorderBox ? 0 : 2, 
19306     headEls : false,
19307     
19308     render : function(){
19309         // add the header.....
19310        
19311         Roo.tree.ColumnTree.superclass.render.apply(this);
19312         
19313         this.el.addClass('x-column-tree');
19314         
19315         this.headers = this.el.createChild(
19316             {cls:'x-tree-headers'},this.innerCt.dom);
19317    
19318         var cols = this.columns, c;
19319         var totalWidth = 0;
19320         this.headEls = [];
19321         var  len = cols.length;
19322         for(var i = 0; i < len; i++){
19323              c = cols[i];
19324              totalWidth += c.width;
19325             this.headEls.push(this.headers.createChild({
19326                  cls:'x-tree-hd ' + (c.cls?c.cls+'-hd':''),
19327                  cn: {
19328                      cls:'x-tree-hd-text',
19329                      html: c.header
19330                  },
19331                  style:'width:'+(c.width-this.borderWidth)+'px;'
19332              }));
19333         }
19334         this.headers.createChild({cls:'x-clear'});
19335         // prevent floats from wrapping when clipped
19336         this.headers.setWidth(totalWidth);
19337         //this.innerCt.setWidth(totalWidth);
19338         this.innerCt.setStyle({ overflow: 'auto' });
19339         this.onResize(this.width, this.height);
19340              
19341         
19342     },
19343     onResize : function(w,h)
19344     {
19345         this.height = h;
19346         this.width = w;
19347         // resize cols..
19348         this.innerCt.setWidth(this.width);
19349         this.innerCt.setHeight(this.height-20);
19350         
19351         // headers...
19352         var cols = this.columns, c;
19353         var totalWidth = 0;
19354         var expEl = false;
19355         var len = cols.length;
19356         for(var i = 0; i < len; i++){
19357             c = cols[i];
19358             if (this.autoExpandColumn !== false && c.dataIndex == this.autoExpandColumn) {
19359                 // it's the expander..
19360                 expEl  = this.headEls[i];
19361                 continue;
19362             }
19363             totalWidth += c.width;
19364             
19365         }
19366         if (expEl) {
19367             expEl.setWidth(  ((w - totalWidth)-this.borderWidth - 20));
19368         }
19369         this.headers.setWidth(w-20);
19370
19371         
19372         
19373         
19374     }
19375 });
19376 /*
19377  * Based on:
19378  * Ext JS Library 1.1.1
19379  * Copyright(c) 2006-2007, Ext JS, LLC.
19380  *
19381  * Originally Released Under LGPL - original licence link has changed is not relivant.
19382  *
19383  * Fork - LGPL
19384  * <script type="text/javascript">
19385  */
19386  
19387 /**
19388  * @class Roo.menu.Menu
19389  * @extends Roo.util.Observable
19390  * A menu object.  This is the container to which you add all other menu items.  Menu can also serve a as a base class
19391  * when you want a specialzed menu based off of another component (like {@link Roo.menu.DateMenu} for example).
19392  * @constructor
19393  * Creates a new Menu
19394  * @param {Object} config Configuration options
19395  */
19396 Roo.menu.Menu = function(config){
19397     Roo.apply(this, config);
19398     this.id = this.id || Roo.id();
19399     this.addEvents({
19400         /**
19401          * @event beforeshow
19402          * Fires before this menu is displayed
19403          * @param {Roo.menu.Menu} this
19404          */
19405         beforeshow : true,
19406         /**
19407          * @event beforehide
19408          * Fires before this menu is hidden
19409          * @param {Roo.menu.Menu} this
19410          */
19411         beforehide : true,
19412         /**
19413          * @event show
19414          * Fires after this menu is displayed
19415          * @param {Roo.menu.Menu} this
19416          */
19417         show : true,
19418         /**
19419          * @event hide
19420          * Fires after this menu is hidden
19421          * @param {Roo.menu.Menu} this
19422          */
19423         hide : true,
19424         /**
19425          * @event click
19426          * Fires when this menu is clicked (or when the enter key is pressed while it is active)
19427          * @param {Roo.menu.Menu} this
19428          * @param {Roo.menu.Item} menuItem The menu item that was clicked
19429          * @param {Roo.EventObject} e
19430          */
19431         click : true,
19432         /**
19433          * @event mouseover
19434          * Fires when the mouse is hovering over this menu
19435          * @param {Roo.menu.Menu} this
19436          * @param {Roo.EventObject} e
19437          * @param {Roo.menu.Item} menuItem The menu item that was clicked
19438          */
19439         mouseover : true,
19440         /**
19441          * @event mouseout
19442          * Fires when the mouse exits this menu
19443          * @param {Roo.menu.Menu} this
19444          * @param {Roo.EventObject} e
19445          * @param {Roo.menu.Item} menuItem The menu item that was clicked
19446          */
19447         mouseout : true,
19448         /**
19449          * @event itemclick
19450          * Fires when a menu item contained in this menu is clicked
19451          * @param {Roo.menu.BaseItem} baseItem The BaseItem that was clicked
19452          * @param {Roo.EventObject} e
19453          */
19454         itemclick: true
19455     });
19456     if (this.registerMenu) {
19457         Roo.menu.MenuMgr.register(this);
19458     }
19459     
19460     var mis = this.items;
19461     this.items = new Roo.util.MixedCollection();
19462     if(mis){
19463         this.add.apply(this, mis);
19464     }
19465 };
19466
19467 Roo.extend(Roo.menu.Menu, Roo.util.Observable, {
19468     /**
19469      * @cfg {Number} minWidth The minimum width of the menu in pixels (defaults to 120)
19470      */
19471     minWidth : 120,
19472     /**
19473      * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop"
19474      * for bottom-right shadow (defaults to "sides")
19475      */
19476     shadow : "sides",
19477     /**
19478      * @cfg {String} subMenuAlign The {@link Roo.Element#alignTo} anchor position value to use for submenus of
19479      * this menu (defaults to "tl-tr?")
19480      */
19481     subMenuAlign : "tl-tr?",
19482     /**
19483      * @cfg {String} defaultAlign The default {@link Roo.Element#alignTo) anchor position value for this menu
19484      * relative to its element of origin (defaults to "tl-bl?")
19485      */
19486     defaultAlign : "tl-bl?",
19487     /**
19488      * @cfg {Boolean} allowOtherMenus True to allow multiple menus to be displayed at the same time (defaults to false)
19489      */
19490     allowOtherMenus : false,
19491     /**
19492      * @cfg {Boolean} registerMenu True (default) - means that clicking on screen etc. hides it.
19493      */
19494     registerMenu : true,
19495
19496     hidden:true,
19497
19498     // private
19499     render : function(){
19500         if(this.el){
19501             return;
19502         }
19503         var el = this.el = new Roo.Layer({
19504             cls: "x-menu",
19505             shadow:this.shadow,
19506             constrain: false,
19507             parentEl: this.parentEl || document.body,
19508             zindex:15000
19509         });
19510
19511         this.keyNav = new Roo.menu.MenuNav(this);
19512
19513         if(this.plain){
19514             el.addClass("x-menu-plain");
19515         }
19516         if(this.cls){
19517             el.addClass(this.cls);
19518         }
19519         // generic focus element
19520         this.focusEl = el.createChild({
19521             tag: "a", cls: "x-menu-focus", href: "#", onclick: "return false;", tabIndex:"-1"
19522         });
19523         var ul = el.createChild({tag: "ul", cls: "x-menu-list"});
19524         ul.on("click", this.onClick, this);
19525         ul.on("mouseover", this.onMouseOver, this);
19526         ul.on("mouseout", this.onMouseOut, this);
19527         this.items.each(function(item){
19528             var li = document.createElement("li");
19529             li.className = "x-menu-list-item";
19530             ul.dom.appendChild(li);
19531             item.render(li, this);
19532         }, this);
19533         this.ul = ul;
19534         this.autoWidth();
19535     },
19536
19537     // private
19538     autoWidth : function(){
19539         var el = this.el, ul = this.ul;
19540         if(!el){
19541             return;
19542         }
19543         var w = this.width;
19544         if(w){
19545             el.setWidth(w);
19546         }else if(Roo.isIE){
19547             el.setWidth(this.minWidth);
19548             var t = el.dom.offsetWidth; // force recalc
19549             el.setWidth(ul.getWidth()+el.getFrameWidth("lr"));
19550         }
19551     },
19552
19553     // private
19554     delayAutoWidth : function(){
19555         if(this.rendered){
19556             if(!this.awTask){
19557                 this.awTask = new Roo.util.DelayedTask(this.autoWidth, this);
19558             }
19559             this.awTask.delay(20);
19560         }
19561     },
19562
19563     // private
19564     findTargetItem : function(e){
19565         var t = e.getTarget(".x-menu-list-item", this.ul,  true);
19566         if(t && t.menuItemId){
19567             return this.items.get(t.menuItemId);
19568         }
19569     },
19570
19571     // private
19572     onClick : function(e){
19573         var t;
19574         if(t = this.findTargetItem(e)){
19575             t.onClick(e);
19576             this.fireEvent("click", this, t, e);
19577         }
19578     },
19579
19580     // private
19581     setActiveItem : function(item, autoExpand){
19582         if(item != this.activeItem){
19583             if(this.activeItem){
19584                 this.activeItem.deactivate();
19585             }
19586             this.activeItem = item;
19587             item.activate(autoExpand);
19588         }else if(autoExpand){
19589             item.expandMenu();
19590         }
19591     },
19592
19593     // private
19594     tryActivate : function(start, step){
19595         var items = this.items;
19596         for(var i = start, len = items.length; i >= 0 && i < len; i+= step){
19597             var item = items.get(i);
19598             if(!item.disabled && item.canActivate){
19599                 this.setActiveItem(item, false);
19600                 return item;
19601             }
19602         }
19603         return false;
19604     },
19605
19606     // private
19607     onMouseOver : function(e){
19608         var t;
19609         if(t = this.findTargetItem(e)){
19610             if(t.canActivate && !t.disabled){
19611                 this.setActiveItem(t, true);
19612             }
19613         }
19614         this.fireEvent("mouseover", this, e, t);
19615     },
19616
19617     // private
19618     onMouseOut : function(e){
19619         var t;
19620         if(t = this.findTargetItem(e)){
19621             if(t == this.activeItem && t.shouldDeactivate(e)){
19622                 this.activeItem.deactivate();
19623                 delete this.activeItem;
19624             }
19625         }
19626         this.fireEvent("mouseout", this, e, t);
19627     },
19628
19629     /**
19630      * Read-only.  Returns true if the menu is currently displayed, else false.
19631      * @type Boolean
19632      */
19633     isVisible : function(){
19634         return this.el && !this.hidden;
19635     },
19636
19637     /**
19638      * Displays this menu relative to another element
19639      * @param {String/HTMLElement/Roo.Element} element The element to align to
19640      * @param {String} position (optional) The {@link Roo.Element#alignTo} anchor position to use in aligning to
19641      * the element (defaults to this.defaultAlign)
19642      * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
19643      */
19644     show : function(el, pos, parentMenu){
19645         this.parentMenu = parentMenu;
19646         if(!this.el){
19647             this.render();
19648         }
19649         this.fireEvent("beforeshow", this);
19650         this.showAt(this.el.getAlignToXY(el, pos || this.defaultAlign), parentMenu, false);
19651     },
19652
19653     /**
19654      * Displays this menu at a specific xy position
19655      * @param {Array} xyPosition Contains X & Y [x, y] values for the position at which to show the menu (coordinates are page-based)
19656      * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
19657      */
19658     showAt : function(xy, parentMenu, /* private: */_e){
19659         this.parentMenu = parentMenu;
19660         if(!this.el){
19661             this.render();
19662         }
19663         if(_e !== false){
19664             this.fireEvent("beforeshow", this);
19665             xy = this.el.adjustForConstraints(xy);
19666         }
19667         this.el.setXY(xy);
19668         this.el.show();
19669         this.hidden = false;
19670         this.focus();
19671         this.fireEvent("show", this);
19672     },
19673
19674     focus : function(){
19675         if(!this.hidden){
19676             this.doFocus.defer(50, this);
19677         }
19678     },
19679
19680     doFocus : function(){
19681         if(!this.hidden){
19682             this.focusEl.focus();
19683         }
19684     },
19685
19686     /**
19687      * Hides this menu and optionally all parent menus
19688      * @param {Boolean} deep (optional) True to hide all parent menus recursively, if any (defaults to false)
19689      */
19690     hide : function(deep){
19691         if(this.el && this.isVisible()){
19692             this.fireEvent("beforehide", this);
19693             if(this.activeItem){
19694                 this.activeItem.deactivate();
19695                 this.activeItem = null;
19696             }
19697             this.el.hide();
19698             this.hidden = true;
19699             this.fireEvent("hide", this);
19700         }
19701         if(deep === true && this.parentMenu){
19702             this.parentMenu.hide(true);
19703         }
19704     },
19705
19706     /**
19707      * Addds one or more items of any type supported by the Menu class, or that can be converted into menu items.
19708      * Any of the following are valid:
19709      * <ul>
19710      * <li>Any menu item object based on {@link Roo.menu.Item}</li>
19711      * <li>An HTMLElement object which will be converted to a menu item</li>
19712      * <li>A menu item config object that will be created as a new menu item</li>
19713      * <li>A string, which can either be '-' or 'separator' to add a menu separator, otherwise
19714      * it will be converted into a {@link Roo.menu.TextItem} and added</li>
19715      * </ul>
19716      * Usage:
19717      * <pre><code>
19718 // Create the menu
19719 var menu = new Roo.menu.Menu();
19720
19721 // Create a menu item to add by reference
19722 var menuItem = new Roo.menu.Item({ text: 'New Item!' });
19723
19724 // Add a bunch of items at once using different methods.
19725 // Only the last item added will be returned.
19726 var item = menu.add(
19727     menuItem,                // add existing item by ref
19728     'Dynamic Item',          // new TextItem
19729     '-',                     // new separator
19730     { text: 'Config Item' }  // new item by config
19731 );
19732 </code></pre>
19733      * @param {Mixed} args One or more menu items, menu item configs or other objects that can be converted to menu items
19734      * @return {Roo.menu.Item} The menu item that was added, or the last one if multiple items were added
19735      */
19736     add : function(){
19737         var a = arguments, l = a.length, item;
19738         for(var i = 0; i < l; i++){
19739             var el = a[i];
19740             if ((typeof(el) == "object") && el.xtype && el.xns) {
19741                 el = Roo.factory(el, Roo.menu);
19742             }
19743             
19744             if(el.render){ // some kind of Item
19745                 item = this.addItem(el);
19746             }else if(typeof el == "string"){ // string
19747                 if(el == "separator" || el == "-"){
19748                     item = this.addSeparator();
19749                 }else{
19750                     item = this.addText(el);
19751                 }
19752             }else if(el.tagName || el.el){ // element
19753                 item = this.addElement(el);
19754             }else if(typeof el == "object"){ // must be menu item config?
19755                 item = this.addMenuItem(el);
19756             }
19757         }
19758         return item;
19759     },
19760
19761     /**
19762      * Returns this menu's underlying {@link Roo.Element} object
19763      * @return {Roo.Element} The element
19764      */
19765     getEl : function(){
19766         if(!this.el){
19767             this.render();
19768         }
19769         return this.el;
19770     },
19771
19772     /**
19773      * Adds a separator bar to the menu
19774      * @return {Roo.menu.Item} The menu item that was added
19775      */
19776     addSeparator : function(){
19777         return this.addItem(new Roo.menu.Separator());
19778     },
19779
19780     /**
19781      * Adds an {@link Roo.Element} object to the menu
19782      * @param {String/HTMLElement/Roo.Element} el The element or DOM node to add, or its id
19783      * @return {Roo.menu.Item} The menu item that was added
19784      */
19785     addElement : function(el){
19786         return this.addItem(new Roo.menu.BaseItem(el));
19787     },
19788
19789     /**
19790      * Adds an existing object based on {@link Roo.menu.Item} to the menu
19791      * @param {Roo.menu.Item} item The menu item to add
19792      * @return {Roo.menu.Item} The menu item that was added
19793      */
19794     addItem : function(item){
19795         this.items.add(item);
19796         if(this.ul){
19797             var li = document.createElement("li");
19798             li.className = "x-menu-list-item";
19799             this.ul.dom.appendChild(li);
19800             item.render(li, this);
19801             this.delayAutoWidth();
19802         }
19803         return item;
19804     },
19805
19806     /**
19807      * Creates a new {@link Roo.menu.Item} based an the supplied config object and adds it to the menu
19808      * @param {Object} config A MenuItem config object
19809      * @return {Roo.menu.Item} The menu item that was added
19810      */
19811     addMenuItem : function(config){
19812         if(!(config instanceof Roo.menu.Item)){
19813             if(typeof config.checked == "boolean"){ // must be check menu item config?
19814                 config = new Roo.menu.CheckItem(config);
19815             }else{
19816                 config = new Roo.menu.Item(config);
19817             }
19818         }
19819         return this.addItem(config);
19820     },
19821
19822     /**
19823      * Creates a new {@link Roo.menu.TextItem} with the supplied text and adds it to the menu
19824      * @param {String} text The text to display in the menu item
19825      * @return {Roo.menu.Item} The menu item that was added
19826      */
19827     addText : function(text){
19828         return this.addItem(new Roo.menu.TextItem({ text : text }));
19829     },
19830
19831     /**
19832      * Inserts an existing object based on {@link Roo.menu.Item} to the menu at a specified index
19833      * @param {Number} index The index in the menu's list of current items where the new item should be inserted
19834      * @param {Roo.menu.Item} item The menu item to add
19835      * @return {Roo.menu.Item} The menu item that was added
19836      */
19837     insert : function(index, item){
19838         this.items.insert(index, item);
19839         if(this.ul){
19840             var li = document.createElement("li");
19841             li.className = "x-menu-list-item";
19842             this.ul.dom.insertBefore(li, this.ul.dom.childNodes[index]);
19843             item.render(li, this);
19844             this.delayAutoWidth();
19845         }
19846         return item;
19847     },
19848
19849     /**
19850      * Removes an {@link Roo.menu.Item} from the menu and destroys the object
19851      * @param {Roo.menu.Item} item The menu item to remove
19852      */
19853     remove : function(item){
19854         this.items.removeKey(item.id);
19855         item.destroy();
19856     },
19857
19858     /**
19859      * Removes and destroys all items in the menu
19860      */
19861     removeAll : function(){
19862         var f;
19863         while(f = this.items.first()){
19864             this.remove(f);
19865         }
19866     }
19867 });
19868
19869 // MenuNav is a private utility class used internally by the Menu
19870 Roo.menu.MenuNav = function(menu){
19871     Roo.menu.MenuNav.superclass.constructor.call(this, menu.el);
19872     this.scope = this.menu = menu;
19873 };
19874
19875 Roo.extend(Roo.menu.MenuNav, Roo.KeyNav, {
19876     doRelay : function(e, h){
19877         var k = e.getKey();
19878         if(!this.menu.activeItem && e.isNavKeyPress() && k != e.SPACE && k != e.RETURN){
19879             this.menu.tryActivate(0, 1);
19880             return false;
19881         }
19882         return h.call(this.scope || this, e, this.menu);
19883     },
19884
19885     up : function(e, m){
19886         if(!m.tryActivate(m.items.indexOf(m.activeItem)-1, -1)){
19887             m.tryActivate(m.items.length-1, -1);
19888         }
19889     },
19890
19891     down : function(e, m){
19892         if(!m.tryActivate(m.items.indexOf(m.activeItem)+1, 1)){
19893             m.tryActivate(0, 1);
19894         }
19895     },
19896
19897     right : function(e, m){
19898         if(m.activeItem){
19899             m.activeItem.expandMenu(true);
19900         }
19901     },
19902
19903     left : function(e, m){
19904         m.hide();
19905         if(m.parentMenu && m.parentMenu.activeItem){
19906             m.parentMenu.activeItem.activate();
19907         }
19908     },
19909
19910     enter : function(e, m){
19911         if(m.activeItem){
19912             e.stopPropagation();
19913             m.activeItem.onClick(e);
19914             m.fireEvent("click", this, m.activeItem);
19915             return true;
19916         }
19917     }
19918 });/*
19919  * Based on:
19920  * Ext JS Library 1.1.1
19921  * Copyright(c) 2006-2007, Ext JS, LLC.
19922  *
19923  * Originally Released Under LGPL - original licence link has changed is not relivant.
19924  *
19925  * Fork - LGPL
19926  * <script type="text/javascript">
19927  */
19928  
19929 /**
19930  * @class Roo.menu.MenuMgr
19931  * Provides a common registry of all menu items on a page so that they can be easily accessed by id.
19932  * @singleton
19933  */
19934 Roo.menu.MenuMgr = function(){
19935    var menus, active, groups = {}, attached = false, lastShow = new Date();
19936
19937    // private - called when first menu is created
19938    function init(){
19939        menus = {};
19940        active = new Roo.util.MixedCollection();
19941        Roo.get(document).addKeyListener(27, function(){
19942            if(active.length > 0){
19943                hideAll();
19944            }
19945        });
19946    }
19947
19948    // private
19949    function hideAll(){
19950        if(active && active.length > 0){
19951            var c = active.clone();
19952            c.each(function(m){
19953                m.hide();
19954            });
19955        }
19956    }
19957
19958    // private
19959    function onHide(m){
19960        active.remove(m);
19961        if(active.length < 1){
19962            Roo.get(document).un("mousedown", onMouseDown);
19963            attached = false;
19964        }
19965    }
19966
19967    // private
19968    function onShow(m){
19969        var last = active.last();
19970        lastShow = new Date();
19971        active.add(m);
19972        if(!attached){
19973            Roo.get(document).on("mousedown", onMouseDown);
19974            attached = true;
19975        }
19976        if(m.parentMenu){
19977           m.getEl().setZIndex(parseInt(m.parentMenu.getEl().getStyle("z-index"), 10) + 3);
19978           m.parentMenu.activeChild = m;
19979        }else if(last && last.isVisible()){
19980           m.getEl().setZIndex(parseInt(last.getEl().getStyle("z-index"), 10) + 3);
19981        }
19982    }
19983
19984    // private
19985    function onBeforeHide(m){
19986        if(m.activeChild){
19987            m.activeChild.hide();
19988        }
19989        if(m.autoHideTimer){
19990            clearTimeout(m.autoHideTimer);
19991            delete m.autoHideTimer;
19992        }
19993    }
19994
19995    // private
19996    function onBeforeShow(m){
19997        var pm = m.parentMenu;
19998        if(!pm && !m.allowOtherMenus){
19999            hideAll();
20000        }else if(pm && pm.activeChild && active != m){
20001            pm.activeChild.hide();
20002        }
20003    }
20004
20005    // private
20006    function onMouseDown(e){
20007        if(lastShow.getElapsed() > 50 && active.length > 0 && !e.getTarget(".x-menu")){
20008            hideAll();
20009        }
20010    }
20011
20012    // private
20013    function onBeforeCheck(mi, state){
20014        if(state){
20015            var g = groups[mi.group];
20016            for(var i = 0, l = g.length; i < l; i++){
20017                if(g[i] != mi){
20018                    g[i].setChecked(false);
20019                }
20020            }
20021        }
20022    }
20023
20024    return {
20025
20026        /**
20027         * Hides all menus that are currently visible
20028         */
20029        hideAll : function(){
20030             hideAll();  
20031        },
20032
20033        // private
20034        register : function(menu){
20035            if(!menus){
20036                init();
20037            }
20038            menus[menu.id] = menu;
20039            menu.on("beforehide", onBeforeHide);
20040            menu.on("hide", onHide);
20041            menu.on("beforeshow", onBeforeShow);
20042            menu.on("show", onShow);
20043            var g = menu.group;
20044            if(g && menu.events["checkchange"]){
20045                if(!groups[g]){
20046                    groups[g] = [];
20047                }
20048                groups[g].push(menu);
20049                menu.on("checkchange", onCheck);
20050            }
20051        },
20052
20053         /**
20054          * Returns a {@link Roo.menu.Menu} object
20055          * @param {String/Object} menu The string menu id, an existing menu object reference, or a Menu config that will
20056          * be used to generate and return a new Menu instance.
20057          */
20058        get : function(menu){
20059            if(typeof menu == "string"){ // menu id
20060                return menus[menu];
20061            }else if(menu.events){  // menu instance
20062                return menu;
20063            }else if(typeof menu.length == 'number'){ // array of menu items?
20064                return new Roo.menu.Menu({items:menu});
20065            }else{ // otherwise, must be a config
20066                return new Roo.menu.Menu(menu);
20067            }
20068        },
20069
20070        // private
20071        unregister : function(menu){
20072            delete menus[menu.id];
20073            menu.un("beforehide", onBeforeHide);
20074            menu.un("hide", onHide);
20075            menu.un("beforeshow", onBeforeShow);
20076            menu.un("show", onShow);
20077            var g = menu.group;
20078            if(g && menu.events["checkchange"]){
20079                groups[g].remove(menu);
20080                menu.un("checkchange", onCheck);
20081            }
20082        },
20083
20084        // private
20085        registerCheckable : function(menuItem){
20086            var g = menuItem.group;
20087            if(g){
20088                if(!groups[g]){
20089                    groups[g] = [];
20090                }
20091                groups[g].push(menuItem);
20092                menuItem.on("beforecheckchange", onBeforeCheck);
20093            }
20094        },
20095
20096        // private
20097        unregisterCheckable : function(menuItem){
20098            var g = menuItem.group;
20099            if(g){
20100                groups[g].remove(menuItem);
20101                menuItem.un("beforecheckchange", onBeforeCheck);
20102            }
20103        }
20104    };
20105 }();/*
20106  * Based on:
20107  * Ext JS Library 1.1.1
20108  * Copyright(c) 2006-2007, Ext JS, LLC.
20109  *
20110  * Originally Released Under LGPL - original licence link has changed is not relivant.
20111  *
20112  * Fork - LGPL
20113  * <script type="text/javascript">
20114  */
20115  
20116
20117 /**
20118  * @class Roo.menu.BaseItem
20119  * @extends Roo.Component
20120  * The base class for all items that render into menus.  BaseItem provides default rendering, activated state
20121  * management and base configuration options shared by all menu components.
20122  * @constructor
20123  * Creates a new BaseItem
20124  * @param {Object} config Configuration options
20125  */
20126 Roo.menu.BaseItem = function(config){
20127     Roo.menu.BaseItem.superclass.constructor.call(this, config);
20128
20129     this.addEvents({
20130         /**
20131          * @event click
20132          * Fires when this item is clicked
20133          * @param {Roo.menu.BaseItem} this
20134          * @param {Roo.EventObject} e
20135          */
20136         click: true,
20137         /**
20138          * @event activate
20139          * Fires when this item is activated
20140          * @param {Roo.menu.BaseItem} this
20141          */
20142         activate : true,
20143         /**
20144          * @event deactivate
20145          * Fires when this item is deactivated
20146          * @param {Roo.menu.BaseItem} this
20147          */
20148         deactivate : true
20149     });
20150
20151     if(this.handler){
20152         this.on("click", this.handler, this.scope, true);
20153     }
20154 };
20155
20156 Roo.extend(Roo.menu.BaseItem, Roo.Component, {
20157     /**
20158      * @cfg {Function} handler
20159      * A function that will handle the click event of this menu item (defaults to undefined)
20160      */
20161     /**
20162      * @cfg {Boolean} canActivate True if this item can be visually activated (defaults to false)
20163      */
20164     canActivate : false,
20165     /**
20166      * @cfg {String} activeClass The CSS class to use when the item becomes activated (defaults to "x-menu-item-active")
20167      */
20168     activeClass : "x-menu-item-active",
20169     /**
20170      * @cfg {Boolean} hideOnClick True to hide the containing menu after this item is clicked (defaults to true)
20171      */
20172     hideOnClick : true,
20173     /**
20174      * @cfg {Number} hideDelay Length of time in milliseconds to wait before hiding after a click (defaults to 100)
20175      */
20176     hideDelay : 100,
20177
20178     // private
20179     ctype: "Roo.menu.BaseItem",
20180
20181     // private
20182     actionMode : "container",
20183
20184     // private
20185     render : function(container, parentMenu){
20186         this.parentMenu = parentMenu;
20187         Roo.menu.BaseItem.superclass.render.call(this, container);
20188         this.container.menuItemId = this.id;
20189     },
20190
20191     // private
20192     onRender : function(container, position){
20193         this.el = Roo.get(this.el);
20194         container.dom.appendChild(this.el.dom);
20195     },
20196
20197     // private
20198     onClick : function(e){
20199         if(!this.disabled && this.fireEvent("click", this, e) !== false
20200                 && this.parentMenu.fireEvent("itemclick", this, e) !== false){
20201             this.handleClick(e);
20202         }else{
20203             e.stopEvent();
20204         }
20205     },
20206
20207     // private
20208     activate : function(){
20209         if(this.disabled){
20210             return false;
20211         }
20212         var li = this.container;
20213         li.addClass(this.activeClass);
20214         this.region = li.getRegion().adjust(2, 2, -2, -2);
20215         this.fireEvent("activate", this);
20216         return true;
20217     },
20218
20219     // private
20220     deactivate : function(){
20221         this.container.removeClass(this.activeClass);
20222         this.fireEvent("deactivate", this);
20223     },
20224
20225     // private
20226     shouldDeactivate : function(e){
20227         return !this.region || !this.region.contains(e.getPoint());
20228     },
20229
20230     // private
20231     handleClick : function(e){
20232         if(this.hideOnClick){
20233             this.parentMenu.hide.defer(this.hideDelay, this.parentMenu, [true]);
20234         }
20235     },
20236
20237     // private
20238     expandMenu : function(autoActivate){
20239         // do nothing
20240     },
20241
20242     // private
20243     hideMenu : function(){
20244         // do nothing
20245     }
20246 });/*
20247  * Based on:
20248  * Ext JS Library 1.1.1
20249  * Copyright(c) 2006-2007, Ext JS, LLC.
20250  *
20251  * Originally Released Under LGPL - original licence link has changed is not relivant.
20252  *
20253  * Fork - LGPL
20254  * <script type="text/javascript">
20255  */
20256  
20257 /**
20258  * @class Roo.menu.Adapter
20259  * @extends Roo.menu.BaseItem
20260  * A base utility class that adapts a non-menu component so that it can be wrapped by a menu item and added to a menu.
20261  * It provides basic rendering, activation management and enable/disable logic required to work in menus.
20262  * @constructor
20263  * Creates a new Adapter
20264  * @param {Object} config Configuration options
20265  */
20266 Roo.menu.Adapter = function(component, config){
20267     Roo.menu.Adapter.superclass.constructor.call(this, config);
20268     this.component = component;
20269 };
20270 Roo.extend(Roo.menu.Adapter, Roo.menu.BaseItem, {
20271     // private
20272     canActivate : true,
20273
20274     // private
20275     onRender : function(container, position){
20276         this.component.render(container);
20277         this.el = this.component.getEl();
20278     },
20279
20280     // private
20281     activate : function(){
20282         if(this.disabled){
20283             return false;
20284         }
20285         this.component.focus();
20286         this.fireEvent("activate", this);
20287         return true;
20288     },
20289
20290     // private
20291     deactivate : function(){
20292         this.fireEvent("deactivate", this);
20293     },
20294
20295     // private
20296     disable : function(){
20297         this.component.disable();
20298         Roo.menu.Adapter.superclass.disable.call(this);
20299     },
20300
20301     // private
20302     enable : function(){
20303         this.component.enable();
20304         Roo.menu.Adapter.superclass.enable.call(this);
20305     }
20306 });/*
20307  * Based on:
20308  * Ext JS Library 1.1.1
20309  * Copyright(c) 2006-2007, Ext JS, LLC.
20310  *
20311  * Originally Released Under LGPL - original licence link has changed is not relivant.
20312  *
20313  * Fork - LGPL
20314  * <script type="text/javascript">
20315  */
20316
20317 /**
20318  * @class Roo.menu.TextItem
20319  * @extends Roo.menu.BaseItem
20320  * Adds a static text string to a menu, usually used as either a heading or group separator.
20321  * Note: old style constructor with text is still supported.
20322  * 
20323  * @constructor
20324  * Creates a new TextItem
20325  * @param {Object} cfg Configuration
20326  */
20327 Roo.menu.TextItem = function(cfg){
20328     if (typeof(cfg) == 'string') {
20329         this.text = cfg;
20330     } else {
20331         Roo.apply(this,cfg);
20332     }
20333     
20334     Roo.menu.TextItem.superclass.constructor.call(this);
20335 };
20336
20337 Roo.extend(Roo.menu.TextItem, Roo.menu.BaseItem, {
20338     /**
20339      * @cfg {Boolean} text Text to show on item.
20340      */
20341     text : '',
20342     
20343     /**
20344      * @cfg {Boolean} hideOnClick True to hide the containing menu after this item is clicked (defaults to false)
20345      */
20346     hideOnClick : false,
20347     /**
20348      * @cfg {String} itemCls The default CSS class to use for text items (defaults to "x-menu-text")
20349      */
20350     itemCls : "x-menu-text",
20351
20352     // private
20353     onRender : function(){
20354         var s = document.createElement("span");
20355         s.className = this.itemCls;
20356         s.innerHTML = this.text;
20357         this.el = s;
20358         Roo.menu.TextItem.superclass.onRender.apply(this, arguments);
20359     }
20360 });/*
20361  * Based on:
20362  * Ext JS Library 1.1.1
20363  * Copyright(c) 2006-2007, Ext JS, LLC.
20364  *
20365  * Originally Released Under LGPL - original licence link has changed is not relivant.
20366  *
20367  * Fork - LGPL
20368  * <script type="text/javascript">
20369  */
20370
20371 /**
20372  * @class Roo.menu.Separator
20373  * @extends Roo.menu.BaseItem
20374  * Adds a separator bar to a menu, used to divide logical groups of menu items. Generally you will
20375  * add one of these by using "-" in you call to add() or in your items config rather than creating one directly.
20376  * @constructor
20377  * @param {Object} config Configuration options
20378  */
20379 Roo.menu.Separator = function(config){
20380     Roo.menu.Separator.superclass.constructor.call(this, config);
20381 };
20382
20383 Roo.extend(Roo.menu.Separator, Roo.menu.BaseItem, {
20384     /**
20385      * @cfg {String} itemCls The default CSS class to use for separators (defaults to "x-menu-sep")
20386      */
20387     itemCls : "x-menu-sep",
20388     /**
20389      * @cfg {Boolean} hideOnClick True to hide the containing menu after this item is clicked (defaults to false)
20390      */
20391     hideOnClick : false,
20392
20393     // private
20394     onRender : function(li){
20395         var s = document.createElement("span");
20396         s.className = this.itemCls;
20397         s.innerHTML = "&#160;";
20398         this.el = s;
20399         li.addClass("x-menu-sep-li");
20400         Roo.menu.Separator.superclass.onRender.apply(this, arguments);
20401     }
20402 });/*
20403  * Based on:
20404  * Ext JS Library 1.1.1
20405  * Copyright(c) 2006-2007, Ext JS, LLC.
20406  *
20407  * Originally Released Under LGPL - original licence link has changed is not relivant.
20408  *
20409  * Fork - LGPL
20410  * <script type="text/javascript">
20411  */
20412 /**
20413  * @class Roo.menu.Item
20414  * @extends Roo.menu.BaseItem
20415  * A base class for all menu items that require menu-related functionality (like sub-menus) and are not static
20416  * display items.  Item extends the base functionality of {@link Roo.menu.BaseItem} by adding menu-specific
20417  * activation and click handling.
20418  * @constructor
20419  * Creates a new Item
20420  * @param {Object} config Configuration options
20421  */
20422 Roo.menu.Item = function(config){
20423     Roo.menu.Item.superclass.constructor.call(this, config);
20424     if(this.menu){
20425         this.menu = Roo.menu.MenuMgr.get(this.menu);
20426     }
20427 };
20428 Roo.extend(Roo.menu.Item, Roo.menu.BaseItem, {
20429     
20430     /**
20431      * @cfg {String} text
20432      * The text to show on the menu item.
20433      */
20434     text: '',
20435      /**
20436      * @cfg {String} HTML to render in menu
20437      * The text to show on the menu item (HTML version).
20438      */
20439     html: '',
20440     /**
20441      * @cfg {String} icon
20442      * The path to an icon to display in this menu item (defaults to Roo.BLANK_IMAGE_URL)
20443      */
20444     icon: undefined,
20445     /**
20446      * @cfg {String} itemCls The default CSS class to use for menu items (defaults to "x-menu-item")
20447      */
20448     itemCls : "x-menu-item",
20449     /**
20450      * @cfg {Boolean} canActivate True if this item can be visually activated (defaults to true)
20451      */
20452     canActivate : true,
20453     /**
20454      * @cfg {Number} showDelay Length of time in milliseconds to wait before showing this item (defaults to 200)
20455      */
20456     showDelay: 200,
20457     // doc'd in BaseItem
20458     hideDelay: 200,
20459
20460     // private
20461     ctype: "Roo.menu.Item",
20462     
20463     // private
20464     onRender : function(container, position){
20465         var el = document.createElement("a");
20466         el.hideFocus = true;
20467         el.unselectable = "on";
20468         el.href = this.href || "#";
20469         if(this.hrefTarget){
20470             el.target = this.hrefTarget;
20471         }
20472         el.className = this.itemCls + (this.menu ?  " x-menu-item-arrow" : "") + (this.cls ?  " " + this.cls : "");
20473         
20474         var html = this.html.length ? this.html  : String.format('{0}',this.text);
20475         
20476         el.innerHTML = String.format(
20477                 '<img src="{0}" class="x-menu-item-icon {1}" />' + html,
20478                 this.icon || Roo.BLANK_IMAGE_URL, this.iconCls || '');
20479         this.el = el;
20480         Roo.menu.Item.superclass.onRender.call(this, container, position);
20481     },
20482
20483     /**
20484      * Sets the text to display in this menu item
20485      * @param {String} text The text to display
20486      * @param {Boolean} isHTML true to indicate text is pure html.
20487      */
20488     setText : function(text, isHTML){
20489         if (isHTML) {
20490             this.html = text;
20491         } else {
20492             this.text = text;
20493             this.html = '';
20494         }
20495         if(this.rendered){
20496             var html = this.html.length ? this.html  : String.format('{0}',this.text);
20497      
20498             this.el.update(String.format(
20499                 '<img src="{0}" class="x-menu-item-icon {2}">' + html,
20500                 this.icon || Roo.BLANK_IMAGE_URL, this.text, this.iconCls || ''));
20501             this.parentMenu.autoWidth();
20502         }
20503     },
20504
20505     // private
20506     handleClick : function(e){
20507         if(!this.href){ // if no link defined, stop the event automatically
20508             e.stopEvent();
20509         }
20510         Roo.menu.Item.superclass.handleClick.apply(this, arguments);
20511     },
20512
20513     // private
20514     activate : function(autoExpand){
20515         if(Roo.menu.Item.superclass.activate.apply(this, arguments)){
20516             this.focus();
20517             if(autoExpand){
20518                 this.expandMenu();
20519             }
20520         }
20521         return true;
20522     },
20523
20524     // private
20525     shouldDeactivate : function(e){
20526         if(Roo.menu.Item.superclass.shouldDeactivate.call(this, e)){
20527             if(this.menu && this.menu.isVisible()){
20528                 return !this.menu.getEl().getRegion().contains(e.getPoint());
20529             }
20530             return true;
20531         }
20532         return false;
20533     },
20534
20535     // private
20536     deactivate : function(){
20537         Roo.menu.Item.superclass.deactivate.apply(this, arguments);
20538         this.hideMenu();
20539     },
20540
20541     // private
20542     expandMenu : function(autoActivate){
20543         if(!this.disabled && this.menu){
20544             clearTimeout(this.hideTimer);
20545             delete this.hideTimer;
20546             if(!this.menu.isVisible() && !this.showTimer){
20547                 this.showTimer = this.deferExpand.defer(this.showDelay, this, [autoActivate]);
20548             }else if (this.menu.isVisible() && autoActivate){
20549                 this.menu.tryActivate(0, 1);
20550             }
20551         }
20552     },
20553
20554     // private
20555     deferExpand : function(autoActivate){
20556         delete this.showTimer;
20557         this.menu.show(this.container, this.parentMenu.subMenuAlign || "tl-tr?", this.parentMenu);
20558         if(autoActivate){
20559             this.menu.tryActivate(0, 1);
20560         }
20561     },
20562
20563     // private
20564     hideMenu : function(){
20565         clearTimeout(this.showTimer);
20566         delete this.showTimer;
20567         if(!this.hideTimer && this.menu && this.menu.isVisible()){
20568             this.hideTimer = this.deferHide.defer(this.hideDelay, this);
20569         }
20570     },
20571
20572     // private
20573     deferHide : function(){
20574         delete this.hideTimer;
20575         this.menu.hide();
20576     }
20577 });/*
20578  * Based on:
20579  * Ext JS Library 1.1.1
20580  * Copyright(c) 2006-2007, Ext JS, LLC.
20581  *
20582  * Originally Released Under LGPL - original licence link has changed is not relivant.
20583  *
20584  * Fork - LGPL
20585  * <script type="text/javascript">
20586  */
20587  
20588 /**
20589  * @class Roo.menu.CheckItem
20590  * @extends Roo.menu.Item
20591  * Adds a menu item that contains a checkbox by default, but can also be part of a radio group.
20592  * @constructor
20593  * Creates a new CheckItem
20594  * @param {Object} config Configuration options
20595  */
20596 Roo.menu.CheckItem = function(config){
20597     Roo.menu.CheckItem.superclass.constructor.call(this, config);
20598     this.addEvents({
20599         /**
20600          * @event beforecheckchange
20601          * Fires before the checked value is set, providing an opportunity to cancel if needed
20602          * @param {Roo.menu.CheckItem} this
20603          * @param {Boolean} checked The new checked value that will be set
20604          */
20605         "beforecheckchange" : true,
20606         /**
20607          * @event checkchange
20608          * Fires after the checked value has been set
20609          * @param {Roo.menu.CheckItem} this
20610          * @param {Boolean} checked The checked value that was set
20611          */
20612         "checkchange" : true
20613     });
20614     if(this.checkHandler){
20615         this.on('checkchange', this.checkHandler, this.scope);
20616     }
20617 };
20618 Roo.extend(Roo.menu.CheckItem, Roo.menu.Item, {
20619     /**
20620      * @cfg {String} group
20621      * All check items with the same group name will automatically be grouped into a single-select
20622      * radio button group (defaults to '')
20623      */
20624     /**
20625      * @cfg {String} itemCls The default CSS class to use for check items (defaults to "x-menu-item x-menu-check-item")
20626      */
20627     itemCls : "x-menu-item x-menu-check-item",
20628     /**
20629      * @cfg {String} groupClass The default CSS class to use for radio group check items (defaults to "x-menu-group-item")
20630      */
20631     groupClass : "x-menu-group-item",
20632
20633     /**
20634      * @cfg {Boolean} checked True to initialize this checkbox as checked (defaults to false).  Note that
20635      * if this checkbox is part of a radio group (group = true) only the last item in the group that is
20636      * initialized with checked = true will be rendered as checked.
20637      */
20638     checked: false,
20639
20640     // private
20641     ctype: "Roo.menu.CheckItem",
20642
20643     // private
20644     onRender : function(c){
20645         Roo.menu.CheckItem.superclass.onRender.apply(this, arguments);
20646         if(this.group){
20647             this.el.addClass(this.groupClass);
20648         }
20649         Roo.menu.MenuMgr.registerCheckable(this);
20650         if(this.checked){
20651             this.checked = false;
20652             this.setChecked(true, true);
20653         }
20654     },
20655
20656     // private
20657     destroy : function(){
20658         if(this.rendered){
20659             Roo.menu.MenuMgr.unregisterCheckable(this);
20660         }
20661         Roo.menu.CheckItem.superclass.destroy.apply(this, arguments);
20662     },
20663
20664     /**
20665      * Set the checked state of this item
20666      * @param {Boolean} checked The new checked value
20667      * @param {Boolean} suppressEvent (optional) True to prevent the checkchange event from firing (defaults to false)
20668      */
20669     setChecked : function(state, suppressEvent){
20670         if(this.checked != state && this.fireEvent("beforecheckchange", this, state) !== false){
20671             if(this.container){
20672                 this.container[state ? "addClass" : "removeClass"]("x-menu-item-checked");
20673             }
20674             this.checked = state;
20675             if(suppressEvent !== true){
20676                 this.fireEvent("checkchange", this, state);
20677             }
20678         }
20679     },
20680
20681     // private
20682     handleClick : function(e){
20683        if(!this.disabled && !(this.checked && this.group)){// disable unselect on radio item
20684            this.setChecked(!this.checked);
20685        }
20686        Roo.menu.CheckItem.superclass.handleClick.apply(this, arguments);
20687     }
20688 });/*
20689  * Based on:
20690  * Ext JS Library 1.1.1
20691  * Copyright(c) 2006-2007, Ext JS, LLC.
20692  *
20693  * Originally Released Under LGPL - original licence link has changed is not relivant.
20694  *
20695  * Fork - LGPL
20696  * <script type="text/javascript">
20697  */
20698  
20699 /**
20700  * @class Roo.menu.DateItem
20701  * @extends Roo.menu.Adapter
20702  * A menu item that wraps the {@link Roo.DatPicker} component.
20703  * @constructor
20704  * Creates a new DateItem
20705  * @param {Object} config Configuration options
20706  */
20707 Roo.menu.DateItem = function(config){
20708     Roo.menu.DateItem.superclass.constructor.call(this, new Roo.DatePicker(config), config);
20709     /** The Roo.DatePicker object @type Roo.DatePicker */
20710     this.picker = this.component;
20711     this.addEvents({select: true});
20712     
20713     this.picker.on("render", function(picker){
20714         picker.getEl().swallowEvent("click");
20715         picker.container.addClass("x-menu-date-item");
20716     });
20717
20718     this.picker.on("select", this.onSelect, this);
20719 };
20720
20721 Roo.extend(Roo.menu.DateItem, Roo.menu.Adapter, {
20722     // private
20723     onSelect : function(picker, date){
20724         this.fireEvent("select", this, date, picker);
20725         Roo.menu.DateItem.superclass.handleClick.call(this);
20726     }
20727 });/*
20728  * Based on:
20729  * Ext JS Library 1.1.1
20730  * Copyright(c) 2006-2007, Ext JS, LLC.
20731  *
20732  * Originally Released Under LGPL - original licence link has changed is not relivant.
20733  *
20734  * Fork - LGPL
20735  * <script type="text/javascript">
20736  */
20737  
20738 /**
20739  * @class Roo.menu.ColorItem
20740  * @extends Roo.menu.Adapter
20741  * A menu item that wraps the {@link Roo.ColorPalette} component.
20742  * @constructor
20743  * Creates a new ColorItem
20744  * @param {Object} config Configuration options
20745  */
20746 Roo.menu.ColorItem = function(config){
20747     Roo.menu.ColorItem.superclass.constructor.call(this, new Roo.ColorPalette(config), config);
20748     /** The Roo.ColorPalette object @type Roo.ColorPalette */
20749     this.palette = this.component;
20750     this.relayEvents(this.palette, ["select"]);
20751     if(this.selectHandler){
20752         this.on('select', this.selectHandler, this.scope);
20753     }
20754 };
20755 Roo.extend(Roo.menu.ColorItem, Roo.menu.Adapter);/*
20756  * Based on:
20757  * Ext JS Library 1.1.1
20758  * Copyright(c) 2006-2007, Ext JS, LLC.
20759  *
20760  * Originally Released Under LGPL - original licence link has changed is not relivant.
20761  *
20762  * Fork - LGPL
20763  * <script type="text/javascript">
20764  */
20765  
20766
20767 /**
20768  * @class Roo.menu.DateMenu
20769  * @extends Roo.menu.Menu
20770  * A menu containing a {@link Roo.menu.DateItem} component (which provides a date picker).
20771  * @constructor
20772  * Creates a new DateMenu
20773  * @param {Object} config Configuration options
20774  */
20775 Roo.menu.DateMenu = function(config){
20776     Roo.menu.DateMenu.superclass.constructor.call(this, config);
20777     this.plain = true;
20778     var di = new Roo.menu.DateItem(config);
20779     this.add(di);
20780     /**
20781      * The {@link Roo.DatePicker} instance for this DateMenu
20782      * @type DatePicker
20783      */
20784     this.picker = di.picker;
20785     /**
20786      * @event select
20787      * @param {DatePicker} picker
20788      * @param {Date} date
20789      */
20790     this.relayEvents(di, ["select"]);
20791
20792     this.on('beforeshow', function(){
20793         if(this.picker){
20794             this.picker.hideMonthPicker(true);
20795         }
20796     }, this);
20797 };
20798 Roo.extend(Roo.menu.DateMenu, Roo.menu.Menu, {
20799     cls:'x-date-menu'
20800 });/*
20801  * Based on:
20802  * Ext JS Library 1.1.1
20803  * Copyright(c) 2006-2007, Ext JS, LLC.
20804  *
20805  * Originally Released Under LGPL - original licence link has changed is not relivant.
20806  *
20807  * Fork - LGPL
20808  * <script type="text/javascript">
20809  */
20810  
20811
20812 /**
20813  * @class Roo.menu.ColorMenu
20814  * @extends Roo.menu.Menu
20815  * A menu containing a {@link Roo.menu.ColorItem} component (which provides a basic color picker).
20816  * @constructor
20817  * Creates a new ColorMenu
20818  * @param {Object} config Configuration options
20819  */
20820 Roo.menu.ColorMenu = function(config){
20821     Roo.menu.ColorMenu.superclass.constructor.call(this, config);
20822     this.plain = true;
20823     var ci = new Roo.menu.ColorItem(config);
20824     this.add(ci);
20825     /**
20826      * The {@link Roo.ColorPalette} instance for this ColorMenu
20827      * @type ColorPalette
20828      */
20829     this.palette = ci.palette;
20830     /**
20831      * @event select
20832      * @param {ColorPalette} palette
20833      * @param {String} color
20834      */
20835     this.relayEvents(ci, ["select"]);
20836 };
20837 Roo.extend(Roo.menu.ColorMenu, Roo.menu.Menu);/*
20838  * Based on:
20839  * Ext JS Library 1.1.1
20840  * Copyright(c) 2006-2007, Ext JS, LLC.
20841  *
20842  * Originally Released Under LGPL - original licence link has changed is not relivant.
20843  *
20844  * Fork - LGPL
20845  * <script type="text/javascript">
20846  */
20847  
20848 /**
20849  * @class Roo.form.Field
20850  * @extends Roo.BoxComponent
20851  * Base class for form fields that provides default event handling, sizing, value handling and other functionality.
20852  * @constructor
20853  * Creates a new Field
20854  * @param {Object} config Configuration options
20855  */
20856 Roo.form.Field = function(config){
20857     Roo.form.Field.superclass.constructor.call(this, config);
20858 };
20859
20860 Roo.extend(Roo.form.Field, Roo.BoxComponent,  {
20861     /**
20862      * @cfg {String} fieldLabel Label to use when rendering a form.
20863      */
20864        /**
20865      * @cfg {String} qtip Mouse over tip
20866      */
20867      
20868     /**
20869      * @cfg {String} invalidClass The CSS class to use when marking a field invalid (defaults to "x-form-invalid")
20870      */
20871     invalidClass : "x-form-invalid",
20872     /**
20873      * @cfg {String} invalidText The error text to use when marking a field invalid and no message is provided (defaults to "The value in this field is invalid")
20874      */
20875     invalidText : "The value in this field is invalid",
20876     /**
20877      * @cfg {String} focusClass The CSS class to use when the field receives focus (defaults to "x-form-focus")
20878      */
20879     focusClass : "x-form-focus",
20880     /**
20881      * @cfg {String/Boolean} validationEvent The event that should initiate field validation. Set to false to disable
20882       automatic validation (defaults to "keyup").
20883      */
20884     validationEvent : "keyup",
20885     /**
20886      * @cfg {Boolean} validateOnBlur Whether the field should validate when it loses focus (defaults to true).
20887      */
20888     validateOnBlur : true,
20889     /**
20890      * @cfg {Number} validationDelay The length of time in milliseconds after user input begins until validation is initiated (defaults to 250)
20891      */
20892     validationDelay : 250,
20893     /**
20894      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
20895      * {tag: "input", type: "text", size: "20", autocomplete: "off"})
20896      */
20897     defaultAutoCreate : {tag: "input", type: "text", size: "20", autocomplete: "off"},
20898     /**
20899      * @cfg {String} fieldClass The default CSS class for the field (defaults to "x-form-field")
20900      */
20901     fieldClass : "x-form-field",
20902     /**
20903      * @cfg {String} msgTarget The location where error text should display.  Should be one of the following values (defaults to 'qtip'):
20904      *<pre>
20905 Value         Description
20906 -----------   ----------------------------------------------------------------------
20907 qtip          Display a quick tip when the user hovers over the field
20908 title         Display a default browser title attribute popup
20909 under         Add a block div beneath the field containing the error text
20910 side          Add an error icon to the right of the field with a popup on hover
20911 [element id]  Add the error text directly to the innerHTML of the specified element
20912 </pre>
20913      */
20914     msgTarget : 'qtip',
20915     /**
20916      * @cfg {String} msgFx <b>Experimental</b> The effect used when displaying a validation message under the field (defaults to 'normal').
20917      */
20918     msgFx : 'normal',
20919
20920     /**
20921      * @cfg {Boolean} readOnly True to mark the field as readOnly in HTML (defaults to false) -- Note: this only sets the element's readOnly DOM attribute.
20922      */
20923     readOnly : false,
20924
20925     /**
20926      * @cfg {Boolean} disabled True to disable the field (defaults to false).
20927      */
20928     disabled : false,
20929
20930     /**
20931      * @cfg {String} inputType The type attribute for input fields -- e.g. radio, text, password (defaults to "text").
20932      */
20933     inputType : undefined,
20934     
20935     /**
20936      * @cfg {Number} tabIndex The tabIndex for this field. Note this only applies to fields that are rendered, not those which are built via applyTo (defaults to undefined).
20937          */
20938         tabIndex : undefined,
20939         
20940     // private
20941     isFormField : true,
20942
20943     // private
20944     hasFocus : false,
20945     /**
20946      * @property {Roo.Element} fieldEl
20947      * Element Containing the rendered Field (with label etc.)
20948      */
20949     /**
20950      * @cfg {Mixed} value A value to initialize this field with.
20951      */
20952     value : undefined,
20953
20954     /**
20955      * @cfg {String} name The field's HTML name attribute.
20956      */
20957     /**
20958      * @cfg {String} cls A CSS class to apply to the field's underlying element.
20959      */
20960
20961         // private ??
20962         initComponent : function(){
20963         Roo.form.Field.superclass.initComponent.call(this);
20964         this.addEvents({
20965             /**
20966              * @event focus
20967              * Fires when this field receives input focus.
20968              * @param {Roo.form.Field} this
20969              */
20970             focus : true,
20971             /**
20972              * @event blur
20973              * Fires when this field loses input focus.
20974              * @param {Roo.form.Field} this
20975              */
20976             blur : true,
20977             /**
20978              * @event specialkey
20979              * Fires when any key related to navigation (arrows, tab, enter, esc, etc.) is pressed.  You can check
20980              * {@link Roo.EventObject#getKey} to determine which key was pressed.
20981              * @param {Roo.form.Field} this
20982              * @param {Roo.EventObject} e The event object
20983              */
20984             specialkey : true,
20985             /**
20986              * @event change
20987              * Fires just before the field blurs if the field value has changed.
20988              * @param {Roo.form.Field} this
20989              * @param {Mixed} newValue The new value
20990              * @param {Mixed} oldValue The original value
20991              */
20992             change : true,
20993             /**
20994              * @event invalid
20995              * Fires after the field has been marked as invalid.
20996              * @param {Roo.form.Field} this
20997              * @param {String} msg The validation message
20998              */
20999             invalid : true,
21000             /**
21001              * @event valid
21002              * Fires after the field has been validated with no errors.
21003              * @param {Roo.form.Field} this
21004              */
21005             valid : true,
21006              /**
21007              * @event keyup
21008              * Fires after the key up
21009              * @param {Roo.form.Field} this
21010              * @param {Roo.EventObject}  e The event Object
21011              */
21012             keyup : true
21013         });
21014     },
21015
21016     /**
21017      * Returns the name attribute of the field if available
21018      * @return {String} name The field name
21019      */
21020     getName: function(){
21021          return this.rendered && this.el.dom.name ? this.el.dom.name : (this.hiddenName || '');
21022     },
21023
21024     // private
21025     onRender : function(ct, position){
21026         Roo.form.Field.superclass.onRender.call(this, ct, position);
21027         if(!this.el){
21028             var cfg = this.getAutoCreate();
21029             if(!cfg.name){
21030                 cfg.name = this.name || this.id;
21031             }
21032             if(this.inputType){
21033                 cfg.type = this.inputType;
21034             }
21035             this.el = ct.createChild(cfg, position);
21036         }
21037         var type = this.el.dom.type;
21038         if(type){
21039             if(type == 'password'){
21040                 type = 'text';
21041             }
21042             this.el.addClass('x-form-'+type);
21043         }
21044         if(this.readOnly){
21045             this.el.dom.readOnly = true;
21046         }
21047         if(this.tabIndex !== undefined){
21048             this.el.dom.setAttribute('tabIndex', this.tabIndex);
21049         }
21050
21051         this.el.addClass([this.fieldClass, this.cls]);
21052         this.initValue();
21053     },
21054
21055     /**
21056      * Apply the behaviors of this component to an existing element. <b>This is used instead of render().</b>
21057      * @param {String/HTMLElement/Element} el The id of the node, a DOM node or an existing Element
21058      * @return {Roo.form.Field} this
21059      */
21060     applyTo : function(target){
21061         this.allowDomMove = false;
21062         this.el = Roo.get(target);
21063         this.render(this.el.dom.parentNode);
21064         return this;
21065     },
21066
21067     // private
21068     initValue : function(){
21069         if(this.value !== undefined){
21070             this.setValue(this.value);
21071         }else if(this.el.dom.value.length > 0){
21072             this.setValue(this.el.dom.value);
21073         }
21074     },
21075
21076     /**
21077      * Returns true if this field has been changed since it was originally loaded and is not disabled.
21078      */
21079     isDirty : function() {
21080         if(this.disabled) {
21081             return false;
21082         }
21083         return String(this.getValue()) !== String(this.originalValue);
21084     },
21085
21086     // private
21087     afterRender : function(){
21088         Roo.form.Field.superclass.afterRender.call(this);
21089         this.initEvents();
21090     },
21091
21092     // private
21093     fireKey : function(e){
21094         //Roo.log('field ' + e.getKey());
21095         if(e.isNavKeyPress()){
21096             this.fireEvent("specialkey", this, e);
21097         }
21098     },
21099
21100     /**
21101      * Resets the current field value to the originally loaded value and clears any validation messages
21102      */
21103     reset : function(){
21104         this.setValue(this.originalValue);
21105         this.clearInvalid();
21106     },
21107
21108     // private
21109     initEvents : function(){
21110         // safari killled keypress - so keydown is now used..
21111         this.el.on("keydown" , this.fireKey,  this);
21112         this.el.on("focus", this.onFocus,  this);
21113         this.el.on("blur", this.onBlur,  this);
21114         this.el.relayEvent('keyup', this);
21115
21116         // reference to original value for reset
21117         this.originalValue = this.getValue();
21118     },
21119
21120     // private
21121     onFocus : function(){
21122         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
21123             this.el.addClass(this.focusClass);
21124         }
21125         if(!this.hasFocus){
21126             this.hasFocus = true;
21127             this.startValue = this.getValue();
21128             this.fireEvent("focus", this);
21129         }
21130     },
21131
21132     beforeBlur : Roo.emptyFn,
21133
21134     // private
21135     onBlur : function(){
21136         this.beforeBlur();
21137         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
21138             this.el.removeClass(this.focusClass);
21139         }
21140         this.hasFocus = false;
21141         if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
21142             this.validate();
21143         }
21144         var v = this.getValue();
21145         if(String(v) !== String(this.startValue)){
21146             this.fireEvent('change', this, v, this.startValue);
21147         }
21148         this.fireEvent("blur", this);
21149     },
21150
21151     /**
21152      * Returns whether or not the field value is currently valid
21153      * @param {Boolean} preventMark True to disable marking the field invalid
21154      * @return {Boolean} True if the value is valid, else false
21155      */
21156     isValid : function(preventMark){
21157         if(this.disabled){
21158             return true;
21159         }
21160         var restore = this.preventMark;
21161         this.preventMark = preventMark === true;
21162         var v = this.validateValue(this.processValue(this.getRawValue()));
21163         this.preventMark = restore;
21164         return v;
21165     },
21166
21167     /**
21168      * Validates the field value
21169      * @return {Boolean} True if the value is valid, else false
21170      */
21171     validate : function(){
21172         if(this.disabled || this.validateValue(this.processValue(this.getRawValue()))){
21173             this.clearInvalid();
21174             return true;
21175         }
21176         return false;
21177     },
21178
21179     processValue : function(value){
21180         return value;
21181     },
21182
21183     // private
21184     // Subclasses should provide the validation implementation by overriding this
21185     validateValue : function(value){
21186         return true;
21187     },
21188
21189     /**
21190      * Mark this field as invalid
21191      * @param {String} msg The validation message
21192      */
21193     markInvalid : function(msg){
21194         if(!this.rendered || this.preventMark){ // not rendered
21195             return;
21196         }
21197         this.el.addClass(this.invalidClass);
21198         msg = msg || this.invalidText;
21199         switch(this.msgTarget){
21200             case 'qtip':
21201                 this.el.dom.qtip = msg;
21202                 this.el.dom.qclass = 'x-form-invalid-tip';
21203                 if(Roo.QuickTips){ // fix for floating editors interacting with DND
21204                     Roo.QuickTips.enable();
21205                 }
21206                 break;
21207             case 'title':
21208                 this.el.dom.title = msg;
21209                 break;
21210             case 'under':
21211                 if(!this.errorEl){
21212                     var elp = this.el.findParent('.x-form-element', 5, true);
21213                     this.errorEl = elp.createChild({cls:'x-form-invalid-msg'});
21214                     this.errorEl.setWidth(elp.getWidth(true)-20);
21215                 }
21216                 this.errorEl.update(msg);
21217                 Roo.form.Field.msgFx[this.msgFx].show(this.errorEl, this);
21218                 break;
21219             case 'side':
21220                 if(!this.errorIcon){
21221                     var elp = this.el.findParent('.x-form-element', 5, true);
21222                     this.errorIcon = elp.createChild({cls:'x-form-invalid-icon'});
21223                 }
21224                 this.alignErrorIcon();
21225                 this.errorIcon.dom.qtip = msg;
21226                 this.errorIcon.dom.qclass = 'x-form-invalid-tip';
21227                 this.errorIcon.show();
21228                 this.on('resize', this.alignErrorIcon, this);
21229                 break;
21230             default:
21231                 var t = Roo.getDom(this.msgTarget);
21232                 t.innerHTML = msg;
21233                 t.style.display = this.msgDisplay;
21234                 break;
21235         }
21236         this.fireEvent('invalid', this, msg);
21237     },
21238
21239     // private
21240     alignErrorIcon : function(){
21241         this.errorIcon.alignTo(this.el, 'tl-tr', [2, 0]);
21242     },
21243
21244     /**
21245      * Clear any invalid styles/messages for this field
21246      */
21247     clearInvalid : function(){
21248         if(!this.rendered || this.preventMark){ // not rendered
21249             return;
21250         }
21251         this.el.removeClass(this.invalidClass);
21252         switch(this.msgTarget){
21253             case 'qtip':
21254                 this.el.dom.qtip = '';
21255                 break;
21256             case 'title':
21257                 this.el.dom.title = '';
21258                 break;
21259             case 'under':
21260                 if(this.errorEl){
21261                     Roo.form.Field.msgFx[this.msgFx].hide(this.errorEl, this);
21262                 }
21263                 break;
21264             case 'side':
21265                 if(this.errorIcon){
21266                     this.errorIcon.dom.qtip = '';
21267                     this.errorIcon.hide();
21268                     this.un('resize', this.alignErrorIcon, this);
21269                 }
21270                 break;
21271             default:
21272                 var t = Roo.getDom(this.msgTarget);
21273                 t.innerHTML = '';
21274                 t.style.display = 'none';
21275                 break;
21276         }
21277         this.fireEvent('valid', this);
21278     },
21279
21280     /**
21281      * Returns the raw data value which may or may not be a valid, defined value.  To return a normalized value see {@link #getValue}.
21282      * @return {Mixed} value The field value
21283      */
21284     getRawValue : function(){
21285         var v = this.el.getValue();
21286         if(v === this.emptyText){
21287             v = '';
21288         }
21289         return v;
21290     },
21291
21292     /**
21293      * Returns the normalized data value (undefined or emptyText will be returned as '').  To return the raw value see {@link #getRawValue}.
21294      * @return {Mixed} value The field value
21295      */
21296     getValue : function(){
21297         var v = this.el.getValue();
21298         if(v === this.emptyText || v === undefined){
21299             v = '';
21300         }
21301         return v;
21302     },
21303
21304     /**
21305      * Sets the underlying DOM field's value directly, bypassing validation.  To set the value with validation see {@link #setValue}.
21306      * @param {Mixed} value The value to set
21307      */
21308     setRawValue : function(v){
21309         return this.el.dom.value = (v === null || v === undefined ? '' : v);
21310     },
21311
21312     /**
21313      * Sets a data value into the field and validates it.  To set the value directly without validation see {@link #setRawValue}.
21314      * @param {Mixed} value The value to set
21315      */
21316     setValue : function(v){
21317         this.value = v;
21318         if(this.rendered){
21319             this.el.dom.value = (v === null || v === undefined ? '' : v);
21320             this.validate();
21321         }
21322     },
21323
21324     adjustSize : function(w, h){
21325         var s = Roo.form.Field.superclass.adjustSize.call(this, w, h);
21326         s.width = this.adjustWidth(this.el.dom.tagName, s.width);
21327         return s;
21328     },
21329
21330     adjustWidth : function(tag, w){
21331         tag = tag.toLowerCase();
21332         if(typeof w == 'number' && Roo.isStrict && !Roo.isSafari){
21333             if(Roo.isIE && (tag == 'input' || tag == 'textarea')){
21334                 if(tag == 'input'){
21335                     return w + 2;
21336                 }
21337                 if(tag = 'textarea'){
21338                     return w-2;
21339                 }
21340             }else if(Roo.isOpera){
21341                 if(tag == 'input'){
21342                     return w + 2;
21343                 }
21344                 if(tag = 'textarea'){
21345                     return w-2;
21346                 }
21347             }
21348         }
21349         return w;
21350     }
21351 });
21352
21353
21354 // anything other than normal should be considered experimental
21355 Roo.form.Field.msgFx = {
21356     normal : {
21357         show: function(msgEl, f){
21358             msgEl.setDisplayed('block');
21359         },
21360
21361         hide : function(msgEl, f){
21362             msgEl.setDisplayed(false).update('');
21363         }
21364     },
21365
21366     slide : {
21367         show: function(msgEl, f){
21368             msgEl.slideIn('t', {stopFx:true});
21369         },
21370
21371         hide : function(msgEl, f){
21372             msgEl.slideOut('t', {stopFx:true,useDisplay:true});
21373         }
21374     },
21375
21376     slideRight : {
21377         show: function(msgEl, f){
21378             msgEl.fixDisplay();
21379             msgEl.alignTo(f.el, 'tl-tr');
21380             msgEl.slideIn('l', {stopFx:true});
21381         },
21382
21383         hide : function(msgEl, f){
21384             msgEl.slideOut('l', {stopFx:true,useDisplay:true});
21385         }
21386     }
21387 };/*
21388  * Based on:
21389  * Ext JS Library 1.1.1
21390  * Copyright(c) 2006-2007, Ext JS, LLC.
21391  *
21392  * Originally Released Under LGPL - original licence link has changed is not relivant.
21393  *
21394  * Fork - LGPL
21395  * <script type="text/javascript">
21396  */
21397  
21398
21399 /**
21400  * @class Roo.form.TextField
21401  * @extends Roo.form.Field
21402  * Basic text field.  Can be used as a direct replacement for traditional text inputs, or as the base
21403  * class for more sophisticated input controls (like {@link Roo.form.TextArea} and {@link Roo.form.ComboBox}).
21404  * @constructor
21405  * Creates a new TextField
21406  * @param {Object} config Configuration options
21407  */
21408 Roo.form.TextField = function(config){
21409     Roo.form.TextField.superclass.constructor.call(this, config);
21410     this.addEvents({
21411         /**
21412          * @event autosize
21413          * Fires when the autosize function is triggered.  The field may or may not have actually changed size
21414          * according to the default logic, but this event provides a hook for the developer to apply additional
21415          * logic at runtime to resize the field if needed.
21416              * @param {Roo.form.Field} this This text field
21417              * @param {Number} width The new field width
21418              */
21419         autosize : true
21420     });
21421 };
21422
21423 Roo.extend(Roo.form.TextField, Roo.form.Field,  {
21424     /**
21425      * @cfg {Boolean} grow True if this field should automatically grow and shrink to its content
21426      */
21427     grow : false,
21428     /**
21429      * @cfg {Number} growMin The minimum width to allow when grow = true (defaults to 30)
21430      */
21431     growMin : 30,
21432     /**
21433      * @cfg {Number} growMax The maximum width to allow when grow = true (defaults to 800)
21434      */
21435     growMax : 800,
21436     /**
21437      * @cfg {String} vtype A validation type name as defined in {@link Roo.form.VTypes} (defaults to null)
21438      */
21439     vtype : null,
21440     /**
21441      * @cfg {String} maskRe An input mask regular expression that will be used to filter keystrokes that don't match (defaults to null)
21442      */
21443     maskRe : null,
21444     /**
21445      * @cfg {Boolean} disableKeyFilter True to disable input keystroke filtering (defaults to false)
21446      */
21447     disableKeyFilter : false,
21448     /**
21449      * @cfg {Boolean} allowBlank False to validate that the value length > 0 (defaults to true)
21450      */
21451     allowBlank : true,
21452     /**
21453      * @cfg {Number} minLength Minimum input field length required (defaults to 0)
21454      */
21455     minLength : 0,
21456     /**
21457      * @cfg {Number} maxLength Maximum input field length allowed (defaults to Number.MAX_VALUE)
21458      */
21459     maxLength : Number.MAX_VALUE,
21460     /**
21461      * @cfg {String} minLengthText Error text to display if the minimum length validation fails (defaults to "The minimum length for this field is {minLength}")
21462      */
21463     minLengthText : "The minimum length for this field is {0}",
21464     /**
21465      * @cfg {String} maxLengthText Error text to display if the maximum length validation fails (defaults to "The maximum length for this field is {maxLength}")
21466      */
21467     maxLengthText : "The maximum length for this field is {0}",
21468     /**
21469      * @cfg {Boolean} selectOnFocus True to automatically select any existing field text when the field receives input focus (defaults to false)
21470      */
21471     selectOnFocus : false,
21472     /**
21473      * @cfg {String} blankText Error text to display if the allow blank validation fails (defaults to "This field is required")
21474      */
21475     blankText : "This field is required",
21476     /**
21477      * @cfg {Function} validator A custom validation function to be called during field validation (defaults to null).
21478      * If available, this function will be called only after the basic validators all return true, and will be passed the
21479      * current field value and expected to return boolean true if the value is valid or a string error message if invalid.
21480      */
21481     validator : null,
21482     /**
21483      * @cfg {RegExp} regex A JavaScript RegExp object to be tested against the field value during validation (defaults to null).
21484      * If available, this regex will be evaluated only after the basic validators all return true, and will be passed the
21485      * current field value.  If the test fails, the field will be marked invalid using {@link #regexText}.
21486      */
21487     regex : null,
21488     /**
21489      * @cfg {String} regexText The error text to display if {@link #regex} is used and the test fails during validation (defaults to "")
21490      */
21491     regexText : "",
21492     /**
21493      * @cfg {String} emptyText The default text to display in an empty field (defaults to null).
21494      */
21495     emptyText : null,
21496     /**
21497      * @cfg {String} emptyClass The CSS class to apply to an empty field to style the {@link #emptyText} (defaults to
21498      * 'x-form-empty-field').  This class is automatically added and removed as needed depending on the current field value.
21499      */
21500     emptyClass : 'x-form-empty-field',
21501
21502     // private
21503     initEvents : function(){
21504         Roo.form.TextField.superclass.initEvents.call(this);
21505         if(this.validationEvent == 'keyup'){
21506             this.validationTask = new Roo.util.DelayedTask(this.validate, this);
21507             this.el.on('keyup', this.filterValidation, this);
21508         }
21509         else if(this.validationEvent !== false){
21510             this.el.on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
21511         }
21512         if(this.selectOnFocus || this.emptyText){
21513             this.on("focus", this.preFocus, this);
21514             if(this.emptyText){
21515                 this.on('blur', this.postBlur, this);
21516                 this.applyEmptyText();
21517             }
21518         }
21519         if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
21520             this.el.on("keypress", this.filterKeys, this);
21521         }
21522         if(this.grow){
21523             this.el.on("keyup", this.onKeyUp,  this, {buffer:50});
21524             this.el.on("click", this.autoSize,  this);
21525         }
21526     },
21527
21528     processValue : function(value){
21529         if(this.stripCharsRe){
21530             var newValue = value.replace(this.stripCharsRe, '');
21531             if(newValue !== value){
21532                 this.setRawValue(newValue);
21533                 return newValue;
21534             }
21535         }
21536         return value;
21537     },
21538
21539     filterValidation : function(e){
21540         if(!e.isNavKeyPress()){
21541             this.validationTask.delay(this.validationDelay);
21542         }
21543     },
21544
21545     // private
21546     onKeyUp : function(e){
21547         if(!e.isNavKeyPress()){
21548             this.autoSize();
21549         }
21550     },
21551
21552     /**
21553      * Resets the current field value to the originally-loaded value and clears any validation messages.
21554      * Also adds emptyText and emptyClass if the original value was blank.
21555      */
21556     reset : function(){
21557         Roo.form.TextField.superclass.reset.call(this);
21558         this.applyEmptyText();
21559     },
21560
21561     applyEmptyText : function(){
21562         if(this.rendered && this.emptyText && this.getRawValue().length < 1){
21563             this.setRawValue(this.emptyText);
21564             this.el.addClass(this.emptyClass);
21565         }
21566     },
21567
21568     // private
21569     preFocus : function(){
21570         if(this.emptyText){
21571             if(this.el.dom.value == this.emptyText){
21572                 this.setRawValue('');
21573             }
21574             this.el.removeClass(this.emptyClass);
21575         }
21576         if(this.selectOnFocus){
21577             this.el.dom.select();
21578         }
21579     },
21580
21581     // private
21582     postBlur : function(){
21583         this.applyEmptyText();
21584     },
21585
21586     // private
21587     filterKeys : function(e){
21588         var k = e.getKey();
21589         if(!Roo.isIE && (e.isNavKeyPress() || k == e.BACKSPACE || (k == e.DELETE && e.button == -1))){
21590             return;
21591         }
21592         var c = e.getCharCode(), cc = String.fromCharCode(c);
21593         if(Roo.isIE && (e.isSpecialKey() || !cc)){
21594             return;
21595         }
21596         if(!this.maskRe.test(cc)){
21597             e.stopEvent();
21598         }
21599     },
21600
21601     setValue : function(v){
21602         if(this.emptyText && this.el && v !== undefined && v !== null && v !== ''){
21603             this.el.removeClass(this.emptyClass);
21604         }
21605         Roo.form.TextField.superclass.setValue.apply(this, arguments);
21606         this.applyEmptyText();
21607         this.autoSize();
21608     },
21609
21610     /**
21611      * Validates a value according to the field's validation rules and marks the field as invalid
21612      * if the validation fails
21613      * @param {Mixed} value The value to validate
21614      * @return {Boolean} True if the value is valid, else false
21615      */
21616     validateValue : function(value){
21617         if(value.length < 1 || value === this.emptyText){ // if it's blank
21618              if(this.allowBlank){
21619                 this.clearInvalid();
21620                 return true;
21621              }else{
21622                 this.markInvalid(this.blankText);
21623                 return false;
21624              }
21625         }
21626         if(value.length < this.minLength){
21627             this.markInvalid(String.format(this.minLengthText, this.minLength));
21628             return false;
21629         }
21630         if(value.length > this.maxLength){
21631             this.markInvalid(String.format(this.maxLengthText, this.maxLength));
21632             return false;
21633         }
21634         if(this.vtype){
21635             var vt = Roo.form.VTypes;
21636             if(!vt[this.vtype](value, this)){
21637                 this.markInvalid(this.vtypeText || vt[this.vtype +'Text']);
21638                 return false;
21639             }
21640         }
21641         if(typeof this.validator == "function"){
21642             var msg = this.validator(value);
21643             if(msg !== true){
21644                 this.markInvalid(msg);
21645                 return false;
21646             }
21647         }
21648         if(this.regex && !this.regex.test(value)){
21649             this.markInvalid(this.regexText);
21650             return false;
21651         }
21652         return true;
21653     },
21654
21655     /**
21656      * Selects text in this field
21657      * @param {Number} start (optional) The index where the selection should start (defaults to 0)
21658      * @param {Number} end (optional) The index where the selection should end (defaults to the text length)
21659      */
21660     selectText : function(start, end){
21661         var v = this.getRawValue();
21662         if(v.length > 0){
21663             start = start === undefined ? 0 : start;
21664             end = end === undefined ? v.length : end;
21665             var d = this.el.dom;
21666             if(d.setSelectionRange){
21667                 d.setSelectionRange(start, end);
21668             }else if(d.createTextRange){
21669                 var range = d.createTextRange();
21670                 range.moveStart("character", start);
21671                 range.moveEnd("character", v.length-end);
21672                 range.select();
21673             }
21674         }
21675     },
21676
21677     /**
21678      * Automatically grows the field to accomodate the width of the text up to the maximum field width allowed.
21679      * This only takes effect if grow = true, and fires the autosize event.
21680      */
21681     autoSize : function(){
21682         if(!this.grow || !this.rendered){
21683             return;
21684         }
21685         if(!this.metrics){
21686             this.metrics = Roo.util.TextMetrics.createInstance(this.el);
21687         }
21688         var el = this.el;
21689         var v = el.dom.value;
21690         var d = document.createElement('div');
21691         d.appendChild(document.createTextNode(v));
21692         v = d.innerHTML;
21693         d = null;
21694         v += "&#160;";
21695         var w = Math.min(this.growMax, Math.max(this.metrics.getWidth(v) + /* add extra padding */ 10, this.growMin));
21696         this.el.setWidth(w);
21697         this.fireEvent("autosize", this, w);
21698     }
21699 });/*
21700  * Based on:
21701  * Ext JS Library 1.1.1
21702  * Copyright(c) 2006-2007, Ext JS, LLC.
21703  *
21704  * Originally Released Under LGPL - original licence link has changed is not relivant.
21705  *
21706  * Fork - LGPL
21707  * <script type="text/javascript">
21708  */
21709  
21710 /**
21711  * @class Roo.form.Hidden
21712  * @extends Roo.form.TextField
21713  * Simple Hidden element used on forms 
21714  * 
21715  * usage: form.add(new Roo.form.HiddenField({ 'name' : 'test1' }));
21716  * 
21717  * @constructor
21718  * Creates a new Hidden form element.
21719  * @param {Object} config Configuration options
21720  */
21721
21722
21723
21724 // easy hidden field...
21725 Roo.form.Hidden = function(config){
21726     Roo.form.Hidden.superclass.constructor.call(this, config);
21727 };
21728   
21729 Roo.extend(Roo.form.Hidden, Roo.form.TextField, {
21730     fieldLabel:      '',
21731     inputType:      'hidden',
21732     width:          50,
21733     allowBlank:     true,
21734     labelSeparator: '',
21735     hidden:         true,
21736     itemCls :       'x-form-item-display-none'
21737
21738
21739 });
21740
21741
21742 /*
21743  * Based on:
21744  * Ext JS Library 1.1.1
21745  * Copyright(c) 2006-2007, Ext JS, LLC.
21746  *
21747  * Originally Released Under LGPL - original licence link has changed is not relivant.
21748  *
21749  * Fork - LGPL
21750  * <script type="text/javascript">
21751  */
21752  
21753 /**
21754  * @class Roo.form.TriggerField
21755  * @extends Roo.form.TextField
21756  * Provides a convenient wrapper for TextFields that adds a clickable trigger button (looks like a combobox by default).
21757  * The trigger has no default action, so you must assign a function to implement the trigger click handler by
21758  * overriding {@link #onTriggerClick}. You can create a TriggerField directly, as it renders exactly like a combobox
21759  * for which you can provide a custom implementation.  For example:
21760  * <pre><code>
21761 var trigger = new Roo.form.TriggerField();
21762 trigger.onTriggerClick = myTriggerFn;
21763 trigger.applyTo('my-field');
21764 </code></pre>
21765  *
21766  * However, in general you will most likely want to use TriggerField as the base class for a reusable component.
21767  * {@link Roo.form.DateField} and {@link Roo.form.ComboBox} are perfect examples of this.
21768  * @cfg {String} triggerClass An additional CSS class used to style the trigger button.  The trigger will always get the
21769  * class 'x-form-trigger' by default and triggerClass will be <b>appended</b> if specified.
21770  * @constructor
21771  * Create a new TriggerField.
21772  * @param {Object} config Configuration options (valid {@Roo.form.TextField} config options will also be applied
21773  * to the base TextField)
21774  */
21775 Roo.form.TriggerField = function(config){
21776     this.mimicing = false;
21777     Roo.form.TriggerField.superclass.constructor.call(this, config);
21778 };
21779
21780 Roo.extend(Roo.form.TriggerField, Roo.form.TextField,  {
21781     /**
21782      * @cfg {String} triggerClass A CSS class to apply to the trigger
21783      */
21784     /**
21785      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
21786      * {tag: "input", type: "text", size: "16", autocomplete: "off"})
21787      */
21788     defaultAutoCreate : {tag: "input", type: "text", size: "16", autocomplete: "off"},
21789     /**
21790      * @cfg {Boolean} hideTrigger True to hide the trigger element and display only the base text field (defaults to false)
21791      */
21792     hideTrigger:false,
21793
21794     /** @cfg {Boolean} grow @hide */
21795     /** @cfg {Number} growMin @hide */
21796     /** @cfg {Number} growMax @hide */
21797
21798     /**
21799      * @hide 
21800      * @method
21801      */
21802     autoSize: Roo.emptyFn,
21803     // private
21804     monitorTab : true,
21805     // private
21806     deferHeight : true,
21807
21808     
21809     actionMode : 'wrap',
21810     // private
21811     onResize : function(w, h){
21812         Roo.form.TriggerField.superclass.onResize.apply(this, arguments);
21813         if(typeof w == 'number'){
21814             var x = w - this.trigger.getWidth();
21815             this.el.setWidth(this.adjustWidth('input', x));
21816             this.trigger.setStyle('left', x+'px');
21817         }
21818     },
21819
21820     // private
21821     adjustSize : Roo.BoxComponent.prototype.adjustSize,
21822
21823     // private
21824     getResizeEl : function(){
21825         return this.wrap;
21826     },
21827
21828     // private
21829     getPositionEl : function(){
21830         return this.wrap;
21831     },
21832
21833     // private
21834     alignErrorIcon : function(){
21835         this.errorIcon.alignTo(this.wrap, 'tl-tr', [2, 0]);
21836     },
21837
21838     // private
21839     onRender : function(ct, position){
21840         Roo.form.TriggerField.superclass.onRender.call(this, ct, position);
21841         this.wrap = this.el.wrap({cls: "x-form-field-wrap"});
21842         this.trigger = this.wrap.createChild(this.triggerConfig ||
21843                 {tag: "img", src: Roo.BLANK_IMAGE_URL, cls: "x-form-trigger " + this.triggerClass});
21844         if(this.hideTrigger){
21845             this.trigger.setDisplayed(false);
21846         }
21847         this.initTrigger();
21848         if(!this.width){
21849             this.wrap.setWidth(this.el.getWidth()+this.trigger.getWidth());
21850         }
21851     },
21852
21853     // private
21854     initTrigger : function(){
21855         this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
21856         this.trigger.addClassOnOver('x-form-trigger-over');
21857         this.trigger.addClassOnClick('x-form-trigger-click');
21858     },
21859
21860     // private
21861     onDestroy : function(){
21862         if(this.trigger){
21863             this.trigger.removeAllListeners();
21864             this.trigger.remove();
21865         }
21866         if(this.wrap){
21867             this.wrap.remove();
21868         }
21869         Roo.form.TriggerField.superclass.onDestroy.call(this);
21870     },
21871
21872     // private
21873     onFocus : function(){
21874         Roo.form.TriggerField.superclass.onFocus.call(this);
21875         if(!this.mimicing){
21876             this.wrap.addClass('x-trigger-wrap-focus');
21877             this.mimicing = true;
21878             Roo.get(Roo.isIE ? document.body : document).on("mousedown", this.mimicBlur, this);
21879             if(this.monitorTab){
21880                 this.el.on("keydown", this.checkTab, this);
21881             }
21882         }
21883     },
21884
21885     // private
21886     checkTab : function(e){
21887         if(e.getKey() == e.TAB){
21888             this.triggerBlur();
21889         }
21890     },
21891
21892     // private
21893     onBlur : function(){
21894         // do nothing
21895     },
21896
21897     // private
21898     mimicBlur : function(e, t){
21899         if(!this.wrap.contains(t) && this.validateBlur()){
21900             this.triggerBlur();
21901         }
21902     },
21903
21904     // private
21905     triggerBlur : function(){
21906         this.mimicing = false;
21907         Roo.get(Roo.isIE ? document.body : document).un("mousedown", this.mimicBlur);
21908         if(this.monitorTab){
21909             this.el.un("keydown", this.checkTab, this);
21910         }
21911         this.wrap.removeClass('x-trigger-wrap-focus');
21912         Roo.form.TriggerField.superclass.onBlur.call(this);
21913     },
21914
21915     // private
21916     // This should be overriden by any subclass that needs to check whether or not the field can be blurred.
21917     validateBlur : function(e, t){
21918         return true;
21919     },
21920
21921     // private
21922     onDisable : function(){
21923         Roo.form.TriggerField.superclass.onDisable.call(this);
21924         if(this.wrap){
21925             this.wrap.addClass('x-item-disabled');
21926         }
21927     },
21928
21929     // private
21930     onEnable : function(){
21931         Roo.form.TriggerField.superclass.onEnable.call(this);
21932         if(this.wrap){
21933             this.wrap.removeClass('x-item-disabled');
21934         }
21935     },
21936
21937     // private
21938     onShow : function(){
21939         var ae = this.getActionEl();
21940         
21941         if(ae){
21942             ae.dom.style.display = '';
21943             ae.dom.style.visibility = 'visible';
21944         }
21945     },
21946
21947     // private
21948     
21949     onHide : function(){
21950         var ae = this.getActionEl();
21951         ae.dom.style.display = 'none';
21952     },
21953
21954     /**
21955      * The function that should handle the trigger's click event.  This method does nothing by default until overridden
21956      * by an implementing function.
21957      * @method
21958      * @param {EventObject} e
21959      */
21960     onTriggerClick : Roo.emptyFn
21961 });
21962
21963 // TwinTriggerField is not a public class to be used directly.  It is meant as an abstract base class
21964 // to be extended by an implementing class.  For an example of implementing this class, see the custom
21965 // SearchField implementation here: http://extjs.com/deploy/ext/examples/form/custom.html
21966 Roo.form.TwinTriggerField = Roo.extend(Roo.form.TriggerField, {
21967     initComponent : function(){
21968         Roo.form.TwinTriggerField.superclass.initComponent.call(this);
21969
21970         this.triggerConfig = {
21971             tag:'span', cls:'x-form-twin-triggers', cn:[
21972             {tag: "img", src: Roo.BLANK_IMAGE_URL, cls: "x-form-trigger " + this.trigger1Class},
21973             {tag: "img", src: Roo.BLANK_IMAGE_URL, cls: "x-form-trigger " + this.trigger2Class}
21974         ]};
21975     },
21976
21977     getTrigger : function(index){
21978         return this.triggers[index];
21979     },
21980
21981     initTrigger : function(){
21982         var ts = this.trigger.select('.x-form-trigger', true);
21983         this.wrap.setStyle('overflow', 'hidden');
21984         var triggerField = this;
21985         ts.each(function(t, all, index){
21986             t.hide = function(){
21987                 var w = triggerField.wrap.getWidth();
21988                 this.dom.style.display = 'none';
21989                 triggerField.el.setWidth(w-triggerField.trigger.getWidth());
21990             };
21991             t.show = function(){
21992                 var w = triggerField.wrap.getWidth();
21993                 this.dom.style.display = '';
21994                 triggerField.el.setWidth(w-triggerField.trigger.getWidth());
21995             };
21996             var triggerIndex = 'Trigger'+(index+1);
21997
21998             if(this['hide'+triggerIndex]){
21999                 t.dom.style.display = 'none';
22000             }
22001             t.on("click", this['on'+triggerIndex+'Click'], this, {preventDefault:true});
22002             t.addClassOnOver('x-form-trigger-over');
22003             t.addClassOnClick('x-form-trigger-click');
22004         }, this);
22005         this.triggers = ts.elements;
22006     },
22007
22008     onTrigger1Click : Roo.emptyFn,
22009     onTrigger2Click : Roo.emptyFn
22010 });/*
22011  * Based on:
22012  * Ext JS Library 1.1.1
22013  * Copyright(c) 2006-2007, Ext JS, LLC.
22014  *
22015  * Originally Released Under LGPL - original licence link has changed is not relivant.
22016  *
22017  * Fork - LGPL
22018  * <script type="text/javascript">
22019  */
22020  
22021 /**
22022  * @class Roo.form.TextArea
22023  * @extends Roo.form.TextField
22024  * Multiline text field.  Can be used as a direct replacement for traditional textarea fields, plus adds
22025  * support for auto-sizing.
22026  * @constructor
22027  * Creates a new TextArea
22028  * @param {Object} config Configuration options
22029  */
22030 Roo.form.TextArea = function(config){
22031     Roo.form.TextArea.superclass.constructor.call(this, config);
22032     // these are provided exchanges for backwards compat
22033     // minHeight/maxHeight were replaced by growMin/growMax to be
22034     // compatible with TextField growing config values
22035     if(this.minHeight !== undefined){
22036         this.growMin = this.minHeight;
22037     }
22038     if(this.maxHeight !== undefined){
22039         this.growMax = this.maxHeight;
22040     }
22041 };
22042
22043 Roo.extend(Roo.form.TextArea, Roo.form.TextField,  {
22044     /**
22045      * @cfg {Number} growMin The minimum height to allow when grow = true (defaults to 60)
22046      */
22047     growMin : 60,
22048     /**
22049      * @cfg {Number} growMax The maximum height to allow when grow = true (defaults to 1000)
22050      */
22051     growMax: 1000,
22052     /**
22053      * @cfg {Boolean} preventScrollbars True to prevent scrollbars from appearing regardless of how much text is
22054      * in the field (equivalent to setting overflow: hidden, defaults to false)
22055      */
22056     preventScrollbars: false,
22057     /**
22058      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
22059      * {tag: "textarea", style: "width:300px;height:60px;", autocomplete: "off"})
22060      */
22061
22062     // private
22063     onRender : function(ct, position){
22064         if(!this.el){
22065             this.defaultAutoCreate = {
22066                 tag: "textarea",
22067                 style:"width:300px;height:60px;",
22068                 autocomplete: "off"
22069             };
22070         }
22071         Roo.form.TextArea.superclass.onRender.call(this, ct, position);
22072         if(this.grow){
22073             this.textSizeEl = Roo.DomHelper.append(document.body, {
22074                 tag: "pre", cls: "x-form-grow-sizer"
22075             });
22076             if(this.preventScrollbars){
22077                 this.el.setStyle("overflow", "hidden");
22078             }
22079             this.el.setHeight(this.growMin);
22080         }
22081     },
22082
22083     onDestroy : function(){
22084         if(this.textSizeEl){
22085             this.textSizeEl.parentNode.removeChild(this.textSizeEl);
22086         }
22087         Roo.form.TextArea.superclass.onDestroy.call(this);
22088     },
22089
22090     // private
22091     onKeyUp : function(e){
22092         if(!e.isNavKeyPress() || e.getKey() == e.ENTER){
22093             this.autoSize();
22094         }
22095     },
22096
22097     /**
22098      * Automatically grows the field to accomodate the height of the text up to the maximum field height allowed.
22099      * This only takes effect if grow = true, and fires the autosize event if the height changes.
22100      */
22101     autoSize : function(){
22102         if(!this.grow || !this.textSizeEl){
22103             return;
22104         }
22105         var el = this.el;
22106         var v = el.dom.value;
22107         var ts = this.textSizeEl;
22108
22109         ts.innerHTML = '';
22110         ts.appendChild(document.createTextNode(v));
22111         v = ts.innerHTML;
22112
22113         Roo.fly(ts).setWidth(this.el.getWidth());
22114         if(v.length < 1){
22115             v = "&#160;&#160;";
22116         }else{
22117             if(Roo.isIE){
22118                 v = v.replace(/\n/g, '<p>&#160;</p>');
22119             }
22120             v += "&#160;\n&#160;";
22121         }
22122         ts.innerHTML = v;
22123         var h = Math.min(this.growMax, Math.max(ts.offsetHeight, this.growMin));
22124         if(h != this.lastHeight){
22125             this.lastHeight = h;
22126             this.el.setHeight(h);
22127             this.fireEvent("autosize", this, h);
22128         }
22129     }
22130 });/*
22131  * Based on:
22132  * Ext JS Library 1.1.1
22133  * Copyright(c) 2006-2007, Ext JS, LLC.
22134  *
22135  * Originally Released Under LGPL - original licence link has changed is not relivant.
22136  *
22137  * Fork - LGPL
22138  * <script type="text/javascript">
22139  */
22140  
22141
22142 /**
22143  * @class Roo.form.NumberField
22144  * @extends Roo.form.TextField
22145  * Numeric text field that provides automatic keystroke filtering and numeric validation.
22146  * @constructor
22147  * Creates a new NumberField
22148  * @param {Object} config Configuration options
22149  */
22150 Roo.form.NumberField = function(config){
22151     Roo.form.NumberField.superclass.constructor.call(this, config);
22152 };
22153
22154 Roo.extend(Roo.form.NumberField, Roo.form.TextField,  {
22155     /**
22156      * @cfg {String} fieldClass The default CSS class for the field (defaults to "x-form-field x-form-num-field")
22157      */
22158     fieldClass: "x-form-field x-form-num-field",
22159     /**
22160      * @cfg {Boolean} allowDecimals False to disallow decimal values (defaults to true)
22161      */
22162     allowDecimals : true,
22163     /**
22164      * @cfg {String} decimalSeparator Character(s) to allow as the decimal separator (defaults to '.')
22165      */
22166     decimalSeparator : ".",
22167     /**
22168      * @cfg {Number} decimalPrecision The maximum precision to display after the decimal separator (defaults to 2)
22169      */
22170     decimalPrecision : 2,
22171     /**
22172      * @cfg {Boolean} allowNegative False to prevent entering a negative sign (defaults to true)
22173      */
22174     allowNegative : true,
22175     /**
22176      * @cfg {Number} minValue The minimum allowed value (defaults to Number.NEGATIVE_INFINITY)
22177      */
22178     minValue : Number.NEGATIVE_INFINITY,
22179     /**
22180      * @cfg {Number} maxValue The maximum allowed value (defaults to Number.MAX_VALUE)
22181      */
22182     maxValue : Number.MAX_VALUE,
22183     /**
22184      * @cfg {String} minText Error text to display if the minimum value validation fails (defaults to "The minimum value for this field is {minValue}")
22185      */
22186     minText : "The minimum value for this field is {0}",
22187     /**
22188      * @cfg {String} maxText Error text to display if the maximum value validation fails (defaults to "The maximum value for this field is {maxValue}")
22189      */
22190     maxText : "The maximum value for this field is {0}",
22191     /**
22192      * @cfg {String} nanText Error text to display if the value is not a valid number.  For example, this can happen
22193      * if a valid character like '.' or '-' is left in the field with no number (defaults to "{value} is not a valid number")
22194      */
22195     nanText : "{0} is not a valid number",
22196
22197     // private
22198     initEvents : function(){
22199         Roo.form.NumberField.superclass.initEvents.call(this);
22200         var allowed = "0123456789";
22201         if(this.allowDecimals){
22202             allowed += this.decimalSeparator;
22203         }
22204         if(this.allowNegative){
22205             allowed += "-";
22206         }
22207         this.stripCharsRe = new RegExp('[^'+allowed+']', 'gi');
22208         var keyPress = function(e){
22209             var k = e.getKey();
22210             if(!Roo.isIE && (e.isSpecialKey() || k == e.BACKSPACE || k == e.DELETE)){
22211                 return;
22212             }
22213             var c = e.getCharCode();
22214             if(allowed.indexOf(String.fromCharCode(c)) === -1){
22215                 e.stopEvent();
22216             }
22217         };
22218         this.el.on("keypress", keyPress, this);
22219     },
22220
22221     // private
22222     validateValue : function(value){
22223         if(!Roo.form.NumberField.superclass.validateValue.call(this, value)){
22224             return false;
22225         }
22226         if(value.length < 1){ // if it's blank and textfield didn't flag it then it's valid
22227              return true;
22228         }
22229         var num = this.parseValue(value);
22230         if(isNaN(num)){
22231             this.markInvalid(String.format(this.nanText, value));
22232             return false;
22233         }
22234         if(num < this.minValue){
22235             this.markInvalid(String.format(this.minText, this.minValue));
22236             return false;
22237         }
22238         if(num > this.maxValue){
22239             this.markInvalid(String.format(this.maxText, this.maxValue));
22240             return false;
22241         }
22242         return true;
22243     },
22244
22245     getValue : function(){
22246         return this.fixPrecision(this.parseValue(Roo.form.NumberField.superclass.getValue.call(this)));
22247     },
22248
22249     // private
22250     parseValue : function(value){
22251         value = parseFloat(String(value).replace(this.decimalSeparator, "."));
22252         return isNaN(value) ? '' : value;
22253     },
22254
22255     // private
22256     fixPrecision : function(value){
22257         var nan = isNaN(value);
22258         if(!this.allowDecimals || this.decimalPrecision == -1 || nan || !value){
22259             return nan ? '' : value;
22260         }
22261         return parseFloat(value).toFixed(this.decimalPrecision);
22262     },
22263
22264     setValue : function(v){
22265         Roo.form.NumberField.superclass.setValue.call(this, String(v).replace(".", this.decimalSeparator));
22266     },
22267
22268     // private
22269     decimalPrecisionFcn : function(v){
22270         return Math.floor(v);
22271     },
22272
22273     beforeBlur : function(){
22274         var v = this.parseValue(this.getRawValue());
22275         if(v){
22276             this.setValue(this.fixPrecision(v));
22277         }
22278     }
22279 });/*
22280  * Based on:
22281  * Ext JS Library 1.1.1
22282  * Copyright(c) 2006-2007, Ext JS, LLC.
22283  *
22284  * Originally Released Under LGPL - original licence link has changed is not relivant.
22285  *
22286  * Fork - LGPL
22287  * <script type="text/javascript">
22288  */
22289  
22290 /**
22291  * @class Roo.form.DateField
22292  * @extends Roo.form.TriggerField
22293  * Provides a date input field with a {@link Roo.DatePicker} dropdown and automatic date validation.
22294 * @constructor
22295 * Create a new DateField
22296 * @param {Object} config
22297  */
22298 Roo.form.DateField = function(config){
22299     Roo.form.DateField.superclass.constructor.call(this, config);
22300     
22301       this.addEvents({
22302          
22303         /**
22304          * @event select
22305          * Fires when a date is selected
22306              * @param {Roo.form.DateField} combo This combo box
22307              * @param {Date} date The date selected
22308              */
22309         'select' : true
22310          
22311     });
22312     
22313     
22314     if(typeof this.minValue == "string") this.minValue = this.parseDate(this.minValue);
22315     if(typeof this.maxValue == "string") this.maxValue = this.parseDate(this.maxValue);
22316     this.ddMatch = null;
22317     if(this.disabledDates){
22318         var dd = this.disabledDates;
22319         var re = "(?:";
22320         for(var i = 0; i < dd.length; i++){
22321             re += dd[i];
22322             if(i != dd.length-1) re += "|";
22323         }
22324         this.ddMatch = new RegExp(re + ")");
22325     }
22326 };
22327
22328 Roo.extend(Roo.form.DateField, Roo.form.TriggerField,  {
22329     /**
22330      * @cfg {String} format
22331      * The default date format string which can be overriden for localization support.  The format must be
22332      * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
22333      */
22334     format : "m/d/y",
22335     /**
22336      * @cfg {String} altFormats
22337      * Multiple date formats separated by "|" to try when parsing a user input value and it doesn't match the defined
22338      * format (defaults to 'm/d/Y|m-d-y|m-d-Y|m/d|m-d|d').
22339      */
22340     altFormats : "m/d/Y|m-d-y|m-d-Y|m/d|m-d|md|mdy|mdY|d",
22341     /**
22342      * @cfg {Array} disabledDays
22343      * An array of days to disable, 0 based. For example, [0, 6] disables Sunday and Saturday (defaults to null).
22344      */
22345     disabledDays : null,
22346     /**
22347      * @cfg {String} disabledDaysText
22348      * The tooltip to display when the date falls on a disabled day (defaults to 'Disabled')
22349      */
22350     disabledDaysText : "Disabled",
22351     /**
22352      * @cfg {Array} disabledDates
22353      * An array of "dates" to disable, as strings. These strings will be used to build a dynamic regular
22354      * expression so they are very powerful. Some examples:
22355      * <ul>
22356      * <li>["03/08/2003", "09/16/2003"] would disable those exact dates</li>
22357      * <li>["03/08", "09/16"] would disable those days for every year</li>
22358      * <li>["^03/08"] would only match the beginning (useful if you are using short years)</li>
22359      * <li>["03/../2006"] would disable every day in March 2006</li>
22360      * <li>["^03"] would disable every day in every March</li>
22361      * </ul>
22362      * In order to support regular expressions, if you are using a date format that has "." in it, you will have to
22363      * escape the dot when restricting dates. For example: ["03\\.08\\.03"].
22364      */
22365     disabledDates : null,
22366     /**
22367      * @cfg {String} disabledDatesText
22368      * The tooltip text to display when the date falls on a disabled date (defaults to 'Disabled')
22369      */
22370     disabledDatesText : "Disabled",
22371     /**
22372      * @cfg {Date/String} minValue
22373      * The minimum allowed date. Can be either a Javascript date object or a string date in a
22374      * valid format (defaults to null).
22375      */
22376     minValue : null,
22377     /**
22378      * @cfg {Date/String} maxValue
22379      * The maximum allowed date. Can be either a Javascript date object or a string date in a
22380      * valid format (defaults to null).
22381      */
22382     maxValue : null,
22383     /**
22384      * @cfg {String} minText
22385      * The error text to display when the date in the cell is before minValue (defaults to
22386      * 'The date in this field must be after {minValue}').
22387      */
22388     minText : "The date in this field must be equal to or after {0}",
22389     /**
22390      * @cfg {String} maxText
22391      * The error text to display when the date in the cell is after maxValue (defaults to
22392      * 'The date in this field must be before {maxValue}').
22393      */
22394     maxText : "The date in this field must be equal to or before {0}",
22395     /**
22396      * @cfg {String} invalidText
22397      * The error text to display when the date in the field is invalid (defaults to
22398      * '{value} is not a valid date - it must be in the format {format}').
22399      */
22400     invalidText : "{0} is not a valid date - it must be in the format {1}",
22401     /**
22402      * @cfg {String} triggerClass
22403      * An additional CSS class used to style the trigger button.  The trigger will always get the
22404      * class 'x-form-trigger' and triggerClass will be <b>appended</b> if specified (defaults to 'x-form-date-trigger'
22405      * which displays a calendar icon).
22406      */
22407     triggerClass : 'x-form-date-trigger',
22408     
22409
22410     /**
22411      * @cfg {bool} useIso
22412      * if enabled, then the date field will use a hidden field to store the 
22413      * real value as iso formated date. default (false)
22414      */ 
22415     useIso : false,
22416     /**
22417      * @cfg {String/Object} autoCreate
22418      * A DomHelper element spec, or true for a default element spec (defaults to
22419      * {tag: "input", type: "text", size: "10", autocomplete: "off"})
22420      */ 
22421     // private
22422     defaultAutoCreate : {tag: "input", type: "text", size: "10", autocomplete: "off"},
22423     
22424     // private
22425     hiddenField: false,
22426     
22427     onRender : function(ct, position)
22428     {
22429         Roo.form.DateField.superclass.onRender.call(this, ct, position);
22430         if (this.useIso) {
22431             this.el.dom.removeAttribute('name'); 
22432             this.hiddenField = this.el.insertSibling({ tag:'input', type:'hidden', name: this.name },
22433                     'before', true);
22434             this.hiddenField.value = this.formatDate(this.value, 'Y-m-d');
22435             // prevent input submission
22436             this.hiddenName = this.name;
22437         }
22438             
22439             
22440     },
22441     
22442     // private
22443     validateValue : function(value)
22444     {
22445         value = this.formatDate(value);
22446         if(!Roo.form.DateField.superclass.validateValue.call(this, value)){
22447             return false;
22448         }
22449         if(value.length < 1){ // if it's blank and textfield didn't flag it then it's valid
22450              return true;
22451         }
22452         var svalue = value;
22453         value = this.parseDate(value);
22454         if(!value){
22455             this.markInvalid(String.format(this.invalidText, svalue, this.format));
22456             return false;
22457         }
22458         var time = value.getTime();
22459         if(this.minValue && time < this.minValue.getTime()){
22460             this.markInvalid(String.format(this.minText, this.formatDate(this.minValue)));
22461             return false;
22462         }
22463         if(this.maxValue && time > this.maxValue.getTime()){
22464             this.markInvalid(String.format(this.maxText, this.formatDate(this.maxValue)));
22465             return false;
22466         }
22467         if(this.disabledDays){
22468             var day = value.getDay();
22469             for(var i = 0; i < this.disabledDays.length; i++) {
22470                 if(day === this.disabledDays[i]){
22471                     this.markInvalid(this.disabledDaysText);
22472                     return false;
22473                 }
22474             }
22475         }
22476         var fvalue = this.formatDate(value);
22477         if(this.ddMatch && this.ddMatch.test(fvalue)){
22478             this.markInvalid(String.format(this.disabledDatesText, fvalue));
22479             return false;
22480         }
22481         return true;
22482     },
22483
22484     // private
22485     // Provides logic to override the default TriggerField.validateBlur which just returns true
22486     validateBlur : function(){
22487         return !this.menu || !this.menu.isVisible();
22488     },
22489
22490     /**
22491      * Returns the current date value of the date field.
22492      * @return {Date} The date value
22493      */
22494     getValue : function(){
22495         
22496         return  this.hiddenField ? this.hiddenField.value : this.parseDate(Roo.form.DateField.superclass.getValue.call(this)) || "";
22497     },
22498
22499     /**
22500      * Sets the value of the date field.  You can pass a date object or any string that can be parsed into a valid
22501      * date, using DateField.format as the date format, according to the same rules as {@link Date#parseDate}
22502      * (the default format used is "m/d/y").
22503      * <br />Usage:
22504      * <pre><code>
22505 //All of these calls set the same date value (May 4, 2006)
22506
22507 //Pass a date object:
22508 var dt = new Date('5/4/06');
22509 dateField.setValue(dt);
22510
22511 //Pass a date string (default format):
22512 dateField.setValue('5/4/06');
22513
22514 //Pass a date string (custom format):
22515 dateField.format = 'Y-m-d';
22516 dateField.setValue('2006-5-4');
22517 </code></pre>
22518      * @param {String/Date} date The date or valid date string
22519      */
22520     setValue : function(date){
22521         if (this.hiddenField) {
22522             this.hiddenField.value = this.formatDate(this.parseDate(date), 'Y-m-d');
22523         }
22524         Roo.form.DateField.superclass.setValue.call(this, this.formatDate(this.parseDate(date)));
22525     },
22526
22527     // private
22528     parseDate : function(value){
22529         if(!value || value instanceof Date){
22530             return value;
22531         }
22532         var v = Date.parseDate(value, this.format);
22533         if(!v && this.altFormats){
22534             if(!this.altFormatsArray){
22535                 this.altFormatsArray = this.altFormats.split("|");
22536             }
22537             for(var i = 0, len = this.altFormatsArray.length; i < len && !v; i++){
22538                 v = Date.parseDate(value, this.altFormatsArray[i]);
22539             }
22540         }
22541         return v;
22542     },
22543
22544     // private
22545     formatDate : function(date, fmt){
22546         return (!date || !(date instanceof Date)) ?
22547                date : date.dateFormat(fmt || this.format);
22548     },
22549
22550     // private
22551     menuListeners : {
22552         select: function(m, d){
22553             this.setValue(d);
22554             this.fireEvent('select', this, d);
22555         },
22556         show : function(){ // retain focus styling
22557             this.onFocus();
22558         },
22559         hide : function(){
22560             this.focus.defer(10, this);
22561             var ml = this.menuListeners;
22562             this.menu.un("select", ml.select,  this);
22563             this.menu.un("show", ml.show,  this);
22564             this.menu.un("hide", ml.hide,  this);
22565         }
22566     },
22567
22568     // private
22569     // Implements the default empty TriggerField.onTriggerClick function to display the DatePicker
22570     onTriggerClick : function(){
22571         if(this.disabled){
22572             return;
22573         }
22574         if(this.menu == null){
22575             this.menu = new Roo.menu.DateMenu();
22576         }
22577         Roo.apply(this.menu.picker,  {
22578             showClear: this.allowBlank,
22579             minDate : this.minValue,
22580             maxDate : this.maxValue,
22581             disabledDatesRE : this.ddMatch,
22582             disabledDatesText : this.disabledDatesText,
22583             disabledDays : this.disabledDays,
22584             disabledDaysText : this.disabledDaysText,
22585             format : this.format,
22586             minText : String.format(this.minText, this.formatDate(this.minValue)),
22587             maxText : String.format(this.maxText, this.formatDate(this.maxValue))
22588         });
22589         this.menu.on(Roo.apply({}, this.menuListeners, {
22590             scope:this
22591         }));
22592         this.menu.picker.setValue(this.getValue() || new Date());
22593         this.menu.show(this.el, "tl-bl?");
22594     },
22595
22596     beforeBlur : function(){
22597         var v = this.parseDate(this.getRawValue());
22598         if(v){
22599             this.setValue(v);
22600         }
22601     }
22602
22603     /** @cfg {Boolean} grow @hide */
22604     /** @cfg {Number} growMin @hide */
22605     /** @cfg {Number} growMax @hide */
22606     /**
22607      * @hide
22608      * @method autoSize
22609      */
22610 });/*
22611  * Based on:
22612  * Ext JS Library 1.1.1
22613  * Copyright(c) 2006-2007, Ext JS, LLC.
22614  *
22615  * Originally Released Under LGPL - original licence link has changed is not relivant.
22616  *
22617  * Fork - LGPL
22618  * <script type="text/javascript">
22619  */
22620  
22621
22622 /**
22623  * @class Roo.form.ComboBox
22624  * @extends Roo.form.TriggerField
22625  * A combobox control with support for autocomplete, remote-loading, paging and many other features.
22626  * @constructor
22627  * Create a new ComboBox.
22628  * @param {Object} config Configuration options
22629  */
22630 Roo.form.ComboBox = function(config){
22631     Roo.form.ComboBox.superclass.constructor.call(this, config);
22632     this.addEvents({
22633         /**
22634          * @event expand
22635          * Fires when the dropdown list is expanded
22636              * @param {Roo.form.ComboBox} combo This combo box
22637              */
22638         'expand' : true,
22639         /**
22640          * @event collapse
22641          * Fires when the dropdown list is collapsed
22642              * @param {Roo.form.ComboBox} combo This combo box
22643              */
22644         'collapse' : true,
22645         /**
22646          * @event beforeselect
22647          * Fires before a list item is selected. Return false to cancel the selection.
22648              * @param {Roo.form.ComboBox} combo This combo box
22649              * @param {Roo.data.Record} record The data record returned from the underlying store
22650              * @param {Number} index The index of the selected item in the dropdown list
22651              */
22652         'beforeselect' : true,
22653         /**
22654          * @event select
22655          * Fires when a list item is selected
22656              * @param {Roo.form.ComboBox} combo This combo box
22657              * @param {Roo.data.Record} record The data record returned from the underlying store (or false on clear)
22658              * @param {Number} index The index of the selected item in the dropdown list
22659              */
22660         'select' : true,
22661         /**
22662          * @event beforequery
22663          * Fires before all queries are processed. Return false to cancel the query or set cancel to true.
22664          * The event object passed has these properties:
22665              * @param {Roo.form.ComboBox} combo This combo box
22666              * @param {String} query The query
22667              * @param {Boolean} forceAll true to force "all" query
22668              * @param {Boolean} cancel true to cancel the query
22669              * @param {Object} e The query event object
22670              */
22671         'beforequery': true,
22672          /**
22673          * @event add
22674          * Fires when the 'add' icon is pressed (add a listener to enable add button)
22675              * @param {Roo.form.ComboBox} combo This combo box
22676              */
22677         'add' : true,
22678         /**
22679          * @event edit
22680          * Fires when the 'edit' icon is pressed (add a listener to enable add button)
22681              * @param {Roo.form.ComboBox} combo This combo box
22682              * @param {Roo.data.Record|false} record The data record returned from the underlying store (or false on nothing selected)
22683              */
22684         'edit' : true
22685         
22686         
22687     });
22688     if(this.transform){
22689         this.allowDomMove = false;
22690         var s = Roo.getDom(this.transform);
22691         if(!this.hiddenName){
22692             this.hiddenName = s.name;
22693         }
22694         if(!this.store){
22695             this.mode = 'local';
22696             var d = [], opts = s.options;
22697             for(var i = 0, len = opts.length;i < len; i++){
22698                 var o = opts[i];
22699                 var value = (Roo.isIE ? o.getAttributeNode('value').specified : o.hasAttribute('value')) ? o.value : o.text;
22700                 if(o.selected) {
22701                     this.value = value;
22702                 }
22703                 d.push([value, o.text]);
22704             }
22705             this.store = new Roo.data.SimpleStore({
22706                 'id': 0,
22707                 fields: ['value', 'text'],
22708                 data : d
22709             });
22710             this.valueField = 'value';
22711             this.displayField = 'text';
22712         }
22713         s.name = Roo.id(); // wipe out the name in case somewhere else they have a reference
22714         if(!this.lazyRender){
22715             this.target = true;
22716             this.el = Roo.DomHelper.insertBefore(s, this.autoCreate || this.defaultAutoCreate);
22717             s.parentNode.removeChild(s); // remove it
22718             this.render(this.el.parentNode);
22719         }else{
22720             s.parentNode.removeChild(s); // remove it
22721         }
22722
22723     }
22724     if (this.store) {
22725         this.store = Roo.factory(this.store, Roo.data);
22726     }
22727     
22728     this.selectedIndex = -1;
22729     if(this.mode == 'local'){
22730         if(config.queryDelay === undefined){
22731             this.queryDelay = 10;
22732         }
22733         if(config.minChars === undefined){
22734             this.minChars = 0;
22735         }
22736     }
22737 };
22738
22739 Roo.extend(Roo.form.ComboBox, Roo.form.TriggerField, {
22740     /**
22741      * @cfg {String/HTMLElement/Element} transform The id, DOM node or element of an existing select to convert to a ComboBox
22742      */
22743     /**
22744      * @cfg {Boolean} lazyRender True to prevent the ComboBox from rendering until requested (should always be used when
22745      * rendering into an Roo.Editor, defaults to false)
22746      */
22747     /**
22748      * @cfg {Boolean/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to:
22749      * {tag: "input", type: "text", size: "24", autocomplete: "off"})
22750      */
22751     /**
22752      * @cfg {Roo.data.Store} store The data store to which this combo is bound (defaults to undefined)
22753      */
22754     /**
22755      * @cfg {String} title If supplied, a header element is created containing this text and added into the top of
22756      * the dropdown list (defaults to undefined, with no header element)
22757      */
22758
22759      /**
22760      * @cfg {String/Roo.Template} tpl The template to use to render the output
22761      */
22762      
22763     // private
22764     defaultAutoCreate : {tag: "input", type: "text", size: "24", autocomplete: "off"},
22765     /**
22766      * @cfg {Number} listWidth The width in pixels of the dropdown list (defaults to the width of the ComboBox field)
22767      */
22768     listWidth: undefined,
22769     /**
22770      * @cfg {String} displayField The underlying data field name to bind to this CombBox (defaults to undefined if
22771      * mode = 'remote' or 'text' if mode = 'local')
22772      */
22773     displayField: undefined,
22774     /**
22775      * @cfg {String} valueField The underlying data value name to bind to this CombBox (defaults to undefined if
22776      * mode = 'remote' or 'value' if mode = 'local'). 
22777      * Note: use of a valueField requires the user make a selection
22778      * in order for a value to be mapped.
22779      */
22780     valueField: undefined,
22781     /**
22782      * @cfg {String} hiddenName If specified, a hidden form field with this name is dynamically generated to store the
22783      * field's data value (defaults to the underlying DOM element's name)
22784      */
22785     hiddenName: undefined,
22786     /**
22787      * @cfg {String} listClass CSS class to apply to the dropdown list element (defaults to '')
22788      */
22789     listClass: '',
22790     /**
22791      * @cfg {String} selectedClass CSS class to apply to the selected item in the dropdown list (defaults to 'x-combo-selected')
22792      */
22793     selectedClass: 'x-combo-selected',
22794     /**
22795      * @cfg {String} triggerClass An additional CSS class used to style the trigger button.  The trigger will always get the
22796      * class 'x-form-trigger' and triggerClass will be <b>appended</b> if specified (defaults to 'x-form-arrow-trigger'
22797      * which displays a downward arrow icon).
22798      */
22799     triggerClass : 'x-form-arrow-trigger',
22800     /**
22801      * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
22802      */
22803     shadow:'sides',
22804     /**
22805      * @cfg {String} listAlign A valid anchor position value. See {@link Roo.Element#alignTo} for details on supported
22806      * anchor positions (defaults to 'tl-bl')
22807      */
22808     listAlign: 'tl-bl?',
22809     /**
22810      * @cfg {Number} maxHeight The maximum height in pixels of the dropdown list before scrollbars are shown (defaults to 300)
22811      */
22812     maxHeight: 300,
22813     /**
22814      * @cfg {String} triggerAction The action to execute when the trigger field is activated.  Use 'all' to run the
22815      * query specified by the allQuery config option (defaults to 'query')
22816      */
22817     triggerAction: 'query',
22818     /**
22819      * @cfg {Number} minChars The minimum number of characters the user must type before autocomplete and typeahead activate
22820      * (defaults to 4, does not apply if editable = false)
22821      */
22822     minChars : 4,
22823     /**
22824      * @cfg {Boolean} typeAhead True to populate and autoselect the remainder of the text being typed after a configurable
22825      * delay (typeAheadDelay) if it matches a known value (defaults to false)
22826      */
22827     typeAhead: false,
22828     /**
22829      * @cfg {Number} queryDelay The length of time in milliseconds to delay between the start of typing and sending the
22830      * query to filter the dropdown list (defaults to 500 if mode = 'remote' or 10 if mode = 'local')
22831      */
22832     queryDelay: 500,
22833     /**
22834      * @cfg {Number} pageSize If greater than 0, a paging toolbar is displayed in the footer of the dropdown list and the
22835      * filter queries will execute with page start and limit parameters.  Only applies when mode = 'remote' (defaults to 0)
22836      */
22837     pageSize: 0,
22838     /**
22839      * @cfg {Boolean} selectOnFocus True to select any existing text in the field immediately on focus.  Only applies
22840      * when editable = true (defaults to false)
22841      */
22842     selectOnFocus:false,
22843     /**
22844      * @cfg {String} queryParam Name of the query as it will be passed on the querystring (defaults to 'query')
22845      */
22846     queryParam: 'query',
22847     /**
22848      * @cfg {String} loadingText The text to display in the dropdown list while data is loading.  Only applies
22849      * when mode = 'remote' (defaults to 'Loading...')
22850      */
22851     loadingText: 'Loading...',
22852     /**
22853      * @cfg {Boolean} resizable True to add a resize handle to the bottom of the dropdown list (defaults to false)
22854      */
22855     resizable: false,
22856     /**
22857      * @cfg {Number} handleHeight The height in pixels of the dropdown list resize handle if resizable = true (defaults to 8)
22858      */
22859     handleHeight : 8,
22860     /**
22861      * @cfg {Boolean} editable False to prevent the user from typing text directly into the field, just like a
22862      * traditional select (defaults to true)
22863      */
22864     editable: true,
22865     /**
22866      * @cfg {String} allQuery The text query to send to the server to return all records for the list with no filtering (defaults to '')
22867      */
22868     allQuery: '',
22869     /**
22870      * @cfg {String} mode Set to 'local' if the ComboBox loads local data (defaults to 'remote' which loads from the server)
22871      */
22872     mode: 'remote',
22873     /**
22874      * @cfg {Number} minListWidth The minimum width of the dropdown list in pixels (defaults to 70, will be ignored if
22875      * listWidth has a higher value)
22876      */
22877     minListWidth : 70,
22878     /**
22879      * @cfg {Boolean} forceSelection True to restrict the selected value to one of the values in the list, false to
22880      * allow the user to set arbitrary text into the field (defaults to false)
22881      */
22882     forceSelection:false,
22883     /**
22884      * @cfg {Number} typeAheadDelay The length of time in milliseconds to wait until the typeahead text is displayed
22885      * if typeAhead = true (defaults to 250)
22886      */
22887     typeAheadDelay : 250,
22888     /**
22889      * @cfg {String} valueNotFoundText When using a name/value combo, if the value passed to setValue is not found in
22890      * the store, valueNotFoundText will be displayed as the field text if defined (defaults to undefined)
22891      */
22892     valueNotFoundText : undefined,
22893     /**
22894      * @cfg {Boolean} blockFocus Prevents all focus calls, so it can work with things like HTML edtor bar
22895      */
22896     blockFocus : false,
22897     
22898     /**
22899      * @cfg {Boolean} disableClear Disable showing of clear button.
22900      */
22901     disableClear : false,
22902     /**
22903      * @cfg {Boolean} alwaysQuery  Disable caching of results, and always send query
22904      */
22905     alwaysQuery : false,
22906     
22907     //private
22908     addicon : false,
22909     editicon: false,
22910     
22911     
22912     // private
22913     onRender : function(ct, position){
22914         Roo.form.ComboBox.superclass.onRender.call(this, ct, position);
22915         if(this.hiddenName){
22916             this.hiddenField = this.el.insertSibling({tag:'input', type:'hidden', name: this.hiddenName, id:  (this.hiddenId||this.hiddenName)},
22917                     'before', true);
22918             this.hiddenField.value =
22919                 this.hiddenValue !== undefined ? this.hiddenValue :
22920                 this.value !== undefined ? this.value : '';
22921
22922             // prevent input submission
22923             this.el.dom.removeAttribute('name');
22924         }
22925         if(Roo.isGecko){
22926             this.el.dom.setAttribute('autocomplete', 'off');
22927         }
22928
22929         var cls = 'x-combo-list';
22930
22931         this.list = new Roo.Layer({
22932             shadow: this.shadow, cls: [cls, this.listClass].join(' '), constrain:false
22933         });
22934
22935         var lw = this.listWidth || Math.max(this.wrap.getWidth(), this.minListWidth);
22936         this.list.setWidth(lw);
22937         this.list.swallowEvent('mousewheel');
22938         this.assetHeight = 0;
22939
22940         if(this.title){
22941             this.header = this.list.createChild({cls:cls+'-hd', html: this.title});
22942             this.assetHeight += this.header.getHeight();
22943         }
22944
22945         this.innerList = this.list.createChild({cls:cls+'-inner'});
22946         this.innerList.on('mouseover', this.onViewOver, this);
22947         this.innerList.on('mousemove', this.onViewMove, this);
22948         this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
22949         
22950         if(this.allowBlank && !this.pageSize && !this.disableClear){
22951             this.footer = this.list.createChild({cls:cls+'-ft'});
22952             this.pageTb = new Roo.Toolbar(this.footer);
22953            
22954         }
22955         if(this.pageSize){
22956             this.footer = this.list.createChild({cls:cls+'-ft'});
22957             this.pageTb = new Roo.PagingToolbar(this.footer, this.store,
22958                     {pageSize: this.pageSize});
22959             
22960         }
22961         
22962         if (this.pageTb && this.allowBlank && !this.disableClear) {
22963             var _this = this;
22964             this.pageTb.add(new Roo.Toolbar.Fill(), {
22965                 cls: 'x-btn-icon x-btn-clear',
22966                 text: '&#160;',
22967                 handler: function()
22968                 {
22969                     _this.collapse();
22970                     _this.clearValue();
22971                     _this.onSelect(false, -1);
22972                 }
22973             });
22974         }
22975         if (this.footer) {
22976             this.assetHeight += this.footer.getHeight();
22977         }
22978         
22979
22980         if(!this.tpl){
22981             this.tpl = '<div class="'+cls+'-item">{' + this.displayField + '}</div>';
22982         }
22983
22984         this.view = new Roo.View(this.innerList, this.tpl, {
22985             singleSelect:true, store: this.store, selectedClass: this.selectedClass
22986         });
22987
22988         this.view.on('click', this.onViewClick, this);
22989
22990         this.store.on('beforeload', this.onBeforeLoad, this);
22991         this.store.on('load', this.onLoad, this);
22992         this.store.on('loadexception', this.collapse, this);
22993
22994         if(this.resizable){
22995             this.resizer = new Roo.Resizable(this.list,  {
22996                pinned:true, handles:'se'
22997             });
22998             this.resizer.on('resize', function(r, w, h){
22999                 this.maxHeight = h-this.handleHeight-this.list.getFrameWidth('tb')-this.assetHeight;
23000                 this.listWidth = w;
23001                 this.innerList.setWidth(w - this.list.getFrameWidth('lr'));
23002                 this.restrictHeight();
23003             }, this);
23004             this[this.pageSize?'footer':'innerList'].setStyle('margin-bottom', this.handleHeight+'px');
23005         }
23006         if(!this.editable){
23007             this.editable = true;
23008             this.setEditable(false);
23009         }  
23010         
23011         
23012         if (typeof(this.events.add.listeners) != 'undefined') {
23013             
23014             this.addicon = this.wrap.createChild(
23015                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-add' });  
23016        
23017             this.addicon.on('click', function(e) {
23018                 this.fireEvent('add', this);
23019             }, this);
23020         }
23021         if (typeof(this.events.edit.listeners) != 'undefined') {
23022             
23023             this.editicon = this.wrap.createChild(
23024                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-edit' });  
23025             if (this.addicon) {
23026                 this.editicon.setStyle('margin-left', '40px');
23027             }
23028             this.editicon.on('click', function(e) {
23029                 
23030                 // we fire even  if inothing is selected..
23031                 this.fireEvent('edit', this, this.lastData );
23032                 
23033             }, this);
23034         }
23035         
23036         
23037         
23038     },
23039
23040     // private
23041     initEvents : function(){
23042         Roo.form.ComboBox.superclass.initEvents.call(this);
23043
23044         this.keyNav = new Roo.KeyNav(this.el, {
23045             "up" : function(e){
23046                 this.inKeyMode = true;
23047                 this.selectPrev();
23048             },
23049
23050             "down" : function(e){
23051                 if(!this.isExpanded()){
23052                     this.onTriggerClick();
23053                 }else{
23054                     this.inKeyMode = true;
23055                     this.selectNext();
23056                 }
23057             },
23058
23059             "enter" : function(e){
23060                 this.onViewClick();
23061                 //return true;
23062             },
23063
23064             "esc" : function(e){
23065                 this.collapse();
23066             },
23067
23068             "tab" : function(e){
23069                 this.onViewClick(false);
23070                 return true;
23071             },
23072
23073             scope : this,
23074
23075             doRelay : function(foo, bar, hname){
23076                 if(hname == 'down' || this.scope.isExpanded()){
23077                    return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
23078                 }
23079                 return true;
23080             },
23081
23082             forceKeyDown: true
23083         });
23084         this.queryDelay = Math.max(this.queryDelay || 10,
23085                 this.mode == 'local' ? 10 : 250);
23086         this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
23087         if(this.typeAhead){
23088             this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
23089         }
23090         if(this.editable !== false){
23091             this.el.on("keyup", this.onKeyUp, this);
23092         }
23093         if(this.forceSelection){
23094             this.on('blur', this.doForce, this);
23095         }
23096     },
23097
23098     onDestroy : function(){
23099         if(this.view){
23100             this.view.setStore(null);
23101             this.view.el.removeAllListeners();
23102             this.view.el.remove();
23103             this.view.purgeListeners();
23104         }
23105         if(this.list){
23106             this.list.destroy();
23107         }
23108         if(this.store){
23109             this.store.un('beforeload', this.onBeforeLoad, this);
23110             this.store.un('load', this.onLoad, this);
23111             this.store.un('loadexception', this.collapse, this);
23112         }
23113         Roo.form.ComboBox.superclass.onDestroy.call(this);
23114     },
23115
23116     // private
23117     fireKey : function(e){
23118         if(e.isNavKeyPress() && !this.list.isVisible()){
23119             this.fireEvent("specialkey", this, e);
23120         }
23121     },
23122
23123     // private
23124     onResize: function(w, h){
23125         Roo.form.ComboBox.superclass.onResize.apply(this, arguments);
23126         
23127         if(typeof w != 'number'){
23128             // we do not handle it!?!?
23129             return;
23130         }
23131         var tw = this.trigger.getWidth();
23132         tw += this.addicon ? this.addicon.getWidth() : 0;
23133         tw += this.editicon ? this.editicon.getWidth() : 0;
23134         var x = w - tw;
23135         this.el.setWidth( this.adjustWidth('input', x));
23136             
23137         this.trigger.setStyle('left', x+'px');
23138         
23139         if(this.list && this.listWidth === undefined){
23140             var lw = Math.max(x + this.trigger.getWidth(), this.minListWidth);
23141             this.list.setWidth(lw);
23142             this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
23143         }
23144         
23145     
23146         
23147     },
23148
23149     /**
23150      * Allow or prevent the user from directly editing the field text.  If false is passed,
23151      * the user will only be able to select from the items defined in the dropdown list.  This method
23152      * is the runtime equivalent of setting the 'editable' config option at config time.
23153      * @param {Boolean} value True to allow the user to directly edit the field text
23154      */
23155     setEditable : function(value){
23156         if(value == this.editable){
23157             return;
23158         }
23159         this.editable = value;
23160         if(!value){
23161             this.el.dom.setAttribute('readOnly', true);
23162             this.el.on('mousedown', this.onTriggerClick,  this);
23163             this.el.addClass('x-combo-noedit');
23164         }else{
23165             this.el.dom.setAttribute('readOnly', false);
23166             this.el.un('mousedown', this.onTriggerClick,  this);
23167             this.el.removeClass('x-combo-noedit');
23168         }
23169     },
23170
23171     // private
23172     onBeforeLoad : function(){
23173         if(!this.hasFocus){
23174             return;
23175         }
23176         this.innerList.update(this.loadingText ?
23177                '<div class="loading-indicator">'+this.loadingText+'</div>' : '');
23178         this.restrictHeight();
23179         this.selectedIndex = -1;
23180     },
23181
23182     // private
23183     onLoad : function(){
23184         if(!this.hasFocus){
23185             return;
23186         }
23187         if(this.store.getCount() > 0){
23188             this.expand();
23189             this.restrictHeight();
23190             if(this.lastQuery == this.allQuery){
23191                 if(this.editable){
23192                     this.el.dom.select();
23193                 }
23194                 if(!this.selectByValue(this.value, true)){
23195                     this.select(0, true);
23196                 }
23197             }else{
23198                 this.selectNext();
23199                 if(this.typeAhead && this.lastKey != Roo.EventObject.BACKSPACE && this.lastKey != Roo.EventObject.DELETE){
23200                     this.taTask.delay(this.typeAheadDelay);
23201                 }
23202             }
23203         }else{
23204             this.onEmptyResults();
23205         }
23206         //this.el.focus();
23207     },
23208
23209     // private
23210     onTypeAhead : function(){
23211         if(this.store.getCount() > 0){
23212             var r = this.store.getAt(0);
23213             var newValue = r.data[this.displayField];
23214             var len = newValue.length;
23215             var selStart = this.getRawValue().length;
23216             if(selStart != len){
23217                 this.setRawValue(newValue);
23218                 this.selectText(selStart, newValue.length);
23219             }
23220         }
23221     },
23222
23223     // private
23224     onSelect : function(record, index){
23225         if(this.fireEvent('beforeselect', this, record, index) !== false){
23226             this.setFromData(index > -1 ? record.data : false);
23227             this.collapse();
23228             this.fireEvent('select', this, record, index);
23229         }
23230     },
23231
23232     /**
23233      * Returns the currently selected field value or empty string if no value is set.
23234      * @return {String} value The selected value
23235      */
23236     getValue : function(){
23237         if(this.valueField){
23238             return typeof this.value != 'undefined' ? this.value : '';
23239         }else{
23240             return Roo.form.ComboBox.superclass.getValue.call(this);
23241         }
23242     },
23243
23244     /**
23245      * Clears any text/value currently set in the field
23246      */
23247     clearValue : function(){
23248         if(this.hiddenField){
23249             this.hiddenField.value = '';
23250         }
23251         this.value = '';
23252         this.setRawValue('');
23253         this.lastSelectionText = '';
23254         this.applyEmptyText();
23255     },
23256
23257     /**
23258      * Sets the specified value into the field.  If the value finds a match, the corresponding record text
23259      * will be displayed in the field.  If the value does not match the data value of an existing item,
23260      * and the valueNotFoundText config option is defined, it will be displayed as the default field text.
23261      * Otherwise the field will be blank (although the value will still be set).
23262      * @param {String} value The value to match
23263      */
23264     setValue : function(v){
23265         var text = v;
23266         if(this.valueField){
23267             var r = this.findRecord(this.valueField, v);
23268             if(r){
23269                 text = r.data[this.displayField];
23270             }else if(this.valueNotFoundText !== undefined){
23271                 text = this.valueNotFoundText;
23272             }
23273         }
23274         this.lastSelectionText = text;
23275         if(this.hiddenField){
23276             this.hiddenField.value = v;
23277         }
23278         Roo.form.ComboBox.superclass.setValue.call(this, text);
23279         this.value = v;
23280     },
23281     /**
23282      * @property {Object} the last set data for the element
23283      */
23284     
23285     lastData : false,
23286     /**
23287      * Sets the value of the field based on a object which is related to the record format for the store.
23288      * @param {Object} value the value to set as. or false on reset?
23289      */
23290     setFromData : function(o){
23291         var dv = ''; // display value
23292         var vv = ''; // value value..
23293         this.lastData = o;
23294         if (this.displayField) {
23295             dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
23296         } else {
23297             // this is an error condition!!!
23298             Roo.log('no  displayField value set for '+ (this.name ? this.name : this.id));
23299         }
23300         
23301         if(this.valueField){
23302             vv = !o || typeof(o[this.valueField]) == 'undefined' ? dv : o[this.valueField];
23303         }
23304         if(this.hiddenField){
23305             this.hiddenField.value = vv;
23306             
23307             this.lastSelectionText = dv;
23308             Roo.form.ComboBox.superclass.setValue.call(this, dv);
23309             this.value = vv;
23310             return;
23311         }
23312         // no hidden field.. - we store the value in 'value', but still display
23313         // display field!!!!
23314         this.lastSelectionText = dv;
23315         Roo.form.ComboBox.superclass.setValue.call(this, dv);
23316         this.value = vv;
23317         
23318         
23319     },
23320     // private
23321     reset : function(){
23322         // overridden so that last data is reset..
23323         this.setValue(this.originalValue);
23324         this.clearInvalid();
23325         this.lastData = false;
23326     },
23327     // private
23328     findRecord : function(prop, value){
23329         var record;
23330         if(this.store.getCount() > 0){
23331             this.store.each(function(r){
23332                 if(r.data[prop] == value){
23333                     record = r;
23334                     return false;
23335                 }
23336             });
23337         }
23338         return record;
23339     },
23340
23341     // private
23342     onViewMove : function(e, t){
23343         this.inKeyMode = false;
23344     },
23345
23346     // private
23347     onViewOver : function(e, t){
23348         if(this.inKeyMode){ // prevent key nav and mouse over conflicts
23349             return;
23350         }
23351         var item = this.view.findItemFromChild(t);
23352         if(item){
23353             var index = this.view.indexOf(item);
23354             this.select(index, false);
23355         }
23356     },
23357
23358     // private
23359     onViewClick : function(doFocus){
23360         var index = this.view.getSelectedIndexes()[0];
23361         var r = this.store.getAt(index);
23362         if(r){
23363             this.onSelect(r, index);
23364         }
23365         if(doFocus !== false && !this.blockFocus){
23366             this.el.focus();
23367         }
23368     },
23369
23370     // private
23371     restrictHeight : function(){
23372         this.innerList.dom.style.height = '';
23373         var inner = this.innerList.dom;
23374         var h = Math.max(inner.clientHeight, inner.offsetHeight, inner.scrollHeight);
23375         this.innerList.setHeight(h < this.maxHeight ? 'auto' : this.maxHeight);
23376         this.list.beginUpdate();
23377         this.list.setHeight(this.innerList.getHeight()+this.list.getFrameWidth('tb')+(this.resizable?this.handleHeight:0)+this.assetHeight);
23378         this.list.alignTo(this.el, this.listAlign);
23379         this.list.endUpdate();
23380     },
23381
23382     // private
23383     onEmptyResults : function(){
23384         this.collapse();
23385     },
23386
23387     /**
23388      * Returns true if the dropdown list is expanded, else false.
23389      */
23390     isExpanded : function(){
23391         return this.list.isVisible();
23392     },
23393
23394     /**
23395      * Select an item in the dropdown list by its data value. This function does NOT cause the select event to fire.
23396      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
23397      * @param {String} value The data value of the item to select
23398      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
23399      * selected item if it is not currently in view (defaults to true)
23400      * @return {Boolean} True if the value matched an item in the list, else false
23401      */
23402     selectByValue : function(v, scrollIntoView){
23403         if(v !== undefined && v !== null){
23404             var r = this.findRecord(this.valueField || this.displayField, v);
23405             if(r){
23406                 this.select(this.store.indexOf(r), scrollIntoView);
23407                 return true;
23408             }
23409         }
23410         return false;
23411     },
23412
23413     /**
23414      * Select an item in the dropdown list by its numeric index in the list. This function does NOT cause the select event to fire.
23415      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
23416      * @param {Number} index The zero-based index of the list item to select
23417      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
23418      * selected item if it is not currently in view (defaults to true)
23419      */
23420     select : function(index, scrollIntoView){
23421         this.selectedIndex = index;
23422         this.view.select(index);
23423         if(scrollIntoView !== false){
23424             var el = this.view.getNode(index);
23425             if(el){
23426                 this.innerList.scrollChildIntoView(el, false);
23427             }
23428         }
23429     },
23430
23431     // private
23432     selectNext : function(){
23433         var ct = this.store.getCount();
23434         if(ct > 0){
23435             if(this.selectedIndex == -1){
23436                 this.select(0);
23437             }else if(this.selectedIndex < ct-1){
23438                 this.select(this.selectedIndex+1);
23439             }
23440         }
23441     },
23442
23443     // private
23444     selectPrev : function(){
23445         var ct = this.store.getCount();
23446         if(ct > 0){
23447             if(this.selectedIndex == -1){
23448                 this.select(0);
23449             }else if(this.selectedIndex != 0){
23450                 this.select(this.selectedIndex-1);
23451             }
23452         }
23453     },
23454
23455     // private
23456     onKeyUp : function(e){
23457         if(this.editable !== false && !e.isSpecialKey()){
23458             this.lastKey = e.getKey();
23459             this.dqTask.delay(this.queryDelay);
23460         }
23461     },
23462
23463     // private
23464     validateBlur : function(){
23465         return !this.list || !this.list.isVisible();   
23466     },
23467
23468     // private
23469     initQuery : function(){
23470         this.doQuery(this.getRawValue());
23471     },
23472
23473     // private
23474     doForce : function(){
23475         if(this.el.dom.value.length > 0){
23476             this.el.dom.value =
23477                 this.lastSelectionText === undefined ? '' : this.lastSelectionText;
23478             this.applyEmptyText();
23479         }
23480     },
23481
23482     /**
23483      * Execute a query to filter the dropdown list.  Fires the beforequery event prior to performing the
23484      * query allowing the query action to be canceled if needed.
23485      * @param {String} query The SQL query to execute
23486      * @param {Boolean} forceAll True to force the query to execute even if there are currently fewer characters
23487      * in the field than the minimum specified by the minChars config option.  It also clears any filter previously
23488      * saved in the current store (defaults to false)
23489      */
23490     doQuery : function(q, forceAll){
23491         if(q === undefined || q === null){
23492             q = '';
23493         }
23494         var qe = {
23495             query: q,
23496             forceAll: forceAll,
23497             combo: this,
23498             cancel:false
23499         };
23500         if(this.fireEvent('beforequery', qe)===false || qe.cancel){
23501             return false;
23502         }
23503         q = qe.query;
23504         forceAll = qe.forceAll;
23505         if(forceAll === true || (q.length >= this.minChars)){
23506             if(this.lastQuery != q || this.alwaysQuery){
23507                 this.lastQuery = q;
23508                 if(this.mode == 'local'){
23509                     this.selectedIndex = -1;
23510                     if(forceAll){
23511                         this.store.clearFilter();
23512                     }else{
23513                         this.store.filter(this.displayField, q);
23514                     }
23515                     this.onLoad();
23516                 }else{
23517                     this.store.baseParams[this.queryParam] = q;
23518                     this.store.load({
23519                         params: this.getParams(q)
23520                     });
23521                     this.expand();
23522                 }
23523             }else{
23524                 this.selectedIndex = -1;
23525                 this.onLoad();   
23526             }
23527         }
23528     },
23529
23530     // private
23531     getParams : function(q){
23532         var p = {};
23533         //p[this.queryParam] = q;
23534         if(this.pageSize){
23535             p.start = 0;
23536             p.limit = this.pageSize;
23537         }
23538         return p;
23539     },
23540
23541     /**
23542      * Hides the dropdown list if it is currently expanded. Fires the 'collapse' event on completion.
23543      */
23544     collapse : function(){
23545         if(!this.isExpanded()){
23546             return;
23547         }
23548         this.list.hide();
23549         Roo.get(document).un('mousedown', this.collapseIf, this);
23550         Roo.get(document).un('mousewheel', this.collapseIf, this);
23551         if (!this.editable) {
23552             Roo.get(document).un('keydown', this.listKeyPress, this);
23553         }
23554         this.fireEvent('collapse', this);
23555     },
23556
23557     // private
23558     collapseIf : function(e){
23559         if(!e.within(this.wrap) && !e.within(this.list)){
23560             this.collapse();
23561         }
23562     },
23563
23564     /**
23565      * Expands the dropdown list if it is currently hidden. Fires the 'expand' event on completion.
23566      */
23567     expand : function(){
23568         if(this.isExpanded() || !this.hasFocus){
23569             return;
23570         }
23571         this.list.alignTo(this.el, this.listAlign);
23572         this.list.show();
23573         Roo.get(document).on('mousedown', this.collapseIf, this);
23574         Roo.get(document).on('mousewheel', this.collapseIf, this);
23575         if (!this.editable) {
23576             Roo.get(document).on('keydown', this.listKeyPress, this);
23577         }
23578         
23579         this.fireEvent('expand', this);
23580     },
23581
23582     // private
23583     // Implements the default empty TriggerField.onTriggerClick function
23584     onTriggerClick : function(){
23585         if(this.disabled){
23586             return;
23587         }
23588         if(this.isExpanded()){
23589             this.collapse();
23590             if (!this.blockFocus) {
23591                 this.el.focus();
23592             }
23593             
23594         }else {
23595             this.hasFocus = true;
23596             if(this.triggerAction == 'all') {
23597                 this.doQuery(this.allQuery, true);
23598             } else {
23599                 this.doQuery(this.getRawValue());
23600             }
23601             if (!this.blockFocus) {
23602                 this.el.focus();
23603             }
23604         }
23605     },
23606     listKeyPress : function(e)
23607     {
23608         //Roo.log('listkeypress');
23609         // scroll to first matching element based on key pres..
23610         if (e.isSpecialKey()) {
23611             return false;
23612         }
23613         var k = String.fromCharCode(e.getKey()).toUpperCase();
23614         //Roo.log(k);
23615         var match  = false;
23616         var csel = this.view.getSelectedNodes();
23617         var cselitem = false;
23618         if (csel.length) {
23619             var ix = this.view.indexOf(csel[0]);
23620             cselitem  = this.store.getAt(ix);
23621             if (!cselitem.get(this.displayField) || cselitem.get(this.displayField).substring(0,1).toUpperCase() != k) {
23622                 cselitem = false;
23623             }
23624             
23625         }
23626         
23627         this.store.each(function(v) { 
23628             if (cselitem) {
23629                 // start at existing selection.
23630                 if (cselitem.id == v.id) {
23631                     cselitem = false;
23632                 }
23633                 return;
23634             }
23635                 
23636             if (v.get(this.displayField) && v.get(this.displayField).substring(0,1).toUpperCase() == k) {
23637                 match = this.store.indexOf(v);
23638                 return false;
23639             }
23640         }, this);
23641         
23642         if (match === false) {
23643             return true; // no more action?
23644         }
23645         // scroll to?
23646         this.view.select(match);
23647         var sn = Roo.get(this.view.getSelectedNodes()[0])
23648         sn.scrollIntoView(sn.dom.parentNode, false);
23649     }
23650
23651     /** 
23652     * @cfg {Boolean} grow 
23653     * @hide 
23654     */
23655     /** 
23656     * @cfg {Number} growMin 
23657     * @hide 
23658     */
23659     /** 
23660     * @cfg {Number} growMax 
23661     * @hide 
23662     */
23663     /**
23664      * @hide
23665      * @method autoSize
23666      */
23667 });/*
23668  * Based on:
23669  * Ext JS Library 1.1.1
23670  * Copyright(c) 2006-2007, Ext JS, LLC.
23671  *
23672  * Originally Released Under LGPL - original licence link has changed is not relivant.
23673  *
23674  * Fork - LGPL
23675  * <script type="text/javascript">
23676  */
23677 /**
23678  * @class Roo.form.Checkbox
23679  * @extends Roo.form.Field
23680  * Single checkbox field.  Can be used as a direct replacement for traditional checkbox fields.
23681  * @constructor
23682  * Creates a new Checkbox
23683  * @param {Object} config Configuration options
23684  */
23685 Roo.form.Checkbox = function(config){
23686     Roo.form.Checkbox.superclass.constructor.call(this, config);
23687     this.addEvents({
23688         /**
23689          * @event check
23690          * Fires when the checkbox is checked or unchecked.
23691              * @param {Roo.form.Checkbox} this This checkbox
23692              * @param {Boolean} checked The new checked value
23693              */
23694         check : true
23695     });
23696 };
23697
23698 Roo.extend(Roo.form.Checkbox, Roo.form.Field,  {
23699     /**
23700      * @cfg {String} focusClass The CSS class to use when the checkbox receives focus (defaults to undefined)
23701      */
23702     focusClass : undefined,
23703     /**
23704      * @cfg {String} fieldClass The default CSS class for the checkbox (defaults to "x-form-field")
23705      */
23706     fieldClass: "x-form-field",
23707     /**
23708      * @cfg {Boolean} checked True if the the checkbox should render already checked (defaults to false)
23709      */
23710     checked: false,
23711     /**
23712      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
23713      * {tag: "input", type: "checkbox", autocomplete: "off"})
23714      */
23715     defaultAutoCreate : { tag: "input", type: 'hidden', autocomplete: "off"},
23716     /**
23717      * @cfg {String} boxLabel The text that appears beside the checkbox
23718      */
23719     boxLabel : "",
23720     /**
23721      * @cfg {String} inputValue The value that should go into the generated input element's value attribute
23722      */  
23723     inputValue : '1',
23724     /**
23725      * @cfg {String} valueOff The value that should go into the generated input element's value when unchecked.
23726      */
23727      valueOff: '0', // value when not checked..
23728
23729     actionMode : 'viewEl', 
23730     //
23731     // private
23732     itemCls : 'x-menu-check-item x-form-item',
23733     groupClass : 'x-menu-group-item',
23734     inputType : 'hidden',
23735     
23736     
23737     inSetChecked: false, // check that we are not calling self...
23738     
23739     inputElement: false, // real input element?
23740     basedOn: false, // ????
23741     
23742     isFormField: true, // not sure where this is needed!!!!
23743
23744     onResize : function(){
23745         Roo.form.Checkbox.superclass.onResize.apply(this, arguments);
23746         if(!this.boxLabel){
23747             this.el.alignTo(this.wrap, 'c-c');
23748         }
23749     },
23750
23751     initEvents : function(){
23752         Roo.form.Checkbox.superclass.initEvents.call(this);
23753         this.el.on("click", this.onClick,  this);
23754         this.el.on("change", this.onClick,  this);
23755     },
23756
23757
23758     getResizeEl : function(){
23759         return this.wrap;
23760     },
23761
23762     getPositionEl : function(){
23763         return this.wrap;
23764     },
23765
23766     // private
23767     onRender : function(ct, position){
23768         Roo.form.Checkbox.superclass.onRender.call(this, ct, position);
23769         /*
23770         if(this.inputValue !== undefined){
23771             this.el.dom.value = this.inputValue;
23772         }
23773         */
23774         //this.wrap = this.el.wrap({cls: "x-form-check-wrap"});
23775         this.wrap = this.el.wrap({cls: 'x-menu-check-item '});
23776         var viewEl = this.wrap.createChild({ 
23777             tag: 'img', cls: 'x-menu-item-icon', style: 'margin: 0px;' ,src : Roo.BLANK_IMAGE_URL });
23778         this.viewEl = viewEl;   
23779         this.wrap.on('click', this.onClick,  this); 
23780         
23781         this.el.on('DOMAttrModified', this.setFromHidden,  this); //ff
23782         this.el.on('propertychange', this.setFromHidden,  this);  //ie
23783         
23784         
23785         
23786         if(this.boxLabel){
23787             this.wrap.createChild({tag: 'label', htmlFor: this.el.id, cls: 'x-form-cb-label', html: this.boxLabel});
23788         //    viewEl.on('click', this.onClick,  this); 
23789         }
23790         //if(this.checked){
23791             this.setChecked(this.checked);
23792         //}else{
23793             //this.checked = this.el.dom;
23794         //}
23795
23796     },
23797
23798     // private
23799     initValue : Roo.emptyFn,
23800
23801     /**
23802      * Returns the checked state of the checkbox.
23803      * @return {Boolean} True if checked, else false
23804      */
23805     getValue : function(){
23806         if(this.el){
23807             return String(this.el.dom.value) == String(this.inputValue ) ? this.inputValue : this.valueOff;
23808         }
23809         return this.valueOff;
23810         
23811     },
23812
23813         // private
23814     onClick : function(){ 
23815         this.setChecked(!this.checked);
23816
23817         //if(this.el.dom.checked != this.checked){
23818         //    this.setValue(this.el.dom.checked);
23819        // }
23820     },
23821
23822     /**
23823      * Sets the checked state of the checkbox.
23824      * On is always based on a string comparison between inputValue and the param.
23825      * @param {Boolean/String} value - the value to set 
23826      * @param {Boolean/String} suppressEvent - whether to suppress the checkchange event.
23827      */
23828     setValue : function(v,suppressEvent){
23829         
23830         
23831         //this.checked = (v === true || v === 'true' || v == '1' || String(v).toLowerCase() == 'on');
23832         //if(this.el && this.el.dom){
23833         //    this.el.dom.checked = this.checked;
23834         //    this.el.dom.defaultChecked = this.checked;
23835         //}
23836         this.setChecked(String(v) === String(this.inputValue), suppressEvent);
23837         //this.fireEvent("check", this, this.checked);
23838     },
23839     // private..
23840     setChecked : function(state,suppressEvent)
23841     {
23842         if (this.inSetChecked) {
23843             this.checked = state;
23844             return;
23845         }
23846         
23847     
23848         if(this.wrap){
23849             this.wrap[state ? 'addClass' : 'removeClass']('x-menu-item-checked');
23850         }
23851         this.checked = state;
23852         if(suppressEvent !== true){
23853             this.fireEvent('check', this, state);
23854         }
23855         this.inSetChecked = true;
23856         this.el.dom.value = state ? this.inputValue : this.valueOff;
23857         this.inSetChecked = false;
23858         
23859     },
23860     // handle setting of hidden value by some other method!!?!?
23861     setFromHidden: function()
23862     {
23863         if(!this.el){
23864             return;
23865         }
23866         //console.log("SET FROM HIDDEN");
23867         //alert('setFrom hidden');
23868         this.setValue(this.el.dom.value);
23869     },
23870     
23871     onDestroy : function()
23872     {
23873         if(this.viewEl){
23874             Roo.get(this.viewEl).remove();
23875         }
23876          
23877         Roo.form.Checkbox.superclass.onDestroy.call(this);
23878     }
23879
23880 });/*
23881  * Based on:
23882  * Ext JS Library 1.1.1
23883  * Copyright(c) 2006-2007, Ext JS, LLC.
23884  *
23885  * Originally Released Under LGPL - original licence link has changed is not relivant.
23886  *
23887  * Fork - LGPL
23888  * <script type="text/javascript">
23889  */
23890  
23891 /**
23892  * @class Roo.form.Radio
23893  * @extends Roo.form.Checkbox
23894  * Single radio field.  Same as Checkbox, but provided as a convenience for automatically setting the input type.
23895  * Radio grouping is handled automatically by the browser if you give each radio in a group the same name.
23896  * @constructor
23897  * Creates a new Radio
23898  * @param {Object} config Configuration options
23899  */
23900 Roo.form.Radio = function(){
23901     Roo.form.Radio.superclass.constructor.apply(this, arguments);
23902 };
23903 Roo.extend(Roo.form.Radio, Roo.form.Checkbox, {
23904     inputType: 'radio',
23905
23906     /**
23907      * If this radio is part of a group, it will return the selected value
23908      * @return {String}
23909      */
23910     getGroupValue : function(){
23911         return this.el.up('form').child('input[name='+this.el.dom.name+']:checked', true).value;
23912     }
23913 });//<script type="text/javascript">
23914
23915 /*
23916  * Ext JS Library 1.1.1
23917  * Copyright(c) 2006-2007, Ext JS, LLC.
23918  * licensing@extjs.com
23919  * 
23920  * http://www.extjs.com/license
23921  */
23922  
23923  /*
23924   * 
23925   * Known bugs:
23926   * Default CSS appears to render it as fixed text by default (should really be Sans-Serif)
23927   * - IE ? - no idea how much works there.
23928   * 
23929   * 
23930   * 
23931   */
23932  
23933
23934 /**
23935  * @class Ext.form.HtmlEditor
23936  * @extends Ext.form.Field
23937  * Provides a lightweight HTML Editor component.
23938  * WARNING - THIS CURRENTlY ONLY WORKS ON FIREFOX - USE FCKeditor for a cross platform version
23939  * 
23940  * <br><br><b>Note: The focus/blur and validation marking functionality inherited from Ext.form.Field is NOT
23941  * supported by this editor.</b><br/><br/>
23942  * An Editor is a sensitive component that can't be used in all spots standard fields can be used. Putting an Editor within
23943  * any element that has display set to 'none' can cause problems in Safari and Firefox.<br/><br/>
23944  */
23945 Roo.form.HtmlEditor = Roo.extend(Roo.form.Field, {
23946       /**
23947      * @cfg {Array} toolbars Array of toolbars. - defaults to just the Standard one
23948      */
23949     toolbars : false,
23950     /**
23951      * @cfg {String} createLinkText The default text for the create link prompt
23952      */
23953     createLinkText : 'Please enter the URL for the link:',
23954     /**
23955      * @cfg {String} defaultLinkValue The default value for the create link prompt (defaults to http:/ /)
23956      */
23957     defaultLinkValue : 'http:/'+'/',
23958    
23959     
23960     // id of frame..
23961     frameId: false,
23962     
23963     // private properties
23964     validationEvent : false,
23965     deferHeight: true,
23966     initialized : false,
23967     activated : false,
23968     sourceEditMode : false,
23969     onFocus : Roo.emptyFn,
23970     iframePad:3,
23971     hideMode:'offsets',
23972     defaultAutoCreate : {
23973         tag: "textarea",
23974         style:"width:500px;height:300px;",
23975         autocomplete: "off"
23976     },
23977
23978     // private
23979     initComponent : function(){
23980         this.addEvents({
23981             /**
23982              * @event initialize
23983              * Fires when the editor is fully initialized (including the iframe)
23984              * @param {HtmlEditor} this
23985              */
23986             initialize: true,
23987             /**
23988              * @event activate
23989              * Fires when the editor is first receives the focus. Any insertion must wait
23990              * until after this event.
23991              * @param {HtmlEditor} this
23992              */
23993             activate: true,
23994              /**
23995              * @event beforesync
23996              * Fires before the textarea is updated with content from the editor iframe. Return false
23997              * to cancel the sync.
23998              * @param {HtmlEditor} this
23999              * @param {String} html
24000              */
24001             beforesync: true,
24002              /**
24003              * @event beforepush
24004              * Fires before the iframe editor is updated with content from the textarea. Return false
24005              * to cancel the push.
24006              * @param {HtmlEditor} this
24007              * @param {String} html
24008              */
24009             beforepush: true,
24010              /**
24011              * @event sync
24012              * Fires when the textarea is updated with content from the editor iframe.
24013              * @param {HtmlEditor} this
24014              * @param {String} html
24015              */
24016             sync: true,
24017              /**
24018              * @event push
24019              * Fires when the iframe editor is updated with content from the textarea.
24020              * @param {HtmlEditor} this
24021              * @param {String} html
24022              */
24023             push: true,
24024              /**
24025              * @event editmodechange
24026              * Fires when the editor switches edit modes
24027              * @param {HtmlEditor} this
24028              * @param {Boolean} sourceEdit True if source edit, false if standard editing.
24029              */
24030             editmodechange: true,
24031             /**
24032              * @event editorevent
24033              * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
24034              * @param {HtmlEditor} this
24035              */
24036             editorevent: true
24037         })
24038     },
24039
24040     /**
24041      * Protected method that will not generally be called directly. It
24042      * is called when the editor creates its toolbar. Override this method if you need to
24043      * add custom toolbar buttons.
24044      * @param {HtmlEditor} editor
24045      */
24046     createToolbar : function(editor){
24047         if (!editor.toolbars || !editor.toolbars.length) {
24048             editor.toolbars = [ new Roo.form.HtmlEditor.ToolbarStandard() ]; // can be empty?
24049         }
24050         
24051         for (var i =0 ; i < editor.toolbars.length;i++) {
24052             editor.toolbars[i] = Roo.factory(editor.toolbars[i], Roo.form.HtmlEditor);
24053             editor.toolbars[i].init(editor);
24054         }
24055          
24056         
24057     },
24058
24059     /**
24060      * Protected method that will not generally be called directly. It
24061      * is called when the editor initializes the iframe with HTML contents. Override this method if you
24062      * want to change the initialization markup of the iframe (e.g. to add stylesheets).
24063      */
24064     getDocMarkup : function(){
24065         return '<html><head><style type="text/css">body{border:0;margin:0;padding:3px;height:98%;cursor:text;}</style></head><body></body></html>';
24066     },
24067
24068     // private
24069     onRender : function(ct, position){
24070         Roo.form.HtmlEditor.superclass.onRender.call(this, ct, position);
24071         this.el.dom.style.border = '0 none';
24072         this.el.dom.setAttribute('tabIndex', -1);
24073         this.el.addClass('x-hidden');
24074         if(Roo.isIE){ // fix IE 1px bogus margin
24075             this.el.applyStyles('margin-top:-1px;margin-bottom:-1px;')
24076         }
24077         this.wrap = this.el.wrap({
24078             cls:'x-html-editor-wrap', cn:{cls:'x-html-editor-tb'}
24079         });
24080
24081         this.frameId = Roo.id();
24082         this.createToolbar(this);
24083         
24084         
24085         
24086         
24087       
24088         
24089         var iframe = this.wrap.createChild({
24090             tag: 'iframe',
24091             id: this.frameId,
24092             name: this.frameId,
24093             frameBorder : 'no',
24094             'src' : Roo.SSL_SECURE_URL ? Roo.SSL_SECURE_URL  :  "javascript:false"
24095         });
24096         
24097        // console.log(iframe);
24098         //this.wrap.dom.appendChild(iframe);
24099
24100         this.iframe = iframe.dom;
24101
24102          this.assignDocWin();
24103         
24104         this.doc.designMode = 'on';
24105        
24106         this.doc.open();
24107         this.doc.write(this.getDocMarkup());
24108         this.doc.close();
24109
24110         
24111         var task = { // must defer to wait for browser to be ready
24112             run : function(){
24113                 //console.log("run task?" + this.doc.readyState);
24114                 this.assignDocWin();
24115                 if(this.doc.body || this.doc.readyState == 'complete'){
24116                     try {
24117                         this.doc.designMode="on";
24118                     } catch (e) {
24119                         return;
24120                     }
24121                     Roo.TaskMgr.stop(task);
24122                     this.initEditor.defer(10, this);
24123                 }
24124             },
24125             interval : 10,
24126             duration:10000,
24127             scope: this
24128         };
24129         Roo.TaskMgr.start(task);
24130
24131         if(!this.width){
24132             this.setSize(this.el.getSize());
24133         }
24134     },
24135
24136     // private
24137     onResize : function(w, h){
24138         Roo.form.HtmlEditor.superclass.onResize.apply(this, arguments);
24139         if(this.el && this.iframe){
24140             if(typeof w == 'number'){
24141                 var aw = w - this.wrap.getFrameWidth('lr');
24142                 this.el.setWidth(this.adjustWidth('textarea', aw));
24143                 this.iframe.style.width = aw + 'px';
24144             }
24145             if(typeof h == 'number'){
24146                 var tbh = 0;
24147                 for (var i =0; i < this.toolbars.length;i++) {
24148                     // fixme - ask toolbars for heights?
24149                     tbh += this.toolbars[i].tb.el.getHeight();
24150                 }
24151                 
24152                 
24153                 
24154                 
24155                 var ah = h - this.wrap.getFrameWidth('tb') - tbh;// this.tb.el.getHeight();
24156                 this.el.setHeight(this.adjustWidth('textarea', ah));
24157                 this.iframe.style.height = ah + 'px';
24158                 if(this.doc){
24159                     (this.doc.body || this.doc.documentElement).style.height = (ah - (this.iframePad*2)) + 'px';
24160                 }
24161             }
24162         }
24163     },
24164
24165     /**
24166      * Toggles the editor between standard and source edit mode.
24167      * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
24168      */
24169     toggleSourceEdit : function(sourceEditMode){
24170         
24171         this.sourceEditMode = sourceEditMode === true;
24172         
24173         if(this.sourceEditMode){
24174           
24175             this.syncValue();
24176             this.iframe.className = 'x-hidden';
24177             this.el.removeClass('x-hidden');
24178             this.el.dom.removeAttribute('tabIndex');
24179             this.el.focus();
24180         }else{
24181              
24182             this.pushValue();
24183             this.iframe.className = '';
24184             this.el.addClass('x-hidden');
24185             this.el.dom.setAttribute('tabIndex', -1);
24186             this.deferFocus();
24187         }
24188         this.setSize(this.wrap.getSize());
24189         this.fireEvent('editmodechange', this, this.sourceEditMode);
24190     },
24191
24192     // private used internally
24193     createLink : function(){
24194         var url = prompt(this.createLinkText, this.defaultLinkValue);
24195         if(url && url != 'http:/'+'/'){
24196             this.relayCmd('createlink', url);
24197         }
24198     },
24199
24200     // private (for BoxComponent)
24201     adjustSize : Roo.BoxComponent.prototype.adjustSize,
24202
24203     // private (for BoxComponent)
24204     getResizeEl : function(){
24205         return this.wrap;
24206     },
24207
24208     // private (for BoxComponent)
24209     getPositionEl : function(){
24210         return this.wrap;
24211     },
24212
24213     // private
24214     initEvents : function(){
24215         this.originalValue = this.getValue();
24216     },
24217
24218     /**
24219      * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
24220      * @method
24221      */
24222     markInvalid : Roo.emptyFn,
24223     /**
24224      * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
24225      * @method
24226      */
24227     clearInvalid : Roo.emptyFn,
24228
24229     setValue : function(v){
24230         Roo.form.HtmlEditor.superclass.setValue.call(this, v);
24231         this.pushValue();
24232     },
24233
24234     /**
24235      * Protected method that will not generally be called directly. If you need/want
24236      * custom HTML cleanup, this is the method you should override.
24237      * @param {String} html The HTML to be cleaned
24238      * return {String} The cleaned HTML
24239      */
24240     cleanHtml : function(html){
24241         html = String(html);
24242         if(html.length > 5){
24243             if(Roo.isSafari){ // strip safari nonsense
24244                 html = html.replace(/\sclass="(?:Apple-style-span|khtml-block-placeholder)"/gi, '');
24245             }
24246         }
24247         if(html == '&nbsp;'){
24248             html = '';
24249         }
24250         return html;
24251     },
24252
24253     /**
24254      * Protected method that will not generally be called directly. Syncs the contents
24255      * of the editor iframe with the textarea.
24256      */
24257     syncValue : function(){
24258         if(this.initialized){
24259             var bd = (this.doc.body || this.doc.documentElement);
24260             this.cleanUpPaste();
24261             var html = bd.innerHTML;
24262             if(Roo.isSafari){
24263                 var bs = bd.getAttribute('style'); // Safari puts text-align styles on the body element!
24264                 var m = bs.match(/text-align:(.*?);/i);
24265                 if(m && m[1]){
24266                     html = '<div style="'+m[0]+'">' + html + '</div>';
24267                 }
24268             }
24269             html = this.cleanHtml(html);
24270             if(this.fireEvent('beforesync', this, html) !== false){
24271                 this.el.dom.value = html;
24272                 this.fireEvent('sync', this, html);
24273             }
24274         }
24275     },
24276
24277     /**
24278      * Protected method that will not generally be called directly. Pushes the value of the textarea
24279      * into the iframe editor.
24280      */
24281     pushValue : function(){
24282         if(this.initialized){
24283             var v = this.el.dom.value;
24284             if(v.length < 1){
24285                 v = '&#160;';
24286             }
24287             
24288             if(this.fireEvent('beforepush', this, v) !== false){
24289                 var d = (this.doc.body || this.doc.documentElement);
24290                 d.innerHTML = v;
24291                 this.cleanUpPaste();
24292                 this.el.dom.value = d.innerHTML;
24293                 this.fireEvent('push', this, v);
24294             }
24295         }
24296     },
24297
24298     // private
24299     deferFocus : function(){
24300         this.focus.defer(10, this);
24301     },
24302
24303     // doc'ed in Field
24304     focus : function(){
24305         if(this.win && !this.sourceEditMode){
24306             this.win.focus();
24307         }else{
24308             this.el.focus();
24309         }
24310     },
24311     
24312     assignDocWin: function()
24313     {
24314         var iframe = this.iframe;
24315         
24316          if(Roo.isIE){
24317             this.doc = iframe.contentWindow.document;
24318             this.win = iframe.contentWindow;
24319         } else {
24320             if (!Roo.get(this.frameId)) {
24321                 return;
24322             }
24323             this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
24324             this.win = Roo.get(this.frameId).dom.contentWindow;
24325         }
24326     },
24327     
24328     // private
24329     initEditor : function(){
24330         //console.log("INIT EDITOR");
24331         this.assignDocWin();
24332         
24333         
24334         
24335         this.doc.designMode="on";
24336         this.doc.open();
24337         this.doc.write(this.getDocMarkup());
24338         this.doc.close();
24339         
24340         var dbody = (this.doc.body || this.doc.documentElement);
24341         //var ss = this.el.getStyles('font-size', 'font-family', 'background-image', 'background-repeat');
24342         // this copies styles from the containing element into thsi one..
24343         // not sure why we need all of this..
24344         var ss = this.el.getStyles('font-size', 'background-image', 'background-repeat');
24345         ss['background-attachment'] = 'fixed'; // w3c
24346         dbody.bgProperties = 'fixed'; // ie
24347         Roo.DomHelper.applyStyles(dbody, ss);
24348         Roo.EventManager.on(this.doc, {
24349             'mousedown': this.onEditorEvent,
24350             'dblclick': this.onEditorEvent,
24351             'click': this.onEditorEvent,
24352             'keyup': this.onEditorEvent,
24353             buffer:100,
24354             scope: this
24355         });
24356         if(Roo.isGecko){
24357             Roo.EventManager.on(this.doc, 'keypress', this.mozKeyPress, this);
24358         }
24359         if(Roo.isIE || Roo.isSafari || Roo.isOpera){
24360             Roo.EventManager.on(this.doc, 'keydown', this.fixKeys, this);
24361         }
24362         this.initialized = true;
24363
24364         this.fireEvent('initialize', this);
24365         this.pushValue();
24366     },
24367
24368     // private
24369     onDestroy : function(){
24370         
24371         
24372         
24373         if(this.rendered){
24374             
24375             for (var i =0; i < this.toolbars.length;i++) {
24376                 // fixme - ask toolbars for heights?
24377                 this.toolbars[i].onDestroy();
24378             }
24379             
24380             this.wrap.dom.innerHTML = '';
24381             this.wrap.remove();
24382         }
24383     },
24384
24385     // private
24386     onFirstFocus : function(){
24387         
24388         this.assignDocWin();
24389         
24390         
24391         this.activated = true;
24392         for (var i =0; i < this.toolbars.length;i++) {
24393             this.toolbars[i].onFirstFocus();
24394         }
24395        
24396         if(Roo.isGecko){ // prevent silly gecko errors
24397             this.win.focus();
24398             var s = this.win.getSelection();
24399             if(!s.focusNode || s.focusNode.nodeType != 3){
24400                 var r = s.getRangeAt(0);
24401                 r.selectNodeContents((this.doc.body || this.doc.documentElement));
24402                 r.collapse(true);
24403                 this.deferFocus();
24404             }
24405             try{
24406                 this.execCmd('useCSS', true);
24407                 this.execCmd('styleWithCSS', false);
24408             }catch(e){}
24409         }
24410         this.fireEvent('activate', this);
24411     },
24412
24413     // private
24414     adjustFont: function(btn){
24415         var adjust = btn.cmd == 'increasefontsize' ? 1 : -1;
24416         //if(Roo.isSafari){ // safari
24417         //    adjust *= 2;
24418        // }
24419         var v = parseInt(this.doc.queryCommandValue('FontSize')|| 3, 10);
24420         if(Roo.isSafari){ // safari
24421             var sm = { 10 : 1, 13: 2, 16:3, 18:4, 24: 5, 32:6, 48: 7 };
24422             v =  (v < 10) ? 10 : v;
24423             v =  (v > 48) ? 48 : v;
24424             v = typeof(sm[v]) == 'undefined' ? 1 : sm[v];
24425             
24426         }
24427         
24428         
24429         v = Math.max(1, v+adjust);
24430         
24431         this.execCmd('FontSize', v  );
24432     },
24433
24434     onEditorEvent : function(e){
24435         this.fireEvent('editorevent', this, e);
24436       //  this.updateToolbar();
24437         this.syncValue();
24438     },
24439
24440     insertTag : function(tg)
24441     {
24442         // could be a bit smarter... -> wrap the current selected tRoo..
24443         
24444         this.execCmd("formatblock",   tg);
24445         
24446     },
24447     
24448     insertText : function(txt)
24449     {
24450         
24451         
24452         range = this.createRange();
24453         range.deleteContents();
24454                //alert(Sender.getAttribute('label'));
24455                
24456         range.insertNode(this.doc.createTextNode(txt));
24457     } ,
24458     
24459     // private
24460     relayBtnCmd : function(btn){
24461         this.relayCmd(btn.cmd);
24462     },
24463
24464     /**
24465      * Executes a Midas editor command on the editor document and performs necessary focus and
24466      * toolbar updates. <b>This should only be called after the editor is initialized.</b>
24467      * @param {String} cmd The Midas command
24468      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
24469      */
24470     relayCmd : function(cmd, value){
24471         this.win.focus();
24472         this.execCmd(cmd, value);
24473         this.fireEvent('editorevent', this);
24474         //this.updateToolbar();
24475         this.deferFocus();
24476     },
24477
24478     /**
24479      * Executes a Midas editor command directly on the editor document.
24480      * For visual commands, you should use {@link #relayCmd} instead.
24481      * <b>This should only be called after the editor is initialized.</b>
24482      * @param {String} cmd The Midas command
24483      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
24484      */
24485     execCmd : function(cmd, value){
24486         this.doc.execCommand(cmd, false, value === undefined ? null : value);
24487         this.syncValue();
24488     },
24489
24490    
24491     /**
24492      * Inserts the passed text at the current cursor position. Note: the editor must be initialized and activated
24493      * to insert tRoo.
24494      * @param {String} text
24495      */
24496     insertAtCursor : function(text){
24497         if(!this.activated){
24498             return;
24499         }
24500         if(Roo.isIE){
24501             this.win.focus();
24502             var r = this.doc.selection.createRange();
24503             if(r){
24504                 r.collapse(true);
24505                 r.pasteHTML(text);
24506                 this.syncValue();
24507                 this.deferFocus();
24508             }
24509         }else if(Roo.isGecko || Roo.isOpera || Roo.isSafari){
24510             this.win.focus();
24511             this.execCmd('InsertHTML', text);
24512             this.deferFocus();
24513         }
24514     },
24515  // private
24516     mozKeyPress : function(e){
24517         if(e.ctrlKey){
24518             var c = e.getCharCode(), cmd;
24519           
24520             if(c > 0){
24521                 c = String.fromCharCode(c).toLowerCase();
24522                 switch(c){
24523                     case 'b':
24524                         cmd = 'bold';
24525                     break;
24526                     case 'i':
24527                         cmd = 'italic';
24528                     break;
24529                     case 'u':
24530                         cmd = 'underline';
24531                     case 'v':
24532                         this.cleanUpPaste.defer(100, this);
24533                         return;
24534                     break;
24535                 }
24536                 if(cmd){
24537                     this.win.focus();
24538                     this.execCmd(cmd);
24539                     this.deferFocus();
24540                     e.preventDefault();
24541                 }
24542                 
24543             }
24544         }
24545     },
24546
24547     // private
24548     fixKeys : function(){ // load time branching for fastest keydown performance
24549         if(Roo.isIE){
24550             return function(e){
24551                 var k = e.getKey(), r;
24552                 if(k == e.TAB){
24553                     e.stopEvent();
24554                     r = this.doc.selection.createRange();
24555                     if(r){
24556                         r.collapse(true);
24557                         r.pasteHTML('&#160;&#160;&#160;&#160;');
24558                         this.deferFocus();
24559                     }
24560                     return;
24561                 }
24562                 
24563                 if(k == e.ENTER){
24564                     r = this.doc.selection.createRange();
24565                     if(r){
24566                         var target = r.parentElement();
24567                         if(!target || target.tagName.toLowerCase() != 'li'){
24568                             e.stopEvent();
24569                             r.pasteHTML('<br />');
24570                             r.collapse(false);
24571                             r.select();
24572                         }
24573                     }
24574                 }
24575                 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
24576                     this.cleanUpPaste.defer(100, this);
24577                     return;
24578                 }
24579                 
24580                 
24581             };
24582         }else if(Roo.isOpera){
24583             return function(e){
24584                 var k = e.getKey();
24585                 if(k == e.TAB){
24586                     e.stopEvent();
24587                     this.win.focus();
24588                     this.execCmd('InsertHTML','&#160;&#160;&#160;&#160;');
24589                     this.deferFocus();
24590                 }
24591                 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
24592                     this.cleanUpPaste.defer(100, this);
24593                     return;
24594                 }
24595                 
24596             };
24597         }else if(Roo.isSafari){
24598             return function(e){
24599                 var k = e.getKey();
24600                 
24601                 if(k == e.TAB){
24602                     e.stopEvent();
24603                     this.execCmd('InsertText','\t');
24604                     this.deferFocus();
24605                     return;
24606                 }
24607                if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
24608                     this.cleanUpPaste.defer(100, this);
24609                     return;
24610                 }
24611                 
24612              };
24613         }
24614     }(),
24615     
24616     getAllAncestors: function()
24617     {
24618         var p = this.getSelectedNode();
24619         var a = [];
24620         if (!p) {
24621             a.push(p); // push blank onto stack..
24622             p = this.getParentElement();
24623         }
24624         
24625         
24626         while (p && (p.nodeType == 1) && (p.tagName.toLowerCase() != 'body')) {
24627             a.push(p);
24628             p = p.parentNode;
24629         }
24630         a.push(this.doc.body);
24631         return a;
24632     },
24633     lastSel : false,
24634     lastSelNode : false,
24635     
24636     
24637     getSelection : function() 
24638     {
24639         this.assignDocWin();
24640         return Roo.isIE ? this.doc.selection : this.win.getSelection();
24641     },
24642     
24643     getSelectedNode: function() 
24644     {
24645         // this may only work on Gecko!!!
24646         
24647         // should we cache this!!!!
24648         
24649         
24650         
24651          
24652         var range = this.createRange(this.getSelection());
24653         
24654         if (Roo.isIE) {
24655             var parent = range.parentElement();
24656             while (true) {
24657                 var testRange = range.duplicate();
24658                 testRange.moveToElementText(parent);
24659                 if (testRange.inRange(range)) {
24660                     break;
24661                 }
24662                 if ((parent.nodeType != 1) || (parent.tagName.toLowerCase() == 'body')) {
24663                     break;
24664                 }
24665                 parent = parent.parentElement;
24666             }
24667             return parent;
24668         }
24669         
24670         
24671         var ar = range.endContainer.childNodes;
24672         if (!ar.length) {
24673             ar = range.commonAncestorContainer.childNodes;
24674             //alert(ar.length);
24675         }
24676         var nodes = [];
24677         var other_nodes = [];
24678         var has_other_nodes = false;
24679         for (var i=0;i<ar.length;i++) {
24680             if ((ar[i].nodeType == 3) && (!ar[i].data.length)) { // empty text ? 
24681                 continue;
24682             }
24683             // fullly contained node.
24684             
24685             if (this.rangeIntersectsNode(range,ar[i]) && this.rangeCompareNode(range,ar[i]) == 3) {
24686                 nodes.push(ar[i]);
24687                 continue;
24688             }
24689             
24690             // probably selected..
24691             if ((ar[i].nodeType == 1) && this.rangeIntersectsNode(range,ar[i]) && (this.rangeCompareNode(range,ar[i]) > 0)) {
24692                 other_nodes.push(ar[i]);
24693                 continue;
24694             }
24695             if (!this.rangeIntersectsNode(range,ar[i])|| (this.rangeCompareNode(range,ar[i]) == 0))  {
24696                 continue;
24697             }
24698             
24699             
24700             has_other_nodes = true;
24701         }
24702         if (!nodes.length && other_nodes.length) {
24703             nodes= other_nodes;
24704         }
24705         if (has_other_nodes || !nodes.length || (nodes.length > 1)) {
24706             return false;
24707         }
24708         
24709         return nodes[0];
24710     },
24711     createRange: function(sel)
24712     {
24713         // this has strange effects when using with 
24714         // top toolbar - not sure if it's a great idea.
24715         //this.editor.contentWindow.focus();
24716         if (typeof sel != "undefined") {
24717             try {
24718                 return sel.getRangeAt ? sel.getRangeAt(0) : sel.createRange();
24719             } catch(e) {
24720                 return this.doc.createRange();
24721             }
24722         } else {
24723             return this.doc.createRange();
24724         }
24725     },
24726     getParentElement: function()
24727     {
24728         
24729         this.assignDocWin();
24730         var sel = Roo.isIE ? this.doc.selection : this.win.getSelection();
24731         
24732         var range = this.createRange(sel);
24733          
24734         try {
24735             var p = range.commonAncestorContainer;
24736             while (p.nodeType == 3) { // text node
24737                 p = p.parentNode;
24738             }
24739             return p;
24740         } catch (e) {
24741             return null;
24742         }
24743     
24744     },
24745     
24746     
24747     
24748     // BC Hacks - cause I cant work out what i was trying to do..
24749     rangeIntersectsNode : function(range, node)
24750     {
24751         var nodeRange = node.ownerDocument.createRange();
24752         try {
24753             nodeRange.selectNode(node);
24754         }
24755         catch (e) {
24756             nodeRange.selectNodeContents(node);
24757         }
24758
24759         return range.compareBoundaryPoints(Range.END_TO_START, nodeRange) == -1 &&
24760                  range.compareBoundaryPoints(Range.START_TO_END, nodeRange) == 1;
24761     },
24762     rangeCompareNode : function(range, node) {
24763         var nodeRange = node.ownerDocument.createRange();
24764         try {
24765             nodeRange.selectNode(node);
24766         } catch (e) {
24767             nodeRange.selectNodeContents(node);
24768         }
24769         var nodeIsBefore = range.compareBoundaryPoints(Range.START_TO_START, nodeRange) == 1;
24770         var nodeIsAfter = range.compareBoundaryPoints(Range.END_TO_END, nodeRange) == -1;
24771
24772         if (nodeIsBefore && !nodeIsAfter)
24773             return 0;
24774         if (!nodeIsBefore && nodeIsAfter)
24775             return 1;
24776         if (nodeIsBefore && nodeIsAfter)
24777             return 2;
24778
24779         return 3;
24780     },
24781
24782     // private? - in a new class?
24783     cleanUpPaste :  function()
24784     {
24785         // cleans up the whole document..
24786       //  console.log('cleanuppaste');
24787         this.cleanUpChildren(this.doc.body);
24788         
24789         
24790     },
24791     cleanUpChildren : function (n)
24792     {
24793         if (!n.childNodes.length) {
24794             return;
24795         }
24796         for (var i = n.childNodes.length-1; i > -1 ; i--) {
24797            this.cleanUpChild(n.childNodes[i]);
24798         }
24799     },
24800     
24801     
24802         
24803     
24804     cleanUpChild : function (node)
24805     {
24806         //console.log(node);
24807         if (node.nodeName == "#text") {
24808             // clean up silly Windows -- stuff?
24809             return; 
24810         }
24811         if (node.nodeName == "#comment") {
24812             node.parentNode.removeChild(node);
24813             // clean up silly Windows -- stuff?
24814             return; 
24815         }
24816         
24817         if (Roo.form.HtmlEditor.black.indexOf(node.tagName.toLowerCase()) > -1) {
24818             // remove node.
24819             node.parentNode.removeChild(node);
24820             return;
24821             
24822         }
24823         if (!node.attributes || !node.attributes.length) {
24824             this.cleanUpChildren(node);
24825             return;
24826         }
24827         
24828         function cleanAttr(n,v)
24829         {
24830             
24831             if (v.match(/^\./) || v.match(/^\//)) {
24832                 return;
24833             }
24834             if (v.match(/^(http|https):\/\//) || v.match(/^mailto:/)) {
24835                 return;
24836             }
24837             Roo.log("(REMOVE)"+ node.tagName +'.' + n + '=' + v);
24838             node.removeAttribute(n);
24839             
24840         }
24841         
24842         function cleanStyle(n,v)
24843         {
24844             if (v.match(/expression/)) { //XSS?? should we even bother..
24845                 node.removeAttribute(n);
24846                 return;
24847             }
24848             
24849             
24850             var parts = v.split(/;/);
24851             Roo.each(parts, function(p) {
24852                 p = p.replace(/\s+/g,'');
24853                 if (!p.length) {
24854                     return;
24855                 }
24856                 var l = p.split(':').shift().replace(/\s+/g,'');
24857                 
24858                 if (Roo.form.HtmlEditor.cwhite.indexOf(l) < 0) {
24859                     Roo.log('(REMOVE)' + node.tagName +'.' + n + ':'+l + '=' + v);
24860                     node.removeAttribute(n);
24861                     return false;
24862                 }
24863             });
24864             
24865             
24866         }
24867         
24868         
24869         for (var i = node.attributes.length-1; i > -1 ; i--) {
24870             var a = node.attributes[i];
24871             //console.log(a);
24872             if (Roo.form.HtmlEditor.ablack.indexOf(a.name.toLowerCase()) > -1) {
24873                 node.removeAttribute(a.name);
24874                 return;
24875             }
24876             if (Roo.form.HtmlEditor.aclean.indexOf(a.name.toLowerCase()) > -1) {
24877                 cleanAttr(a.name,a.value); // fixme..
24878                 return;
24879             }
24880             if (a.name == 'style') {
24881                 cleanStyle(a.name,a.value);
24882             }
24883             /// clean up MS crap..
24884             if (a.name == 'class') {
24885                 if (a.value.match(/^Mso/)) {
24886                     node.className = '';
24887                 }
24888             }
24889             
24890             // style cleanup!?
24891             // class cleanup?
24892             
24893         }
24894         
24895         
24896         this.cleanUpChildren(node);
24897         
24898         
24899     }
24900     
24901     
24902     // hide stuff that is not compatible
24903     /**
24904      * @event blur
24905      * @hide
24906      */
24907     /**
24908      * @event change
24909      * @hide
24910      */
24911     /**
24912      * @event focus
24913      * @hide
24914      */
24915     /**
24916      * @event specialkey
24917      * @hide
24918      */
24919     /**
24920      * @cfg {String} fieldClass @hide
24921      */
24922     /**
24923      * @cfg {String} focusClass @hide
24924      */
24925     /**
24926      * @cfg {String} autoCreate @hide
24927      */
24928     /**
24929      * @cfg {String} inputType @hide
24930      */
24931     /**
24932      * @cfg {String} invalidClass @hide
24933      */
24934     /**
24935      * @cfg {String} invalidText @hide
24936      */
24937     /**
24938      * @cfg {String} msgFx @hide
24939      */
24940     /**
24941      * @cfg {String} validateOnBlur @hide
24942      */
24943 });
24944
24945 Roo.form.HtmlEditor.white = [
24946         'area', 'br', 'img', 'input', 'hr', 'wbr',
24947         
24948        'address', 'blockquote', 'center', 'dd',      'dir',       'div', 
24949        'dl',      'dt',         'h1',     'h2',      'h3',        'h4', 
24950        'h5',      'h6',         'hr',     'isindex', 'listing',   'marquee', 
24951        'menu',    'multicol',   'ol',     'p',       'plaintext', 'pre', 
24952        'table',   'ul',         'xmp', 
24953        
24954        'caption', 'col', 'colgroup', 'tbody', 'td', 'tfoot', 'th', 
24955       'thead',   'tr', 
24956      
24957       'dir', 'menu', 'ol', 'ul', 'dl',
24958        
24959       'embed',  'object'
24960 ];
24961
24962
24963 Roo.form.HtmlEditor.black = [
24964     //    'embed',  'object', // enable - backend responsiblity to clean thiese
24965         'applet', // 
24966         'base',   'basefont', 'bgsound', 'blink',  'body', 
24967         'frame',  'frameset', 'head',    'html',   'ilayer', 
24968         'iframe', 'layer',  'link',     'meta',    'object',   
24969         'script', 'style' ,'title',  'xml' // clean later..
24970 ];
24971 Roo.form.HtmlEditor.clean = [
24972     'script', 'style', 'title', 'xml'
24973 ];
24974
24975 // attributes..
24976
24977 Roo.form.HtmlEditor.ablack = [
24978     'on'
24979 ];
24980     
24981 Roo.form.HtmlEditor.aclean = [ 
24982     'action', 'background', 'codebase', 'dynsrc', 'href', 'lowsrc'
24983 ];
24984
24985 // protocols..
24986 Roo.form.HtmlEditor.pwhite= [
24987         'http',  'https',  'mailto'
24988 ];
24989
24990 Roo.form.HtmlEditor.cwhite= [
24991         'text-align',
24992         'font-size'
24993 ];
24994
24995 // <script type="text/javascript">
24996 /*
24997  * Based on
24998  * Ext JS Library 1.1.1
24999  * Copyright(c) 2006-2007, Ext JS, LLC.
25000  *  
25001  
25002  */
25003
25004 /**
25005  * @class Roo.form.HtmlEditorToolbar1
25006  * Basic Toolbar
25007  * 
25008  * Usage:
25009  *
25010  new Roo.form.HtmlEditor({
25011     ....
25012     toolbars : [
25013         new Roo.form.HtmlEditorToolbar1({
25014             disable : { fonts: 1 , format: 1, ..., ... , ...],
25015             btns : [ .... ]
25016         })
25017     }
25018      
25019  * 
25020  * @cfg {Object} disable List of elements to disable..
25021  * @cfg {Array} btns List of additional buttons.
25022  * 
25023  * 
25024  * NEEDS Extra CSS? 
25025  * .x-html-editor-tb .x-edit-none .x-btn-text { background: none; }
25026  */
25027  
25028 Roo.form.HtmlEditor.ToolbarStandard = function(config)
25029 {
25030     
25031     Roo.apply(this, config);
25032     //Roo.form.HtmlEditorToolbar1.superclass.constructor.call(this, editor.wrap.dom.firstChild, [], config);
25033     // dont call parent... till later.
25034 }
25035
25036 Roo.apply(Roo.form.HtmlEditor.ToolbarStandard.prototype,  {
25037     
25038     tb: false,
25039     
25040     rendered: false,
25041     
25042     editor : false,
25043     /**
25044      * @cfg {Object} disable  List of toolbar elements to disable
25045          
25046      */
25047     disable : false,
25048       /**
25049      * @cfg {Array} fontFamilies An array of available font families
25050      */
25051     fontFamilies : [
25052         'Arial',
25053         'Courier New',
25054         'Tahoma',
25055         'Times New Roman',
25056         'Verdana'
25057     ],
25058     
25059     specialChars : [
25060            "&#169;",
25061           "&#174;",     
25062           "&#8482;",    
25063           "&#163;" ,    
25064          // "&#8212;",    
25065           "&#8230;",    
25066           "&#247;" ,    
25067         //  "&#225;" ,     ?? a acute?
25068            "&#8364;"    , //Euro
25069        //   "&#8220;"    ,
25070         //  "&#8221;"    ,
25071         //  "&#8226;"    ,
25072           "&#176;"  //   , // degrees
25073
25074          // "&#233;"     , // e ecute
25075          // "&#250;"     , // u ecute?
25076     ],
25077     inputElements : [ 
25078             "form", "input:text", "input:hidden", "input:checkbox", "input:radio", "input:password", 
25079             "input:submit", "input:button", "select", "textarea", "label" ],
25080     formats : [
25081         ["p"] ,  
25082         ["h1"],["h2"],["h3"],["h4"],["h5"],["h6"], 
25083         ["pre"],[ "code"], 
25084         ["abbr"],[ "acronym"],[ "address"],[ "cite"],[ "samp"],[ "var"]
25085     ],
25086      /**
25087      * @cfg {String} defaultFont default font to use.
25088      */
25089     defaultFont: 'tahoma',
25090    
25091     fontSelect : false,
25092     
25093     
25094     formatCombo : false,
25095     
25096     init : function(editor)
25097     {
25098         this.editor = editor;
25099         
25100         
25101         var fid = editor.frameId;
25102         var etb = this;
25103         function btn(id, toggle, handler){
25104             var xid = fid + '-'+ id ;
25105             return {
25106                 id : xid,
25107                 cmd : id,
25108                 cls : 'x-btn-icon x-edit-'+id,
25109                 enableToggle:toggle !== false,
25110                 scope: editor, // was editor...
25111                 handler:handler||editor.relayBtnCmd,
25112                 clickEvent:'mousedown',
25113                 tooltip: etb.buttonTips[id] || undefined, ///tips ???
25114                 tabIndex:-1
25115             };
25116         }
25117         
25118         
25119         
25120         var tb = new Roo.Toolbar(editor.wrap.dom.firstChild);
25121         this.tb = tb;
25122          // stop form submits
25123         tb.el.on('click', function(e){
25124             e.preventDefault(); // what does this do?
25125         });
25126
25127         if(!this.disable.font && !Roo.isSafari){
25128             /* why no safari for fonts
25129             editor.fontSelect = tb.el.createChild({
25130                 tag:'select',
25131                 tabIndex: -1,
25132                 cls:'x-font-select',
25133                 html: editor.createFontOptions()
25134             });
25135             editor.fontSelect.on('change', function(){
25136                 var font = editor.fontSelect.dom.value;
25137                 editor.relayCmd('fontname', font);
25138                 editor.deferFocus();
25139             }, editor);
25140             tb.add(
25141                 editor.fontSelect.dom,
25142                 '-'
25143             );
25144             */
25145         };
25146         if(!this.disable.formats){
25147             this.formatCombo = new Roo.form.ComboBox({
25148                 store: new Roo.data.SimpleStore({
25149                     id : 'tag',
25150                     fields: ['tag'],
25151                     data : this.formats // from states.js
25152                 }),
25153                 blockFocus : true,
25154                 //autoCreate : {tag: "div",  size: "20"},
25155                 displayField:'tag',
25156                 typeAhead: false,
25157                 mode: 'local',
25158                 editable : false,
25159                 triggerAction: 'all',
25160                 emptyText:'Add tag',
25161                 selectOnFocus:true,
25162                 width:135,
25163                 listeners : {
25164                     'select': function(c, r, i) {
25165                         editor.insertTag(r.get('tag'));
25166                         editor.focus();
25167                     }
25168                 }
25169
25170             });
25171             tb.addField(this.formatCombo);
25172             
25173         }
25174         
25175         if(!this.disable.format){
25176             tb.add(
25177                 btn('bold'),
25178                 btn('italic'),
25179                 btn('underline')
25180             );
25181         };
25182         if(!this.disable.fontSize){
25183             tb.add(
25184                 '-',
25185                 
25186                 
25187                 btn('increasefontsize', false, editor.adjustFont),
25188                 btn('decreasefontsize', false, editor.adjustFont)
25189             );
25190         };
25191         
25192         
25193         if(this.disable.colors){
25194             tb.add(
25195                 '-', {
25196                     id:editor.frameId +'-forecolor',
25197                     cls:'x-btn-icon x-edit-forecolor',
25198                     clickEvent:'mousedown',
25199                     tooltip: this.buttonTips['forecolor'] || undefined,
25200                     tabIndex:-1,
25201                     menu : new Roo.menu.ColorMenu({
25202                         allowReselect: true,
25203                         focus: Roo.emptyFn,
25204                         value:'000000',
25205                         plain:true,
25206                         selectHandler: function(cp, color){
25207                             editor.execCmd('forecolor', Roo.isSafari || Roo.isIE ? '#'+color : color);
25208                             editor.deferFocus();
25209                         },
25210                         scope: editor,
25211                         clickEvent:'mousedown'
25212                     })
25213                 }, {
25214                     id:editor.frameId +'backcolor',
25215                     cls:'x-btn-icon x-edit-backcolor',
25216                     clickEvent:'mousedown',
25217                     tooltip: this.buttonTips['backcolor'] || undefined,
25218                     tabIndex:-1,
25219                     menu : new Roo.menu.ColorMenu({
25220                         focus: Roo.emptyFn,
25221                         value:'FFFFFF',
25222                         plain:true,
25223                         allowReselect: true,
25224                         selectHandler: function(cp, color){
25225                             if(Roo.isGecko){
25226                                 editor.execCmd('useCSS', false);
25227                                 editor.execCmd('hilitecolor', color);
25228                                 editor.execCmd('useCSS', true);
25229                                 editor.deferFocus();
25230                             }else{
25231                                 editor.execCmd(Roo.isOpera ? 'hilitecolor' : 'backcolor', 
25232                                     Roo.isSafari || Roo.isIE ? '#'+color : color);
25233                                 editor.deferFocus();
25234                             }
25235                         },
25236                         scope:editor,
25237                         clickEvent:'mousedown'
25238                     })
25239                 }
25240             );
25241         };
25242         // now add all the items...
25243         
25244
25245         if(!this.disable.alignments){
25246             tb.add(
25247                 '-',
25248                 btn('justifyleft'),
25249                 btn('justifycenter'),
25250                 btn('justifyright')
25251             );
25252         };
25253
25254         //if(!Roo.isSafari){
25255             if(!this.disable.links){
25256                 tb.add(
25257                     '-',
25258                     btn('createlink', false, editor.createLink)    /// MOVE TO HERE?!!?!?!?!
25259                 );
25260             };
25261
25262             if(!this.disable.lists){
25263                 tb.add(
25264                     '-',
25265                     btn('insertorderedlist'),
25266                     btn('insertunorderedlist')
25267                 );
25268             }
25269             if(!this.disable.sourceEdit){
25270                 tb.add(
25271                     '-',
25272                     btn('sourceedit', true, function(btn){
25273                         this.toggleSourceEdit(btn.pressed);
25274                     })
25275                 );
25276             }
25277         //}
25278         
25279         var smenu = { };
25280         // special menu.. - needs to be tidied up..
25281         if (!this.disable.special) {
25282             smenu = {
25283                 text: "&#169;",
25284                 cls: 'x-edit-none',
25285                 menu : {
25286                     items : []
25287                    }
25288             };
25289             for (var i =0; i < this.specialChars.length; i++) {
25290                 smenu.menu.items.push({
25291                     
25292                     html: this.specialChars[i],
25293                     handler: function(a,b) {
25294                         editor.insertAtCursor(String.fromCharCode(a.html.replace('&#','').replace(';', '')));
25295                         
25296                     },
25297                     tabIndex:-1
25298                 });
25299             }
25300             
25301             
25302             tb.add(smenu);
25303             
25304             
25305         }
25306         if (this.btns) {
25307             for(var i =0; i< this.btns.length;i++) {
25308                 var b = this.btns[i];
25309                 b.cls =  'x-edit-none';
25310                 b.scope = editor;
25311                 tb.add(b);
25312             }
25313         
25314         }
25315         
25316         
25317         
25318         // disable everything...
25319         
25320         this.tb.items.each(function(item){
25321            if(item.id != editor.frameId+ '-sourceedit'){
25322                 item.disable();
25323             }
25324         });
25325         this.rendered = true;
25326         
25327         // the all the btns;
25328         editor.on('editorevent', this.updateToolbar, this);
25329         // other toolbars need to implement this..
25330         //editor.on('editmodechange', this.updateToolbar, this);
25331     },
25332     
25333     
25334     
25335     /**
25336      * Protected method that will not generally be called directly. It triggers
25337      * a toolbar update by reading the markup state of the current selection in the editor.
25338      */
25339     updateToolbar: function(){
25340
25341         if(!this.editor.activated){
25342             this.editor.onFirstFocus();
25343             return;
25344         }
25345
25346         var btns = this.tb.items.map, 
25347             doc = this.editor.doc,
25348             frameId = this.editor.frameId;
25349
25350         if(!this.disable.font && !Roo.isSafari){
25351             /*
25352             var name = (doc.queryCommandValue('FontName')||this.editor.defaultFont).toLowerCase();
25353             if(name != this.fontSelect.dom.value){
25354                 this.fontSelect.dom.value = name;
25355             }
25356             */
25357         }
25358         if(!this.disable.format){
25359             btns[frameId + '-bold'].toggle(doc.queryCommandState('bold'));
25360             btns[frameId + '-italic'].toggle(doc.queryCommandState('italic'));
25361             btns[frameId + '-underline'].toggle(doc.queryCommandState('underline'));
25362         }
25363         if(!this.disable.alignments){
25364             btns[frameId + '-justifyleft'].toggle(doc.queryCommandState('justifyleft'));
25365             btns[frameId + '-justifycenter'].toggle(doc.queryCommandState('justifycenter'));
25366             btns[frameId + '-justifyright'].toggle(doc.queryCommandState('justifyright'));
25367         }
25368         if(!Roo.isSafari && !this.disable.lists){
25369             btns[frameId + '-insertorderedlist'].toggle(doc.queryCommandState('insertorderedlist'));
25370             btns[frameId + '-insertunorderedlist'].toggle(doc.queryCommandState('insertunorderedlist'));
25371         }
25372         
25373         var ans = this.editor.getAllAncestors();
25374         if (this.formatCombo) {
25375             
25376             
25377             var store = this.formatCombo.store;
25378             this.formatCombo.setValue("");
25379             for (var i =0; i < ans.length;i++) {
25380                 if (ans[i] && store.query('tag',ans[i].tagName.toLowerCase(), false).length) {
25381                     // select it..
25382                     this.formatCombo.setValue(ans[i].tagName.toLowerCase());
25383                     break;
25384                 }
25385             }
25386         }
25387         
25388         
25389         
25390         // hides menus... - so this cant be on a menu...
25391         Roo.menu.MenuMgr.hideAll();
25392
25393         //this.editorsyncValue();
25394     },
25395    
25396     
25397     createFontOptions : function(){
25398         var buf = [], fs = this.fontFamilies, ff, lc;
25399         for(var i = 0, len = fs.length; i< len; i++){
25400             ff = fs[i];
25401             lc = ff.toLowerCase();
25402             buf.push(
25403                 '<option value="',lc,'" style="font-family:',ff,';"',
25404                     (this.defaultFont == lc ? ' selected="true">' : '>'),
25405                     ff,
25406                 '</option>'
25407             );
25408         }
25409         return buf.join('');
25410     },
25411     
25412     toggleSourceEdit : function(sourceEditMode){
25413         if(sourceEditMode === undefined){
25414             sourceEditMode = !this.sourceEditMode;
25415         }
25416         this.sourceEditMode = sourceEditMode === true;
25417         var btn = this.tb.items.get(this.editor.frameId +'-sourceedit');
25418         // just toggle the button?
25419         if(btn.pressed !== this.editor.sourceEditMode){
25420             btn.toggle(this.editor.sourceEditMode);
25421             return;
25422         }
25423         
25424         if(this.sourceEditMode){
25425             this.tb.items.each(function(item){
25426                 if(item.cmd != 'sourceedit'){
25427                     item.disable();
25428                 }
25429             });
25430           
25431         }else{
25432             if(this.initialized){
25433                 this.tb.items.each(function(item){
25434                     item.enable();
25435                 });
25436             }
25437             
25438         }
25439         // tell the editor that it's been pressed..
25440         this.editor.toggleSourceEdit(sourceEditMode);
25441        
25442     },
25443      /**
25444      * Object collection of toolbar tooltips for the buttons in the editor. The key
25445      * is the command id associated with that button and the value is a valid QuickTips object.
25446      * For example:
25447 <pre><code>
25448 {
25449     bold : {
25450         title: 'Bold (Ctrl+B)',
25451         text: 'Make the selected text bold.',
25452         cls: 'x-html-editor-tip'
25453     },
25454     italic : {
25455         title: 'Italic (Ctrl+I)',
25456         text: 'Make the selected text italic.',
25457         cls: 'x-html-editor-tip'
25458     },
25459     ...
25460 </code></pre>
25461     * @type Object
25462      */
25463     buttonTips : {
25464         bold : {
25465             title: 'Bold (Ctrl+B)',
25466             text: 'Make the selected text bold.',
25467             cls: 'x-html-editor-tip'
25468         },
25469         italic : {
25470             title: 'Italic (Ctrl+I)',
25471             text: 'Make the selected text italic.',
25472             cls: 'x-html-editor-tip'
25473         },
25474         underline : {
25475             title: 'Underline (Ctrl+U)',
25476             text: 'Underline the selected text.',
25477             cls: 'x-html-editor-tip'
25478         },
25479         increasefontsize : {
25480             title: 'Grow Text',
25481             text: 'Increase the font size.',
25482             cls: 'x-html-editor-tip'
25483         },
25484         decreasefontsize : {
25485             title: 'Shrink Text',
25486             text: 'Decrease the font size.',
25487             cls: 'x-html-editor-tip'
25488         },
25489         backcolor : {
25490             title: 'Text Highlight Color',
25491             text: 'Change the background color of the selected text.',
25492             cls: 'x-html-editor-tip'
25493         },
25494         forecolor : {
25495             title: 'Font Color',
25496             text: 'Change the color of the selected text.',
25497             cls: 'x-html-editor-tip'
25498         },
25499         justifyleft : {
25500             title: 'Align Text Left',
25501             text: 'Align text to the left.',
25502             cls: 'x-html-editor-tip'
25503         },
25504         justifycenter : {
25505             title: 'Center Text',
25506             text: 'Center text in the editor.',
25507             cls: 'x-html-editor-tip'
25508         },
25509         justifyright : {
25510             title: 'Align Text Right',
25511             text: 'Align text to the right.',
25512             cls: 'x-html-editor-tip'
25513         },
25514         insertunorderedlist : {
25515             title: 'Bullet List',
25516             text: 'Start a bulleted list.',
25517             cls: 'x-html-editor-tip'
25518         },
25519         insertorderedlist : {
25520             title: 'Numbered List',
25521             text: 'Start a numbered list.',
25522             cls: 'x-html-editor-tip'
25523         },
25524         createlink : {
25525             title: 'Hyperlink',
25526             text: 'Make the selected text a hyperlink.',
25527             cls: 'x-html-editor-tip'
25528         },
25529         sourceedit : {
25530             title: 'Source Edit',
25531             text: 'Switch to source editing mode.',
25532             cls: 'x-html-editor-tip'
25533         }
25534     },
25535     // private
25536     onDestroy : function(){
25537         if(this.rendered){
25538             
25539             this.tb.items.each(function(item){
25540                 if(item.menu){
25541                     item.menu.removeAll();
25542                     if(item.menu.el){
25543                         item.menu.el.destroy();
25544                     }
25545                 }
25546                 item.destroy();
25547             });
25548              
25549         }
25550     },
25551     onFirstFocus: function() {
25552         this.tb.items.each(function(item){
25553            item.enable();
25554         });
25555     }
25556 });
25557
25558
25559
25560
25561 // <script type="text/javascript">
25562 /*
25563  * Based on
25564  * Ext JS Library 1.1.1
25565  * Copyright(c) 2006-2007, Ext JS, LLC.
25566  *  
25567  
25568  */
25569
25570  
25571 /**
25572  * @class Roo.form.HtmlEditor.ToolbarContext
25573  * Context Toolbar
25574  * 
25575  * Usage:
25576  *
25577  new Roo.form.HtmlEditor({
25578     ....
25579     toolbars : [
25580         new Roo.form.HtmlEditor.ToolbarStandard(),
25581         new Roo.form.HtmlEditor.ToolbarContext()
25582         })
25583     }
25584      
25585  * 
25586  * @config : {Object} disable List of elements to disable.. (not done yet.)
25587  * 
25588  * 
25589  */
25590
25591 Roo.form.HtmlEditor.ToolbarContext = function(config)
25592 {
25593     
25594     Roo.apply(this, config);
25595     //Roo.form.HtmlEditorToolbar1.superclass.constructor.call(this, editor.wrap.dom.firstChild, [], config);
25596     // dont call parent... till later.
25597 }
25598 Roo.form.HtmlEditor.ToolbarContext.types = {
25599     'IMG' : {
25600         width : {
25601             title: "Width",
25602             width: 40
25603         },
25604         height:  {
25605             title: "Height",
25606             width: 40
25607         },
25608         align: {
25609             title: "Align",
25610             opts : [ [""],[ "left"],[ "right"],[ "center"],[ "top"]],
25611             width : 80
25612             
25613         },
25614         border: {
25615             title: "Border",
25616             width: 40
25617         },
25618         alt: {
25619             title: "Alt",
25620             width: 120
25621         },
25622         src : {
25623             title: "Src",
25624             width: 220
25625         }
25626         
25627     },
25628     'A' : {
25629         name : {
25630             title: "Name",
25631             width: 50
25632         },
25633         href:  {
25634             title: "Href",
25635             width: 220
25636         } // border?
25637         
25638     },
25639     'TABLE' : {
25640         rows : {
25641             title: "Rows",
25642             width: 20
25643         },
25644         cols : {
25645             title: "Cols",
25646             width: 20
25647         },
25648         width : {
25649             title: "Width",
25650             width: 40
25651         },
25652         height : {
25653             title: "Height",
25654             width: 40
25655         },
25656         border : {
25657             title: "Border",
25658             width: 20
25659         }
25660     },
25661     'TD' : {
25662         width : {
25663             title: "Width",
25664             width: 40
25665         },
25666         height : {
25667             title: "Height",
25668             width: 40
25669         },   
25670         align: {
25671             title: "Align",
25672             opts : [[""],[ "left"],[ "center"],[ "right"],[ "justify"],[ "char"]],
25673             width: 40
25674         },
25675         valign: {
25676             title: "Valign",
25677             opts : [[""],[ "top"],[ "middle"],[ "bottom"],[ "baseline"]],
25678             width: 40
25679         },
25680         colspan: {
25681             title: "Colspan",
25682             width: 20
25683             
25684         }
25685     },
25686     'INPUT' : {
25687         name : {
25688             title: "name",
25689             width: 120
25690         },
25691         value : {
25692             title: "Value",
25693             width: 120
25694         },
25695         width : {
25696             title: "Width",
25697             width: 40
25698         }
25699     },
25700     'LABEL' : {
25701         'for' : {
25702             title: "For",
25703             width: 120
25704         }
25705     },
25706     'TEXTAREA' : {
25707           name : {
25708             title: "name",
25709             width: 120
25710         },
25711         rows : {
25712             title: "Rows",
25713             width: 20
25714         },
25715         cols : {
25716             title: "Cols",
25717             width: 20
25718         }
25719     },
25720     'SELECT' : {
25721         name : {
25722             title: "name",
25723             width: 120
25724         },
25725         selectoptions : {
25726             title: "Options",
25727             width: 200
25728         }
25729     },
25730     'BODY' : {
25731         title : {
25732             title: "title",
25733             width: 120,
25734             disabled : true
25735         }
25736     }
25737 };
25738
25739
25740
25741 Roo.apply(Roo.form.HtmlEditor.ToolbarContext.prototype,  {
25742     
25743     tb: false,
25744     
25745     rendered: false,
25746     
25747     editor : false,
25748     /**
25749      * @cfg {Object} disable  List of toolbar elements to disable
25750          
25751      */
25752     disable : false,
25753     
25754     
25755     
25756     toolbars : false,
25757     
25758     init : function(editor)
25759     {
25760         this.editor = editor;
25761         
25762         
25763         var fid = editor.frameId;
25764         var etb = this;
25765         function btn(id, toggle, handler){
25766             var xid = fid + '-'+ id ;
25767             return {
25768                 id : xid,
25769                 cmd : id,
25770                 cls : 'x-btn-icon x-edit-'+id,
25771                 enableToggle:toggle !== false,
25772                 scope: editor, // was editor...
25773                 handler:handler||editor.relayBtnCmd,
25774                 clickEvent:'mousedown',
25775                 tooltip: etb.buttonTips[id] || undefined, ///tips ???
25776                 tabIndex:-1
25777             };
25778         }
25779         // create a new element.
25780         var wdiv = editor.wrap.createChild({
25781                 tag: 'div'
25782             }, editor.wrap.dom.firstChild.nextSibling, true);
25783         
25784         // can we do this more than once??
25785         
25786          // stop form submits
25787       
25788  
25789         // disable everything...
25790         var ty= Roo.form.HtmlEditor.ToolbarContext.types;
25791         this.toolbars = {};
25792            
25793         for (var i in  ty) {
25794           
25795             this.toolbars[i] = this.buildToolbar(ty[i],i);
25796         }
25797         this.tb = this.toolbars.BODY;
25798         this.tb.el.show();
25799         
25800          
25801         this.rendered = true;
25802         
25803         // the all the btns;
25804         editor.on('editorevent', this.updateToolbar, this);
25805         // other toolbars need to implement this..
25806         //editor.on('editmodechange', this.updateToolbar, this);
25807     },
25808     
25809     
25810     
25811     /**
25812      * Protected method that will not generally be called directly. It triggers
25813      * a toolbar update by reading the markup state of the current selection in the editor.
25814      */
25815     updateToolbar: function(){
25816
25817         if(!this.editor.activated){
25818             this.editor.onFirstFocus();
25819             return;
25820         }
25821
25822         
25823         var ans = this.editor.getAllAncestors();
25824         
25825         // pick
25826         var ty= Roo.form.HtmlEditor.ToolbarContext.types;
25827         var sel = ans.length ? (ans[0] ?  ans[0]  : ans[1]) : this.editor.doc.body;
25828         sel = sel ? sel : this.editor.doc.body;
25829         sel = sel.tagName.length ? sel : this.editor.doc.body;
25830         var tn = sel.tagName.toUpperCase();
25831         sel = typeof(ty[tn]) != 'undefined' ? sel : this.editor.doc.body;
25832         tn = sel.tagName.toUpperCase();
25833         if (this.tb.name  == tn) {
25834             return; // no change
25835         }
25836         this.tb.el.hide();
25837         ///console.log("show: " + tn);
25838         this.tb =  this.toolbars[tn];
25839         this.tb.el.show();
25840         this.tb.fields.each(function(e) {
25841             e.setValue(sel.getAttribute(e.name));
25842         });
25843         this.tb.selectedNode = sel;
25844         
25845         
25846         Roo.menu.MenuMgr.hideAll();
25847
25848         //this.editorsyncValue();
25849     },
25850    
25851        
25852     // private
25853     onDestroy : function(){
25854         if(this.rendered){
25855             
25856             this.tb.items.each(function(item){
25857                 if(item.menu){
25858                     item.menu.removeAll();
25859                     if(item.menu.el){
25860                         item.menu.el.destroy();
25861                     }
25862                 }
25863                 item.destroy();
25864             });
25865              
25866         }
25867     },
25868     onFirstFocus: function() {
25869         // need to do this for all the toolbars..
25870         this.tb.items.each(function(item){
25871            item.enable();
25872         });
25873     },
25874     buildToolbar: function(tlist, nm)
25875     {
25876         var editor = this.editor;
25877          // create a new element.
25878         var wdiv = editor.wrap.createChild({
25879                 tag: 'div'
25880             }, editor.wrap.dom.firstChild.nextSibling, true);
25881         
25882        
25883         var tb = new Roo.Toolbar(wdiv);
25884         tb.add(nm+ ":&nbsp;");
25885         for (var i in tlist) {
25886             var item = tlist[i];
25887             tb.add(item.title + ":&nbsp;");
25888             if (item.opts) {
25889                 // fixme
25890                 
25891               
25892                 tb.addField( new Roo.form.ComboBox({
25893                     store: new Roo.data.SimpleStore({
25894                         id : 'val',
25895                         fields: ['val'],
25896                         data : item.opts // from states.js
25897                     }),
25898                     name : i,
25899                     displayField:'val',
25900                     typeAhead: false,
25901                     mode: 'local',
25902                     editable : false,
25903                     triggerAction: 'all',
25904                     emptyText:'Select',
25905                     selectOnFocus:true,
25906                     width: item.width ? item.width  : 130,
25907                     listeners : {
25908                         'select': function(c, r, i) {
25909                             tb.selectedNode.setAttribute(c.name, r.get('val'));
25910                         }
25911                     }
25912
25913                 }));
25914                 continue;
25915                     
25916                 
25917                 
25918                 
25919                 
25920                 tb.addField( new Roo.form.TextField({
25921                     name: i,
25922                     width: 100,
25923                     //allowBlank:false,
25924                     value: ''
25925                 }));
25926                 continue;
25927             }
25928             tb.addField( new Roo.form.TextField({
25929                 name: i,
25930                 width: item.width,
25931                 //allowBlank:true,
25932                 value: '',
25933                 listeners: {
25934                     'change' : function(f, nv, ov) {
25935                         tb.selectedNode.setAttribute(f.name, nv);
25936                     }
25937                 }
25938             }));
25939              
25940         }
25941         tb.el.on('click', function(e){
25942             e.preventDefault(); // what does this do?
25943         });
25944         tb.el.setVisibilityMode( Roo.Element.DISPLAY);
25945         tb.el.hide();
25946         tb.name = nm;
25947         // dont need to disable them... as they will get hidden
25948         return tb;
25949          
25950         
25951     }
25952     
25953     
25954     
25955     
25956 });
25957
25958
25959
25960
25961
25962 /*
25963  * Based on:
25964  * Ext JS Library 1.1.1
25965  * Copyright(c) 2006-2007, Ext JS, LLC.
25966  *
25967  * Originally Released Under LGPL - original licence link has changed is not relivant.
25968  *
25969  * Fork - LGPL
25970  * <script type="text/javascript">
25971  */
25972  
25973 /**
25974  * @class Roo.form.BasicForm
25975  * @extends Roo.util.Observable
25976  * Supplies the functionality to do "actions" on forms and initialize Roo.form.Field types on existing markup.
25977  * @constructor
25978  * @param {String/HTMLElement/Roo.Element} el The form element or its id
25979  * @param {Object} config Configuration options
25980  */
25981 Roo.form.BasicForm = function(el, config){
25982     this.allItems = [];
25983     this.childForms = [];
25984     Roo.apply(this, config);
25985     /*
25986      * The Roo.form.Field items in this form.
25987      * @type MixedCollection
25988      */
25989      
25990      
25991     this.items = new Roo.util.MixedCollection(false, function(o){
25992         return o.id || (o.id = Roo.id());
25993     });
25994     this.addEvents({
25995         /**
25996          * @event beforeaction
25997          * Fires before any action is performed. Return false to cancel the action.
25998          * @param {Form} this
25999          * @param {Action} action The action to be performed
26000          */
26001         beforeaction: true,
26002         /**
26003          * @event actionfailed
26004          * Fires when an action fails.
26005          * @param {Form} this
26006          * @param {Action} action The action that failed
26007          */
26008         actionfailed : true,
26009         /**
26010          * @event actioncomplete
26011          * Fires when an action is completed.
26012          * @param {Form} this
26013          * @param {Action} action The action that completed
26014          */
26015         actioncomplete : true
26016     });
26017     if(el){
26018         this.initEl(el);
26019     }
26020     Roo.form.BasicForm.superclass.constructor.call(this);
26021 };
26022
26023 Roo.extend(Roo.form.BasicForm, Roo.util.Observable, {
26024     /**
26025      * @cfg {String} method
26026      * The request method to use (GET or POST) for form actions if one isn't supplied in the action options.
26027      */
26028     /**
26029      * @cfg {DataReader} reader
26030      * An Roo.data.DataReader (e.g. {@link Roo.data.XmlReader}) to be used to read data when executing "load" actions.
26031      * This is optional as there is built-in support for processing JSON.
26032      */
26033     /**
26034      * @cfg {DataReader} errorReader
26035      * An Roo.data.DataReader (e.g. {@link Roo.data.XmlReader}) to be used to read data when reading validation errors on "submit" actions.
26036      * This is completely optional as there is built-in support for processing JSON.
26037      */
26038     /**
26039      * @cfg {String} url
26040      * The URL to use for form actions if one isn't supplied in the action options.
26041      */
26042     /**
26043      * @cfg {Boolean} fileUpload
26044      * Set to true if this form is a file upload.
26045      */
26046      
26047     /**
26048      * @cfg {Object} baseParams
26049      * Parameters to pass with all requests. e.g. baseParams: {id: '123', foo: 'bar'}.
26050      */
26051      /**
26052      
26053     /**
26054      * @cfg {Number} timeout Timeout for form actions in seconds (default is 30 seconds).
26055      */
26056     timeout: 30,
26057
26058     // private
26059     activeAction : null,
26060
26061     /**
26062      * @cfg {Boolean} trackResetOnLoad If set to true, form.reset() resets to the last loaded
26063      * or setValues() data instead of when the form was first created.
26064      */
26065     trackResetOnLoad : false,
26066     
26067     
26068     /**
26069      * childForms - used for multi-tab forms
26070      * @type {Array}
26071      */
26072     childForms : false,
26073     
26074     /**
26075      * allItems - full list of fields.
26076      * @type {Array}
26077      */
26078     allItems : false,
26079     
26080     /**
26081      * By default wait messages are displayed with Roo.MessageBox.wait. You can target a specific
26082      * element by passing it or its id or mask the form itself by passing in true.
26083      * @type Mixed
26084      */
26085     waitMsgTarget : false,
26086
26087     // private
26088     initEl : function(el){
26089         this.el = Roo.get(el);
26090         this.id = this.el.id || Roo.id();
26091         this.el.on('submit', this.onSubmit, this);
26092         this.el.addClass('x-form');
26093     },
26094
26095     // private
26096     onSubmit : function(e){
26097         e.stopEvent();
26098     },
26099
26100     /**
26101      * Returns true if client-side validation on the form is successful.
26102      * @return Boolean
26103      */
26104     isValid : function(){
26105         var valid = true;
26106         this.items.each(function(f){
26107            if(!f.validate()){
26108                valid = false;
26109            }
26110         });
26111         return valid;
26112     },
26113
26114     /**
26115      * Returns true if any fields in this form have changed since their original load.
26116      * @return Boolean
26117      */
26118     isDirty : function(){
26119         var dirty = false;
26120         this.items.each(function(f){
26121            if(f.isDirty()){
26122                dirty = true;
26123                return false;
26124            }
26125         });
26126         return dirty;
26127     },
26128
26129     /**
26130      * Performs a predefined action (submit or load) or custom actions you define on this form.
26131      * @param {String} actionName The name of the action type
26132      * @param {Object} options (optional) The options to pass to the action.  All of the config options listed
26133      * below are supported by both the submit and load actions unless otherwise noted (custom actions could also
26134      * accept other config options):
26135      * <pre>
26136 Property          Type             Description
26137 ----------------  ---------------  ----------------------------------------------------------------------------------
26138 url               String           The url for the action (defaults to the form's url)
26139 method            String           The form method to use (defaults to the form's method, or POST if not defined)
26140 params            String/Object    The params to pass (defaults to the form's baseParams, or none if not defined)
26141 clientValidation  Boolean          Applies to submit only.  Pass true to call form.isValid() prior to posting to
26142                                    validate the form on the client (defaults to false)
26143      * </pre>
26144      * @return {BasicForm} this
26145      */
26146     doAction : function(action, options){
26147         if(typeof action == 'string'){
26148             action = new Roo.form.Action.ACTION_TYPES[action](this, options);
26149         }
26150         if(this.fireEvent('beforeaction', this, action) !== false){
26151             this.beforeAction(action);
26152             action.run.defer(100, action);
26153         }
26154         return this;
26155     },
26156
26157     /**
26158      * Shortcut to do a submit action.
26159      * @param {Object} options The options to pass to the action (see {@link #doAction} for details)
26160      * @return {BasicForm} this
26161      */
26162     submit : function(options){
26163         this.doAction('submit', options);
26164         return this;
26165     },
26166
26167     /**
26168      * Shortcut to do a load action.
26169      * @param {Object} options The options to pass to the action (see {@link #doAction} for details)
26170      * @return {BasicForm} this
26171      */
26172     load : function(options){
26173         this.doAction('load', options);
26174         return this;
26175     },
26176
26177     /**
26178      * Persists the values in this form into the passed Roo.data.Record object in a beginEdit/endEdit block.
26179      * @param {Record} record The record to edit
26180      * @return {BasicForm} this
26181      */
26182     updateRecord : function(record){
26183         record.beginEdit();
26184         var fs = record.fields;
26185         fs.each(function(f){
26186             var field = this.findField(f.name);
26187             if(field){
26188                 record.set(f.name, field.getValue());
26189             }
26190         }, this);
26191         record.endEdit();
26192         return this;
26193     },
26194
26195     /**
26196      * Loads an Roo.data.Record into this form.
26197      * @param {Record} record The record to load
26198      * @return {BasicForm} this
26199      */
26200     loadRecord : function(record){
26201         this.setValues(record.data);
26202         return this;
26203     },
26204
26205     // private
26206     beforeAction : function(action){
26207         var o = action.options;
26208         
26209        
26210         if(this.waitMsgTarget === true){
26211             this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
26212         }else if(this.waitMsgTarget){
26213             this.waitMsgTarget = Roo.get(this.waitMsgTarget);
26214             this.waitMsgTarget.mask(o.waitMsg || "Sending", 'x-mask-loading');
26215         }else {
26216             Roo.MessageBox.wait(o.waitMsg || "Sending", o.waitTitle || this.waitTitle || 'Please Wait...');
26217         }
26218          
26219     },
26220
26221     // private
26222     afterAction : function(action, success){
26223         this.activeAction = null;
26224         var o = action.options;
26225         
26226         if(this.waitMsgTarget === true){
26227             this.el.unmask();
26228         }else if(this.waitMsgTarget){
26229             this.waitMsgTarget.unmask();
26230         }else{
26231             Roo.MessageBox.updateProgress(1);
26232             Roo.MessageBox.hide();
26233         }
26234          
26235         if(success){
26236             if(o.reset){
26237                 this.reset();
26238             }
26239             Roo.callback(o.success, o.scope, [this, action]);
26240             this.fireEvent('actioncomplete', this, action);
26241             
26242         }else{
26243             Roo.callback(o.failure, o.scope, [this, action]);
26244             // show an error message if no failed handler is set..
26245             if (!this.hasListener('actionfailed')) {
26246                 Roo.MessageBox.alert("Error", "Saving Failed, please check your entries");
26247             }
26248             
26249             this.fireEvent('actionfailed', this, action);
26250         }
26251         
26252     },
26253
26254     /**
26255      * Find a Roo.form.Field in this form by id, dataIndex, name or hiddenName
26256      * @param {String} id The value to search for
26257      * @return Field
26258      */
26259     findField : function(id){
26260         var field = this.items.get(id);
26261         if(!field){
26262             this.items.each(function(f){
26263                 if(f.isFormField && (f.dataIndex == id || f.id == id || f.getName() == id)){
26264                     field = f;
26265                     return false;
26266                 }
26267             });
26268         }
26269         return field || null;
26270     },
26271
26272     /**
26273      * Add a secondary form to this one, 
26274      * Used to provide tabbed forms. One form is primary, with hidden values 
26275      * which mirror the elements from the other forms.
26276      * 
26277      * @param {Roo.form.Form} form to add.
26278      * 
26279      */
26280     addForm : function(form)
26281     {
26282        
26283         if (this.childForms.indexOf(form) > -1) {
26284             // already added..
26285             return;
26286         }
26287         this.childForms.push(form);
26288         var n = '';
26289         Roo.each(form.allItems, function (fe) {
26290             
26291             n = typeof(fe.getName) == 'undefined' ? fe.name : fe.getName();
26292             if (this.findField(n)) { // already added..
26293                 return;
26294             }
26295             var add = new Roo.form.Hidden({
26296                 name : n
26297             });
26298             add.render(this.el);
26299             
26300             this.add( add );
26301         }, this);
26302         
26303     },
26304     /**
26305      * Mark fields in this form invalid in bulk.
26306      * @param {Array/Object} errors Either an array in the form [{id:'fieldId', msg:'The message'},...] or an object hash of {id: msg, id2: msg2}
26307      * @return {BasicForm} this
26308      */
26309     markInvalid : function(errors){
26310         if(errors instanceof Array){
26311             for(var i = 0, len = errors.length; i < len; i++){
26312                 var fieldError = errors[i];
26313                 var f = this.findField(fieldError.id);
26314                 if(f){
26315                     f.markInvalid(fieldError.msg);
26316                 }
26317             }
26318         }else{
26319             var field, id;
26320             for(id in errors){
26321                 if(typeof errors[id] != 'function' && (field = this.findField(id))){
26322                     field.markInvalid(errors[id]);
26323                 }
26324             }
26325         }
26326         Roo.each(this.childForms || [], function (f) {
26327             f.markInvalid(errors);
26328         });
26329         
26330         return this;
26331     },
26332
26333     /**
26334      * Set values for fields in this form in bulk.
26335      * @param {Array/Object} values Either an array in the form [{id:'fieldId', value:'foo'},...] or an object hash of {id: value, id2: value2}
26336      * @return {BasicForm} this
26337      */
26338     setValues : function(values){
26339         if(values instanceof Array){ // array of objects
26340             for(var i = 0, len = values.length; i < len; i++){
26341                 var v = values[i];
26342                 var f = this.findField(v.id);
26343                 if(f){
26344                     f.setValue(v.value);
26345                     if(this.trackResetOnLoad){
26346                         f.originalValue = f.getValue();
26347                     }
26348                 }
26349             }
26350         }else{ // object hash
26351             var field, id;
26352             for(id in values){
26353                 if(typeof values[id] != 'function' && (field = this.findField(id))){
26354                     
26355                     if (field.setFromData && 
26356                         field.valueField && 
26357                         field.displayField &&
26358                         // combos' with local stores can 
26359                         // be queried via setValue()
26360                         // to set their value..
26361                         (field.store && !field.store.isLocal)
26362                         ) {
26363                         // it's a combo
26364                         var sd = { };
26365                         sd[field.valueField] = typeof(values[field.hiddenName]) == 'undefined' ? '' : values[field.hiddenName];
26366                         sd[field.displayField] = typeof(values[field.name]) == 'undefined' ? '' : values[field.name];
26367                         field.setFromData(sd);
26368                         
26369                     } else {
26370                         field.setValue(values[id]);
26371                     }
26372                     
26373                     
26374                     if(this.trackResetOnLoad){
26375                         field.originalValue = field.getValue();
26376                     }
26377                 }
26378             }
26379         }
26380          
26381         Roo.each(this.childForms || [], function (f) {
26382             f.setValues(values);
26383         });
26384                 
26385         return this;
26386     },
26387
26388     /**
26389      * Returns the fields in this form as an object with key/value pairs. If multiple fields exist with the same name
26390      * they are returned as an array.
26391      * @param {Boolean} asString
26392      * @return {Object}
26393      */
26394     getValues : function(asString){
26395         if (this.childForms) {
26396             // copy values from the child forms
26397             Roo.each(this.childForms, function (f) {
26398                 this.setValues(f.getValues());
26399             }, this);
26400         }
26401         
26402         
26403         
26404         var fs = Roo.lib.Ajax.serializeForm(this.el.dom);
26405         if(asString === true){
26406             return fs;
26407         }
26408         return Roo.urlDecode(fs);
26409     },
26410     
26411     /**
26412      * Returns the fields in this form as an object with key/value pairs. 
26413      * This differs from getValues as it calls getValue on each child item, rather than using dom data.
26414      * @return {Object}
26415      */
26416     getFieldValues : function()
26417     {
26418         if (this.childForms) {
26419             // copy values from the child forms
26420             Roo.each(this.childForms, function (f) {
26421                 this.setValues(f.getValues());
26422             }, this);
26423         }
26424         
26425         var ret = {};
26426         this.items.each(function(f){
26427             if (!f.getName()) {
26428                 return;
26429             }
26430             var v = f.getValue();
26431             if ((typeof(v) == 'object') && f.getRawValue) {
26432                 v = f.getRawValue() ; // dates..
26433             }
26434             ret[f.getName()] = v;
26435         });
26436         
26437         return ret;
26438     },
26439
26440     /**
26441      * Clears all invalid messages in this form.
26442      * @return {BasicForm} this
26443      */
26444     clearInvalid : function(){
26445         this.items.each(function(f){
26446            f.clearInvalid();
26447         });
26448         
26449         Roo.each(this.childForms || [], function (f) {
26450             f.clearInvalid();
26451         });
26452         
26453         
26454         return this;
26455     },
26456
26457     /**
26458      * Resets this form.
26459      * @return {BasicForm} this
26460      */
26461     reset : function(){
26462         this.items.each(function(f){
26463             f.reset();
26464         });
26465         
26466         Roo.each(this.childForms || [], function (f) {
26467             f.reset();
26468         });
26469        
26470         
26471         return this;
26472     },
26473
26474     /**
26475      * Add Roo.form components to this form.
26476      * @param {Field} field1
26477      * @param {Field} field2 (optional)
26478      * @param {Field} etc (optional)
26479      * @return {BasicForm} this
26480      */
26481     add : function(){
26482         this.items.addAll(Array.prototype.slice.call(arguments, 0));
26483         return this;
26484     },
26485
26486
26487     /**
26488      * Removes a field from the items collection (does NOT remove its markup).
26489      * @param {Field} field
26490      * @return {BasicForm} this
26491      */
26492     remove : function(field){
26493         this.items.remove(field);
26494         return this;
26495     },
26496
26497     /**
26498      * Looks at the fields in this form, checks them for an id attribute,
26499      * and calls applyTo on the existing dom element with that id.
26500      * @return {BasicForm} this
26501      */
26502     render : function(){
26503         this.items.each(function(f){
26504             if(f.isFormField && !f.rendered && document.getElementById(f.id)){ // if the element exists
26505                 f.applyTo(f.id);
26506             }
26507         });
26508         return this;
26509     },
26510
26511     /**
26512      * Calls {@link Ext#apply} for all fields in this form with the passed object.
26513      * @param {Object} values
26514      * @return {BasicForm} this
26515      */
26516     applyToFields : function(o){
26517         this.items.each(function(f){
26518            Roo.apply(f, o);
26519         });
26520         return this;
26521     },
26522
26523     /**
26524      * Calls {@link Ext#applyIf} for all field in this form with the passed object.
26525      * @param {Object} values
26526      * @return {BasicForm} this
26527      */
26528     applyIfToFields : function(o){
26529         this.items.each(function(f){
26530            Roo.applyIf(f, o);
26531         });
26532         return this;
26533     }
26534 });
26535
26536 // back compat
26537 Roo.BasicForm = Roo.form.BasicForm;/*
26538  * Based on:
26539  * Ext JS Library 1.1.1
26540  * Copyright(c) 2006-2007, Ext JS, LLC.
26541  *
26542  * Originally Released Under LGPL - original licence link has changed is not relivant.
26543  *
26544  * Fork - LGPL
26545  * <script type="text/javascript">
26546  */
26547
26548 /**
26549  * @class Roo.form.Form
26550  * @extends Roo.form.BasicForm
26551  * Adds the ability to dynamically render forms with JavaScript to {@link Roo.form.BasicForm}.
26552  * @constructor
26553  * @param {Object} config Configuration options
26554  */
26555 Roo.form.Form = function(config){
26556     var xitems =  [];
26557     if (config.items) {
26558         xitems = config.items;
26559         delete config.items;
26560     }
26561    
26562     
26563     Roo.form.Form.superclass.constructor.call(this, null, config);
26564     this.url = this.url || this.action;
26565     if(!this.root){
26566         this.root = new Roo.form.Layout(Roo.applyIf({
26567             id: Roo.id()
26568         }, config));
26569     }
26570     this.active = this.root;
26571     /**
26572      * Array of all the buttons that have been added to this form via {@link addButton}
26573      * @type Array
26574      */
26575     this.buttons = [];
26576     this.allItems = [];
26577     this.addEvents({
26578         /**
26579          * @event clientvalidation
26580          * If the monitorValid config option is true, this event fires repetitively to notify of valid state
26581          * @param {Form} this
26582          * @param {Boolean} valid true if the form has passed client-side validation
26583          */
26584         clientvalidation: true,
26585         /**
26586          * @event rendered
26587          * Fires when the form is rendered
26588          * @param {Roo.form.Form} form
26589          */
26590         rendered : true
26591     });
26592     
26593     if (this.progressUrl) {
26594             // push a hidden field onto the list of fields..
26595             this.addxtype( {
26596                     xns: Roo.form, 
26597                     xtype : 'Hidden', 
26598                     name : 'UPLOAD_IDENTIFIER' 
26599             });
26600         }
26601         
26602     
26603     Roo.each(xitems, this.addxtype, this);
26604     
26605     
26606     
26607 };
26608
26609 Roo.extend(Roo.form.Form, Roo.form.BasicForm, {
26610     /**
26611      * @cfg {Number} labelWidth The width of labels. This property cascades to child containers.
26612      */
26613     /**
26614      * @cfg {String} itemCls A css class to apply to the x-form-item of fields. This property cascades to child containers.
26615      */
26616     /**
26617      * @cfg {String} buttonAlign Valid values are "left," "center" and "right" (defaults to "center")
26618      */
26619     buttonAlign:'center',
26620
26621     /**
26622      * @cfg {Number} minButtonWidth Minimum width of all buttons in pixels (defaults to 75)
26623      */
26624     minButtonWidth:75,
26625
26626     /**
26627      * @cfg {String} labelAlign Valid values are "left," "top" and "right" (defaults to "left").
26628      * This property cascades to child containers if not set.
26629      */
26630     labelAlign:'left',
26631
26632     /**
26633      * @cfg {Boolean} monitorValid If true the form monitors its valid state <b>client-side</b> and
26634      * fires a looping event with that state. This is required to bind buttons to the valid
26635      * state using the config value formBind:true on the button.
26636      */
26637     monitorValid : false,
26638
26639     /**
26640      * @cfg {Number} monitorPoll The milliseconds to poll valid state, ignored if monitorValid is not true (defaults to 200)
26641      */
26642     monitorPoll : 200,
26643     
26644     /**
26645      * @cfg {String} progressUrl - Url to return progress data 
26646      */
26647     
26648     progressUrl : false,
26649   
26650     /**
26651      * Opens a new {@link Roo.form.Column} container in the layout stack. If fields are passed after the config, the
26652      * fields are added and the column is closed. If no fields are passed the column remains open
26653      * until end() is called.
26654      * @param {Object} config The config to pass to the column
26655      * @param {Field} field1 (optional)
26656      * @param {Field} field2 (optional)
26657      * @param {Field} etc (optional)
26658      * @return Column The column container object
26659      */
26660     column : function(c){
26661         var col = new Roo.form.Column(c);
26662         this.start(col);
26663         if(arguments.length > 1){ // duplicate code required because of Opera
26664             this.add.apply(this, Array.prototype.slice.call(arguments, 1));
26665             this.end();
26666         }
26667         return col;
26668     },
26669
26670     /**
26671      * Opens a new {@link Roo.form.FieldSet} container in the layout stack. If fields are passed after the config, the
26672      * fields are added and the fieldset is closed. If no fields are passed the fieldset remains open
26673      * until end() is called.
26674      * @param {Object} config The config to pass to the fieldset
26675      * @param {Field} field1 (optional)
26676      * @param {Field} field2 (optional)
26677      * @param {Field} etc (optional)
26678      * @return FieldSet The fieldset container object
26679      */
26680     fieldset : function(c){
26681         var fs = new Roo.form.FieldSet(c);
26682         this.start(fs);
26683         if(arguments.length > 1){ // duplicate code required because of Opera
26684             this.add.apply(this, Array.prototype.slice.call(arguments, 1));
26685             this.end();
26686         }
26687         return fs;
26688     },
26689
26690     /**
26691      * Opens a new {@link Roo.form.Layout} container in the layout stack. If fields are passed after the config, the
26692      * fields are added and the container is closed. If no fields are passed the container remains open
26693      * until end() is called.
26694      * @param {Object} config The config to pass to the Layout
26695      * @param {Field} field1 (optional)
26696      * @param {Field} field2 (optional)
26697      * @param {Field} etc (optional)
26698      * @return Layout The container object
26699      */
26700     container : function(c){
26701         var l = new Roo.form.Layout(c);
26702         this.start(l);
26703         if(arguments.length > 1){ // duplicate code required because of Opera
26704             this.add.apply(this, Array.prototype.slice.call(arguments, 1));
26705             this.end();
26706         }
26707         return l;
26708     },
26709
26710     /**
26711      * Opens the passed container in the layout stack. The container can be any {@link Roo.form.Layout} or subclass.
26712      * @param {Object} container A Roo.form.Layout or subclass of Layout
26713      * @return {Form} this
26714      */
26715     start : function(c){
26716         // cascade label info
26717         Roo.applyIf(c, {'labelAlign': this.active.labelAlign, 'labelWidth': this.active.labelWidth, 'itemCls': this.active.itemCls});
26718         this.active.stack.push(c);
26719         c.ownerCt = this.active;
26720         this.active = c;
26721         return this;
26722     },
26723
26724     /**
26725      * Closes the current open container
26726      * @return {Form} this
26727      */
26728     end : function(){
26729         if(this.active == this.root){
26730             return this;
26731         }
26732         this.active = this.active.ownerCt;
26733         return this;
26734     },
26735
26736     /**
26737      * Add Roo.form components to the current open container (e.g. column, fieldset, etc.).  Fields added via this method
26738      * can also be passed with an additional property of fieldLabel, which if supplied, will provide the text to display
26739      * as the label of the field.
26740      * @param {Field} field1
26741      * @param {Field} field2 (optional)
26742      * @param {Field} etc. (optional)
26743      * @return {Form} this
26744      */
26745     add : function(){
26746         this.active.stack.push.apply(this.active.stack, arguments);
26747         this.allItems.push.apply(this.allItems,arguments);
26748         var r = [];
26749         for(var i = 0, a = arguments, len = a.length; i < len; i++) {
26750             if(a[i].isFormField){
26751                 r.push(a[i]);
26752             }
26753         }
26754         if(r.length > 0){
26755             Roo.form.Form.superclass.add.apply(this, r);
26756         }
26757         return this;
26758     },
26759     
26760
26761     
26762     
26763     
26764      /**
26765      * Find any element that has been added to a form, using it's ID or name
26766      * This can include framesets, columns etc. along with regular fields..
26767      * @param {String} id - id or name to find.
26768      
26769      * @return {Element} e - or false if nothing found.
26770      */
26771     findbyId : function(id)
26772     {
26773         var ret = false;
26774         if (!id) {
26775             return ret;
26776         }
26777         Roo.each(this.allItems, function(f){
26778             if (f.id == id || f.name == id ){
26779                 ret = f;
26780                 return false;
26781             }
26782         });
26783         return ret;
26784     },
26785
26786     
26787     
26788     /**
26789      * Render this form into the passed container. This should only be called once!
26790      * @param {String/HTMLElement/Element} container The element this component should be rendered into
26791      * @return {Form} this
26792      */
26793     render : function(ct)
26794     {
26795         
26796         
26797         
26798         ct = Roo.get(ct);
26799         var o = this.autoCreate || {
26800             tag: 'form',
26801             method : this.method || 'POST',
26802             id : this.id || Roo.id()
26803         };
26804         this.initEl(ct.createChild(o));
26805
26806         this.root.render(this.el);
26807         
26808        
26809              
26810         this.items.each(function(f){
26811             f.render('x-form-el-'+f.id);
26812         });
26813
26814         if(this.buttons.length > 0){
26815             // tables are required to maintain order and for correct IE layout
26816             var tb = this.el.createChild({cls:'x-form-btns-ct', cn: {
26817                 cls:"x-form-btns x-form-btns-"+this.buttonAlign,
26818                 html:'<table cellspacing="0"><tbody><tr></tr></tbody></table><div class="x-clear"></div>'
26819             }}, null, true);
26820             var tr = tb.getElementsByTagName('tr')[0];
26821             for(var i = 0, len = this.buttons.length; i < len; i++) {
26822                 var b = this.buttons[i];
26823                 var td = document.createElement('td');
26824                 td.className = 'x-form-btn-td';
26825                 b.render(tr.appendChild(td));
26826             }
26827         }
26828         if(this.monitorValid){ // initialize after render
26829             this.startMonitoring();
26830         }
26831         this.fireEvent('rendered', this);
26832         return this;
26833     },
26834
26835     /**
26836      * Adds a button to the footer of the form - this <b>must</b> be called before the form is rendered.
26837      * @param {String/Object} config A string becomes the button text, an object can either be a Button config
26838      * object or a valid Roo.DomHelper element config
26839      * @param {Function} handler The function called when the button is clicked
26840      * @param {Object} scope (optional) The scope of the handler function
26841      * @return {Roo.Button}
26842      */
26843     addButton : function(config, handler, scope){
26844         var bc = {
26845             handler: handler,
26846             scope: scope,
26847             minWidth: this.minButtonWidth,
26848             hideParent:true
26849         };
26850         if(typeof config == "string"){
26851             bc.text = config;
26852         }else{
26853             Roo.apply(bc, config);
26854         }
26855         var btn = new Roo.Button(null, bc);
26856         this.buttons.push(btn);
26857         return btn;
26858     },
26859
26860      /**
26861      * Adds a series of form elements (using the xtype property as the factory method.
26862      * Valid xtypes are:  TextField, TextArea .... Button, Layout, FieldSet, Column, (and 'end' to close a block)
26863      * @param {Object} config 
26864      */
26865     
26866     addxtype : function()
26867     {
26868         var ar = Array.prototype.slice.call(arguments, 0);
26869         var ret = false;
26870         for(var i = 0; i < ar.length; i++) {
26871             if (!ar[i]) {
26872                 continue; // skip -- if this happends something invalid got sent, we 
26873                 // should ignore it, as basically that interface element will not show up
26874                 // and that should be pretty obvious!!
26875             }
26876             
26877             if (Roo.form[ar[i].xtype]) {
26878                 ar[i].form = this;
26879                 var fe = Roo.factory(ar[i], Roo.form);
26880                 if (!ret) {
26881                     ret = fe;
26882                 }
26883                 fe.form = this;
26884                 if (fe.store) {
26885                     fe.store.form = this;
26886                 }
26887                 if (fe.isLayout) {  
26888                          
26889                     this.start(fe);
26890                     this.allItems.push(fe);
26891                     if (fe.items && fe.addxtype) {
26892                         fe.addxtype.apply(fe, fe.items);
26893                         delete fe.items;
26894                     }
26895                      this.end();
26896                     continue;
26897                 }
26898                 
26899                 
26900                  
26901                 this.add(fe);
26902               //  console.log('adding ' + ar[i].xtype);
26903             }
26904             if (ar[i].xtype == 'Button') {  
26905                 //console.log('adding button');
26906                 //console.log(ar[i]);
26907                 this.addButton(ar[i]);
26908                 this.allItems.push(fe);
26909                 continue;
26910             }
26911             
26912             if (ar[i].xtype == 'end') { // so we can add fieldsets... / layout etc.
26913                 alert('end is not supported on xtype any more, use items');
26914             //    this.end();
26915             //    //console.log('adding end');
26916             }
26917             
26918         }
26919         return ret;
26920     },
26921     
26922     /**
26923      * Starts monitoring of the valid state of this form. Usually this is done by passing the config
26924      * option "monitorValid"
26925      */
26926     startMonitoring : function(){
26927         if(!this.bound){
26928             this.bound = true;
26929             Roo.TaskMgr.start({
26930                 run : this.bindHandler,
26931                 interval : this.monitorPoll || 200,
26932                 scope: this
26933             });
26934         }
26935     },
26936
26937     /**
26938      * Stops monitoring of the valid state of this form
26939      */
26940     stopMonitoring : function(){
26941         this.bound = false;
26942     },
26943
26944     // private
26945     bindHandler : function(){
26946         if(!this.bound){
26947             return false; // stops binding
26948         }
26949         var valid = true;
26950         this.items.each(function(f){
26951             if(!f.isValid(true)){
26952                 valid = false;
26953                 return false;
26954             }
26955         });
26956         for(var i = 0, len = this.buttons.length; i < len; i++){
26957             var btn = this.buttons[i];
26958             if(btn.formBind === true && btn.disabled === valid){
26959                 btn.setDisabled(!valid);
26960             }
26961         }
26962         this.fireEvent('clientvalidation', this, valid);
26963     }
26964     
26965     
26966     
26967     
26968     
26969     
26970     
26971     
26972 });
26973
26974
26975 // back compat
26976 Roo.Form = Roo.form.Form;
26977 /*
26978  * Based on:
26979  * Ext JS Library 1.1.1
26980  * Copyright(c) 2006-2007, Ext JS, LLC.
26981  *
26982  * Originally Released Under LGPL - original licence link has changed is not relivant.
26983  *
26984  * Fork - LGPL
26985  * <script type="text/javascript">
26986  */
26987  
26988  /**
26989  * @class Roo.form.Action
26990  * Internal Class used to handle form actions
26991  * @constructor
26992  * @param {Roo.form.BasicForm} el The form element or its id
26993  * @param {Object} config Configuration options
26994  */
26995  
26996  
26997 // define the action interface
26998 Roo.form.Action = function(form, options){
26999     this.form = form;
27000     this.options = options || {};
27001 };
27002 /**
27003  * Client Validation Failed
27004  * @const 
27005  */
27006 Roo.form.Action.CLIENT_INVALID = 'client';
27007 /**
27008  * Server Validation Failed
27009  * @const 
27010  */
27011  Roo.form.Action.SERVER_INVALID = 'server';
27012  /**
27013  * Connect to Server Failed
27014  * @const 
27015  */
27016 Roo.form.Action.CONNECT_FAILURE = 'connect';
27017 /**
27018  * Reading Data from Server Failed
27019  * @const 
27020  */
27021 Roo.form.Action.LOAD_FAILURE = 'load';
27022
27023 Roo.form.Action.prototype = {
27024     type : 'default',
27025     failureType : undefined,
27026     response : undefined,
27027     result : undefined,
27028
27029     // interface method
27030     run : function(options){
27031
27032     },
27033
27034     // interface method
27035     success : function(response){
27036
27037     },
27038
27039     // interface method
27040     handleResponse : function(response){
27041
27042     },
27043
27044     // default connection failure
27045     failure : function(response){
27046         
27047         this.response = response;
27048         this.failureType = Roo.form.Action.CONNECT_FAILURE;
27049         this.form.afterAction(this, false);
27050     },
27051
27052     processResponse : function(response){
27053         this.response = response;
27054         if(!response.responseText){
27055             return true;
27056         }
27057         this.result = this.handleResponse(response);
27058         return this.result;
27059     },
27060
27061     // utility functions used internally
27062     getUrl : function(appendParams){
27063         var url = this.options.url || this.form.url || this.form.el.dom.action;
27064         if(appendParams){
27065             var p = this.getParams();
27066             if(p){
27067                 url += (url.indexOf('?') != -1 ? '&' : '?') + p;
27068             }
27069         }
27070         return url;
27071     },
27072
27073     getMethod : function(){
27074         return (this.options.method || this.form.method || this.form.el.dom.method || 'POST').toUpperCase();
27075     },
27076
27077     getParams : function(){
27078         var bp = this.form.baseParams;
27079         var p = this.options.params;
27080         if(p){
27081             if(typeof p == "object"){
27082                 p = Roo.urlEncode(Roo.applyIf(p, bp));
27083             }else if(typeof p == 'string' && bp){
27084                 p += '&' + Roo.urlEncode(bp);
27085             }
27086         }else if(bp){
27087             p = Roo.urlEncode(bp);
27088         }
27089         return p;
27090     },
27091
27092     createCallback : function(){
27093         return {
27094             success: this.success,
27095             failure: this.failure,
27096             scope: this,
27097             timeout: (this.form.timeout*1000),
27098             upload: this.form.fileUpload ? this.success : undefined
27099         };
27100     }
27101 };
27102
27103 Roo.form.Action.Submit = function(form, options){
27104     Roo.form.Action.Submit.superclass.constructor.call(this, form, options);
27105 };
27106
27107 Roo.extend(Roo.form.Action.Submit, Roo.form.Action, {
27108     type : 'submit',
27109
27110     haveProgress : false,
27111     uploadComplete : false,
27112     
27113     // uploadProgress indicator.
27114     uploadProgress : function()
27115     {
27116         if (!this.form.progressUrl) {
27117             return;
27118         }
27119         
27120         if (!this.haveProgress) {
27121             Roo.MessageBox.progress("Uploading", "Uploading");
27122         }
27123         if (this.uploadComplete) {
27124            Roo.MessageBox.hide();
27125            return;
27126         }
27127         
27128         this.haveProgress = true;
27129    
27130         var uid = this.form.findField('UPLOAD_IDENTIFIER').getValue();
27131         
27132         var c = new Roo.data.Connection();
27133         c.request({
27134             url : this.form.progressUrl,
27135             params: {
27136                 id : uid
27137             },
27138             method: 'GET',
27139             success : function(req){
27140                //console.log(data);
27141                 var rdata = false;
27142                 var edata;
27143                 try  {
27144                    rdata = Roo.decode(req.responseText)
27145                 } catch (e) {
27146                     Roo.log("Invalid data from server..");
27147                     Roo.log(edata);
27148                     return;
27149                 }
27150                 if (!rdata || !rdata.success) {
27151                     Roo.log(rdata);
27152                     return;
27153                 }
27154                 var data = rdata.data;
27155                 
27156                 if (this.uploadComplete) {
27157                    Roo.MessageBox.hide();
27158                    return;
27159                 }
27160                    
27161                 if (data){
27162                     Roo.MessageBox.updateProgress(data.bytes_uploaded/data.bytes_total,
27163                        Math.floor((data.bytes_total - data.bytes_uploaded)/1000) + 'k remaining'
27164                     );
27165                 }
27166                 this.uploadProgress.defer(2000,this);
27167             },
27168        
27169             failure: function(data) {
27170                 Roo.log('progress url failed ');
27171                 Roo.log(data);
27172             },
27173             scope : this
27174         });
27175            
27176     },
27177     
27178     
27179     run : function()
27180     {
27181         // run get Values on the form, so it syncs any secondary forms.
27182         this.form.getValues();
27183         
27184         var o = this.options;
27185         var method = this.getMethod();
27186         var isPost = method == 'POST';
27187         if(o.clientValidation === false || this.form.isValid()){
27188             
27189             if (this.form.progressUrl) {
27190                 this.form.findField('UPLOAD_IDENTIFIER').setValue(
27191                     (new Date() * 1) + '' + Math.random());
27192                     
27193             } 
27194             
27195             
27196             Roo.Ajax.request(Roo.apply(this.createCallback(), {
27197                 form:this.form.el.dom,
27198                 url:this.getUrl(!isPost),
27199                 method: method,
27200                 params:isPost ? this.getParams() : null,
27201                 isUpload: this.form.fileUpload
27202             }));
27203             
27204             this.uploadProgress();
27205
27206         }else if (o.clientValidation !== false){ // client validation failed
27207             this.failureType = Roo.form.Action.CLIENT_INVALID;
27208             this.form.afterAction(this, false);
27209         }
27210     },
27211
27212     success : function(response)
27213     {
27214         this.uploadComplete= true;
27215         if (this.haveProgress) {
27216             Roo.MessageBox.hide();
27217         }
27218         
27219         
27220         var result = this.processResponse(response);
27221         if(result === true || result.success){
27222             this.form.afterAction(this, true);
27223             return;
27224         }
27225         if(result.errors){
27226             this.form.markInvalid(result.errors);
27227             this.failureType = Roo.form.Action.SERVER_INVALID;
27228         }
27229         this.form.afterAction(this, false);
27230     },
27231     failure : function(response)
27232     {
27233         this.uploadComplete= true;
27234         if (this.haveProgress) {
27235             Roo.MessageBox.hide();
27236         }
27237         
27238         
27239         this.response = response;
27240         this.failureType = Roo.form.Action.CONNECT_FAILURE;
27241         this.form.afterAction(this, false);
27242     },
27243     
27244     handleResponse : function(response){
27245         if(this.form.errorReader){
27246             var rs = this.form.errorReader.read(response);
27247             var errors = [];
27248             if(rs.records){
27249                 for(var i = 0, len = rs.records.length; i < len; i++) {
27250                     var r = rs.records[i];
27251                     errors[i] = r.data;
27252                 }
27253             }
27254             if(errors.length < 1){
27255                 errors = null;
27256             }
27257             return {
27258                 success : rs.success,
27259                 errors : errors
27260             };
27261         }
27262         var ret = false;
27263         try {
27264             ret = Roo.decode(response.responseText);
27265         } catch (e) {
27266             ret = {
27267                 success: false,
27268                 errorMsg: "Failed to read server message: " + (response ? response.responseText : ' - no message'),
27269                 errors : []
27270             };
27271         }
27272         return ret;
27273         
27274     }
27275 });
27276
27277
27278 Roo.form.Action.Load = function(form, options){
27279     Roo.form.Action.Load.superclass.constructor.call(this, form, options);
27280     this.reader = this.form.reader;
27281 };
27282
27283 Roo.extend(Roo.form.Action.Load, Roo.form.Action, {
27284     type : 'load',
27285
27286     run : function(){
27287         
27288         Roo.Ajax.request(Roo.apply(
27289                 this.createCallback(), {
27290                     method:this.getMethod(),
27291                     url:this.getUrl(false),
27292                     params:this.getParams()
27293         }));
27294     },
27295
27296     success : function(response){
27297         
27298         var result = this.processResponse(response);
27299         if(result === true || !result.success || !result.data){
27300             this.failureType = Roo.form.Action.LOAD_FAILURE;
27301             this.form.afterAction(this, false);
27302             return;
27303         }
27304         this.form.clearInvalid();
27305         this.form.setValues(result.data);
27306         this.form.afterAction(this, true);
27307     },
27308
27309     handleResponse : function(response){
27310         if(this.form.reader){
27311             var rs = this.form.reader.read(response);
27312             var data = rs.records && rs.records[0] ? rs.records[0].data : null;
27313             return {
27314                 success : rs.success,
27315                 data : data
27316             };
27317         }
27318         return Roo.decode(response.responseText);
27319     }
27320 });
27321
27322 Roo.form.Action.ACTION_TYPES = {
27323     'load' : Roo.form.Action.Load,
27324     'submit' : Roo.form.Action.Submit
27325 };/*
27326  * Based on:
27327  * Ext JS Library 1.1.1
27328  * Copyright(c) 2006-2007, Ext JS, LLC.
27329  *
27330  * Originally Released Under LGPL - original licence link has changed is not relivant.
27331  *
27332  * Fork - LGPL
27333  * <script type="text/javascript">
27334  */
27335  
27336 /**
27337  * @class Roo.form.Layout
27338  * @extends Roo.Component
27339  * Creates a container for layout and rendering of fields in an {@link Roo.form.Form}.
27340  * @constructor
27341  * @param {Object} config Configuration options
27342  */
27343 Roo.form.Layout = function(config){
27344     var xitems = [];
27345     if (config.items) {
27346         xitems = config.items;
27347         delete config.items;
27348     }
27349     Roo.form.Layout.superclass.constructor.call(this, config);
27350     this.stack = [];
27351     Roo.each(xitems, this.addxtype, this);
27352      
27353 };
27354
27355 Roo.extend(Roo.form.Layout, Roo.Component, {
27356     /**
27357      * @cfg {String/Object} autoCreate
27358      * A DomHelper element spec used to autocreate the layout (defaults to {tag: 'div', cls: 'x-form-ct'})
27359      */
27360     /**
27361      * @cfg {String/Object/Function} style
27362      * A style specification string, e.g. "width:100px", or object in the form {width:"100px"}, or
27363      * a function which returns such a specification.
27364      */
27365     /**
27366      * @cfg {String} labelAlign
27367      * Valid values are "left," "top" and "right" (defaults to "left")
27368      */
27369     /**
27370      * @cfg {Number} labelWidth
27371      * Fixed width in pixels of all field labels (defaults to undefined)
27372      */
27373     /**
27374      * @cfg {Boolean} clear
27375      * True to add a clearing element at the end of this layout, equivalent to CSS clear: both (defaults to true)
27376      */
27377     clear : true,
27378     /**
27379      * @cfg {String} labelSeparator
27380      * The separator to use after field labels (defaults to ':')
27381      */
27382     labelSeparator : ':',
27383     /**
27384      * @cfg {Boolean} hideLabels
27385      * True to suppress the display of field labels in this layout (defaults to false)
27386      */
27387     hideLabels : false,
27388
27389     // private
27390     defaultAutoCreate : {tag: 'div', cls: 'x-form-ct'},
27391     
27392     isLayout : true,
27393     
27394     // private
27395     onRender : function(ct, position){
27396         if(this.el){ // from markup
27397             this.el = Roo.get(this.el);
27398         }else {  // generate
27399             var cfg = this.getAutoCreate();
27400             this.el = ct.createChild(cfg, position);
27401         }
27402         if(this.style){
27403             this.el.applyStyles(this.style);
27404         }
27405         if(this.labelAlign){
27406             this.el.addClass('x-form-label-'+this.labelAlign);
27407         }
27408         if(this.hideLabels){
27409             this.labelStyle = "display:none";
27410             this.elementStyle = "padding-left:0;";
27411         }else{
27412             if(typeof this.labelWidth == 'number'){
27413                 this.labelStyle = "width:"+this.labelWidth+"px;";
27414                 this.elementStyle = "padding-left:"+((this.labelWidth+(typeof this.labelPad == 'number' ? this.labelPad : 5))+'px')+";";
27415             }
27416             if(this.labelAlign == 'top'){
27417                 this.labelStyle = "width:auto;";
27418                 this.elementStyle = "padding-left:0;";
27419             }
27420         }
27421         var stack = this.stack;
27422         var slen = stack.length;
27423         if(slen > 0){
27424             if(!this.fieldTpl){
27425                 var t = new Roo.Template(
27426                     '<div class="x-form-item {5}">',
27427                         '<label for="{0}" style="{2}">{1}{4}</label>',
27428                         '<div class="x-form-element" id="x-form-el-{0}" style="{3}">',
27429                         '</div>',
27430                     '</div><div class="x-form-clear-left"></div>'
27431                 );
27432                 t.disableFormats = true;
27433                 t.compile();
27434                 Roo.form.Layout.prototype.fieldTpl = t;
27435             }
27436             for(var i = 0; i < slen; i++) {
27437                 if(stack[i].isFormField){
27438                     this.renderField(stack[i]);
27439                 }else{
27440                     this.renderComponent(stack[i]);
27441                 }
27442             }
27443         }
27444         if(this.clear){
27445             this.el.createChild({cls:'x-form-clear'});
27446         }
27447     },
27448
27449     // private
27450     renderField : function(f){
27451         f.fieldEl = Roo.get(this.fieldTpl.append(this.el, [
27452                f.id, //0
27453                f.fieldLabel, //1
27454                f.labelStyle||this.labelStyle||'', //2
27455                this.elementStyle||'', //3
27456                typeof f.labelSeparator == 'undefined' ? this.labelSeparator : f.labelSeparator, //4
27457                f.itemCls||this.itemCls||''  //5
27458        ], true).getPrevSibling());
27459     },
27460
27461     // private
27462     renderComponent : function(c){
27463         c.render(c.isLayout ? this.el : this.el.createChild());    
27464     },
27465     /**
27466      * Adds a object form elements (using the xtype property as the factory method.)
27467      * Valid xtypes are:  TextField, TextArea .... Button, Layout, FieldSet, Column
27468      * @param {Object} config 
27469      */
27470     addxtype : function(o)
27471     {
27472         // create the lement.
27473         o.form = this.form;
27474         var fe = Roo.factory(o, Roo.form);
27475         this.form.allItems.push(fe);
27476         this.stack.push(fe);
27477         
27478         if (fe.isFormField) {
27479             this.form.items.add(fe);
27480         }
27481          
27482         return fe;
27483     }
27484 });
27485
27486 /**
27487  * @class Roo.form.Column
27488  * @extends Roo.form.Layout
27489  * Creates a column container for layout and rendering of fields in an {@link Roo.form.Form}.
27490  * @constructor
27491  * @param {Object} config Configuration options
27492  */
27493 Roo.form.Column = function(config){
27494     Roo.form.Column.superclass.constructor.call(this, config);
27495 };
27496
27497 Roo.extend(Roo.form.Column, Roo.form.Layout, {
27498     /**
27499      * @cfg {Number/String} width
27500      * The fixed width of the column in pixels or CSS value (defaults to "auto")
27501      */
27502     /**
27503      * @cfg {String/Object} autoCreate
27504      * A DomHelper element spec used to autocreate the column (defaults to {tag: 'div', cls: 'x-form-ct x-form-column'})
27505      */
27506
27507     // private
27508     defaultAutoCreate : {tag: 'div', cls: 'x-form-ct x-form-column'},
27509
27510     // private
27511     onRender : function(ct, position){
27512         Roo.form.Column.superclass.onRender.call(this, ct, position);
27513         if(this.width){
27514             this.el.setWidth(this.width);
27515         }
27516     }
27517 });
27518
27519
27520 /**
27521  * @class Roo.form.Row
27522  * @extends Roo.form.Layout
27523  * Creates a row container for layout and rendering of fields in an {@link Roo.form.Form}.
27524  * @constructor
27525  * @param {Object} config Configuration options
27526  */
27527
27528  
27529 Roo.form.Row = function(config){
27530     Roo.form.Row.superclass.constructor.call(this, config);
27531 };
27532  
27533 Roo.extend(Roo.form.Row, Roo.form.Layout, {
27534       /**
27535      * @cfg {Number/String} width
27536      * The fixed width of the column in pixels or CSS value (defaults to "auto")
27537      */
27538     /**
27539      * @cfg {Number/String} height
27540      * The fixed height of the column in pixels or CSS value (defaults to "auto")
27541      */
27542     defaultAutoCreate : {tag: 'div', cls: 'x-form-ct x-form-row'},
27543     
27544     padWidth : 20,
27545     // private
27546     onRender : function(ct, position){
27547         //console.log('row render');
27548         if(!this.rowTpl){
27549             var t = new Roo.Template(
27550                 '<div class="x-form-item {5}" style="float:left;width:{6}px">',
27551                     '<label for="{0}" style="{2}">{1}{4}</label>',
27552                     '<div class="x-form-element" id="x-form-el-{0}" style="{3}">',
27553                     '</div>',
27554                 '</div>'
27555             );
27556             t.disableFormats = true;
27557             t.compile();
27558             Roo.form.Layout.prototype.rowTpl = t;
27559         }
27560         this.fieldTpl = this.rowTpl;
27561         
27562         //console.log('lw' + this.labelWidth +', la:' + this.labelAlign);
27563         var labelWidth = 100;
27564         
27565         if ((this.labelAlign != 'top')) {
27566             if (typeof this.labelWidth == 'number') {
27567                 labelWidth = this.labelWidth
27568             }
27569             this.padWidth =  20 + labelWidth;
27570             
27571         }
27572         
27573         Roo.form.Column.superclass.onRender.call(this, ct, position);
27574         if(this.width){
27575             this.el.setWidth(this.width);
27576         }
27577         if(this.height){
27578             this.el.setHeight(this.height);
27579         }
27580     },
27581     
27582     // private
27583     renderField : function(f){
27584         f.fieldEl = this.fieldTpl.append(this.el, [
27585                f.id, f.fieldLabel,
27586                f.labelStyle||this.labelStyle||'',
27587                this.elementStyle||'',
27588                typeof f.labelSeparator == 'undefined' ? this.labelSeparator : f.labelSeparator,
27589                f.itemCls||this.itemCls||'',
27590                f.width ? f.width + this.padWidth : 160 + this.padWidth
27591        ],true);
27592     }
27593 });
27594  
27595
27596 /**
27597  * @class Roo.form.FieldSet
27598  * @extends Roo.form.Layout
27599  * Creates a fieldset container for layout and rendering of fields in an {@link Roo.form.Form}.
27600  * @constructor
27601  * @param {Object} config Configuration options
27602  */
27603 Roo.form.FieldSet = function(config){
27604     Roo.form.FieldSet.superclass.constructor.call(this, config);
27605 };
27606
27607 Roo.extend(Roo.form.FieldSet, Roo.form.Layout, {
27608     /**
27609      * @cfg {String} legend
27610      * The text to display as the legend for the FieldSet (defaults to '')
27611      */
27612     /**
27613      * @cfg {String/Object} autoCreate
27614      * A DomHelper element spec used to autocreate the fieldset (defaults to {tag: 'fieldset', cn: {tag:'legend'}})
27615      */
27616
27617     // private
27618     defaultAutoCreate : {tag: 'fieldset', cn: {tag:'legend'}},
27619
27620     // private
27621     onRender : function(ct, position){
27622         Roo.form.FieldSet.superclass.onRender.call(this, ct, position);
27623         if(this.legend){
27624             this.setLegend(this.legend);
27625         }
27626     },
27627
27628     // private
27629     setLegend : function(text){
27630         if(this.rendered){
27631             this.el.child('legend').update(text);
27632         }
27633     }
27634 });/*
27635  * Based on:
27636  * Ext JS Library 1.1.1
27637  * Copyright(c) 2006-2007, Ext JS, LLC.
27638  *
27639  * Originally Released Under LGPL - original licence link has changed is not relivant.
27640  *
27641  * Fork - LGPL
27642  * <script type="text/javascript">
27643  */
27644 /**
27645  * @class Roo.form.VTypes
27646  * Overridable validation definitions. The validations provided are basic and intended to be easily customizable and extended.
27647  * @singleton
27648  */
27649 Roo.form.VTypes = function(){
27650     // closure these in so they are only created once.
27651     var alpha = /^[a-zA-Z_]+$/;
27652     var alphanum = /^[a-zA-Z0-9_]+$/;
27653     var email = /^([\w]+)(.[\w]+)*@([\w-]+\.){1,5}([A-Za-z]){2,4}$/;
27654     var url = /(((https?)|(ftp)):\/\/([\-\w]+\.)+\w{2,3}(\/[%\-\w]+(\.\w{2,})?)*(([\w\-\.\?\\\/+@&#;`~=%!]*)(\.\w{2,})?)*\/?)/i;
27655
27656     // All these messages and functions are configurable
27657     return {
27658         /**
27659          * The function used to validate email addresses
27660          * @param {String} value The email address
27661          */
27662         'email' : function(v){
27663             return email.test(v);
27664         },
27665         /**
27666          * The error text to display when the email validation function returns false
27667          * @type String
27668          */
27669         'emailText' : 'This field should be an e-mail address in the format "user@domain.com"',
27670         /**
27671          * The keystroke filter mask to be applied on email input
27672          * @type RegExp
27673          */
27674         'emailMask' : /[a-z0-9_\.\-@]/i,
27675
27676         /**
27677          * The function used to validate URLs
27678          * @param {String} value The URL
27679          */
27680         'url' : function(v){
27681             return url.test(v);
27682         },
27683         /**
27684          * The error text to display when the url validation function returns false
27685          * @type String
27686          */
27687         'urlText' : 'This field should be a URL in the format "http:/'+'/www.domain.com"',
27688         
27689         /**
27690          * The function used to validate alpha values
27691          * @param {String} value The value
27692          */
27693         'alpha' : function(v){
27694             return alpha.test(v);
27695         },
27696         /**
27697          * The error text to display when the alpha validation function returns false
27698          * @type String
27699          */
27700         'alphaText' : 'This field should only contain letters and _',
27701         /**
27702          * The keystroke filter mask to be applied on alpha input
27703          * @type RegExp
27704          */
27705         'alphaMask' : /[a-z_]/i,
27706
27707         /**
27708          * The function used to validate alphanumeric values
27709          * @param {String} value The value
27710          */
27711         'alphanum' : function(v){
27712             return alphanum.test(v);
27713         },
27714         /**
27715          * The error text to display when the alphanumeric validation function returns false
27716          * @type String
27717          */
27718         'alphanumText' : 'This field should only contain letters, numbers and _',
27719         /**
27720          * The keystroke filter mask to be applied on alphanumeric input
27721          * @type RegExp
27722          */
27723         'alphanumMask' : /[a-z0-9_]/i
27724     };
27725 }();//<script type="text/javascript">
27726
27727 /**
27728  * @class Roo.form.FCKeditor
27729  * @extends Roo.form.TextArea
27730  * Wrapper around the FCKEditor http://www.fckeditor.net
27731  * @constructor
27732  * Creates a new FCKeditor
27733  * @param {Object} config Configuration options
27734  */
27735 Roo.form.FCKeditor = function(config){
27736     Roo.form.FCKeditor.superclass.constructor.call(this, config);
27737     this.addEvents({
27738          /**
27739          * @event editorinit
27740          * Fired when the editor is initialized - you can add extra handlers here..
27741          * @param {FCKeditor} this
27742          * @param {Object} the FCK object.
27743          */
27744         editorinit : true
27745     });
27746     
27747     
27748 };
27749 Roo.form.FCKeditor.editors = { };
27750 Roo.extend(Roo.form.FCKeditor, Roo.form.TextArea,
27751 {
27752     //defaultAutoCreate : {
27753     //    tag : "textarea",style   : "width:100px;height:60px;" ,autocomplete    : "off"
27754     //},
27755     // private
27756     /**
27757      * @cfg {Object} fck options - see fck manual for details.
27758      */
27759     fckconfig : false,
27760     
27761     /**
27762      * @cfg {Object} fck toolbar set (Basic or Default)
27763      */
27764     toolbarSet : 'Basic',
27765     /**
27766      * @cfg {Object} fck BasePath
27767      */ 
27768     basePath : '/fckeditor/',
27769     
27770     
27771     frame : false,
27772     
27773     value : '',
27774     
27775    
27776     onRender : function(ct, position)
27777     {
27778         if(!this.el){
27779             this.defaultAutoCreate = {
27780                 tag: "textarea",
27781                 style:"width:300px;height:60px;",
27782                 autocomplete: "off"
27783             };
27784         }
27785         Roo.form.FCKeditor.superclass.onRender.call(this, ct, position);
27786         /*
27787         if(this.grow){
27788             this.textSizeEl = Roo.DomHelper.append(document.body, {tag: "pre", cls: "x-form-grow-sizer"});
27789             if(this.preventScrollbars){
27790                 this.el.setStyle("overflow", "hidden");
27791             }
27792             this.el.setHeight(this.growMin);
27793         }
27794         */
27795         //console.log('onrender' + this.getId() );
27796         Roo.form.FCKeditor.editors[this.getId()] = this;
27797          
27798
27799         this.replaceTextarea() ;
27800         
27801     },
27802     
27803     getEditor : function() {
27804         return this.fckEditor;
27805     },
27806     /**
27807      * Sets a data value into the field and validates it.  To set the value directly without validation see {@link #setRawValue}.
27808      * @param {Mixed} value The value to set
27809      */
27810     
27811     
27812     setValue : function(value)
27813     {
27814         //console.log('setValue: ' + value);
27815         
27816         if(typeof(value) == 'undefined') { // not sure why this is happending...
27817             return;
27818         }
27819         Roo.form.FCKeditor.superclass.setValue.apply(this,[value]);
27820         
27821         //if(!this.el || !this.getEditor()) {
27822         //    this.value = value;
27823             //this.setValue.defer(100,this,[value]);    
27824         //    return;
27825         //} 
27826         
27827         if(!this.getEditor()) {
27828             return;
27829         }
27830         
27831         this.getEditor().SetData(value);
27832         
27833         //
27834
27835     },
27836
27837     /**
27838      * Returns the normalized data value (undefined or emptyText will be returned as '').  To return the raw value see {@link #getRawValue}.
27839      * @return {Mixed} value The field value
27840      */
27841     getValue : function()
27842     {
27843         
27844         if (this.frame && this.frame.dom.style.display == 'none') {
27845             return Roo.form.FCKeditor.superclass.getValue.call(this);
27846         }
27847         
27848         if(!this.el || !this.getEditor()) {
27849            
27850            // this.getValue.defer(100,this); 
27851             return this.value;
27852         }
27853        
27854         
27855         var value=this.getEditor().GetData();
27856         Roo.form.FCKeditor.superclass.setValue.apply(this,[value]);
27857         return Roo.form.FCKeditor.superclass.getValue.call(this);
27858         
27859
27860     },
27861
27862     /**
27863      * Returns the raw data value which may or may not be a valid, defined value.  To return a normalized value see {@link #getValue}.
27864      * @return {Mixed} value The field value
27865      */
27866     getRawValue : function()
27867     {
27868         if (this.frame && this.frame.dom.style.display == 'none') {
27869             return Roo.form.FCKeditor.superclass.getRawValue.call(this);
27870         }
27871         
27872         if(!this.el || !this.getEditor()) {
27873             //this.getRawValue.defer(100,this); 
27874             return this.value;
27875             return;
27876         }
27877         
27878         
27879         
27880         var value=this.getEditor().GetData();
27881         Roo.form.FCKeditor.superclass.setRawValue.apply(this,[value]);
27882         return Roo.form.FCKeditor.superclass.getRawValue.call(this);
27883          
27884     },
27885     
27886     setSize : function(w,h) {
27887         
27888         
27889         
27890         //if (this.frame && this.frame.dom.style.display == 'none') {
27891         //    Roo.form.FCKeditor.superclass.setSize.apply(this, [w, h]);
27892         //    return;
27893         //}
27894         //if(!this.el || !this.getEditor()) {
27895         //    this.setSize.defer(100,this, [w,h]); 
27896         //    return;
27897         //}
27898         
27899         
27900         
27901         Roo.form.FCKeditor.superclass.setSize.apply(this, [w, h]);
27902         
27903         this.frame.dom.setAttribute('width', w);
27904         this.frame.dom.setAttribute('height', h);
27905         this.frame.setSize(w,h);
27906         
27907     },
27908     
27909     toggleSourceEdit : function(value) {
27910         
27911       
27912          
27913         this.el.dom.style.display = value ? '' : 'none';
27914         this.frame.dom.style.display = value ?  'none' : '';
27915         
27916     },
27917     
27918     
27919     focus: function(tag)
27920     {
27921         if (this.frame.dom.style.display == 'none') {
27922             return Roo.form.FCKeditor.superclass.focus.call(this);
27923         }
27924         if(!this.el || !this.getEditor()) {
27925             this.focus.defer(100,this, [tag]); 
27926             return;
27927         }
27928         
27929         
27930         
27931         
27932         var tgs = this.getEditor().EditorDocument.getElementsByTagName(tag);
27933         this.getEditor().Focus();
27934         if (tgs.length) {
27935             if (!this.getEditor().Selection.GetSelection()) {
27936                 this.focus.defer(100,this, [tag]); 
27937                 return;
27938             }
27939             
27940             
27941             var r = this.getEditor().EditorDocument.createRange();
27942             r.setStart(tgs[0],0);
27943             r.setEnd(tgs[0],0);
27944             this.getEditor().Selection.GetSelection().removeAllRanges();
27945             this.getEditor().Selection.GetSelection().addRange(r);
27946             this.getEditor().Focus();
27947         }
27948         
27949     },
27950     
27951     
27952     
27953     replaceTextarea : function()
27954     {
27955         if ( document.getElementById( this.getId() + '___Frame' ) )
27956             return ;
27957         //if ( !this.checkBrowser || this._isCompatibleBrowser() )
27958         //{
27959             // We must check the elements firstly using the Id and then the name.
27960         var oTextarea = document.getElementById( this.getId() );
27961         
27962         var colElementsByName = document.getElementsByName( this.getId() ) ;
27963          
27964         oTextarea.style.display = 'none' ;
27965
27966         if ( oTextarea.tabIndex ) {            
27967             this.TabIndex = oTextarea.tabIndex ;
27968         }
27969         
27970         this._insertHtmlBefore( this._getConfigHtml(), oTextarea ) ;
27971         this._insertHtmlBefore( this._getIFrameHtml(), oTextarea ) ;
27972         this.frame = Roo.get(this.getId() + '___Frame')
27973     },
27974     
27975     _getConfigHtml : function()
27976     {
27977         var sConfig = '' ;
27978
27979         for ( var o in this.fckconfig ) {
27980             sConfig += sConfig.length > 0  ? '&amp;' : '';
27981             sConfig += encodeURIComponent( o ) + '=' + encodeURIComponent( this.fckconfig[o] ) ;
27982         }
27983
27984         return '<input type="hidden" id="' + this.getId() + '___Config" value="' + sConfig + '" style="display:none" />' ;
27985     },
27986     
27987     
27988     _getIFrameHtml : function()
27989     {
27990         var sFile = 'fckeditor.html' ;
27991         /* no idea what this is about..
27992         try
27993         {
27994             if ( (/fcksource=true/i).test( window.top.location.search ) )
27995                 sFile = 'fckeditor.original.html' ;
27996         }
27997         catch (e) { 
27998         */
27999
28000         var sLink = this.basePath + 'editor/' + sFile + '?InstanceName=' + encodeURIComponent( this.getId() ) ;
28001         sLink += this.toolbarSet ? ( '&amp;Toolbar=' + this.toolbarSet)  : '';
28002         
28003         
28004         var html = '<iframe id="' + this.getId() +
28005             '___Frame" src="' + sLink +
28006             '" width="' + this.width +
28007             '" height="' + this.height + '"' +
28008             (this.tabIndex ?  ' tabindex="' + this.tabIndex + '"' :'' ) +
28009             ' frameborder="0" scrolling="no"></iframe>' ;
28010
28011         return html ;
28012     },
28013     
28014     _insertHtmlBefore : function( html, element )
28015     {
28016         if ( element.insertAdjacentHTML )       {
28017             // IE
28018             element.insertAdjacentHTML( 'beforeBegin', html ) ;
28019         } else { // Gecko
28020             var oRange = document.createRange() ;
28021             oRange.setStartBefore( element ) ;
28022             var oFragment = oRange.createContextualFragment( html );
28023             element.parentNode.insertBefore( oFragment, element ) ;
28024         }
28025     }
28026     
28027     
28028   
28029     
28030     
28031     
28032     
28033
28034 });
28035
28036 //Roo.reg('fckeditor', Roo.form.FCKeditor);
28037
28038 function FCKeditor_OnComplete(editorInstance){
28039     var f = Roo.form.FCKeditor.editors[editorInstance.Name];
28040     f.fckEditor = editorInstance;
28041     //console.log("loaded");
28042     f.fireEvent('editorinit', f, editorInstance);
28043
28044   
28045
28046  
28047
28048
28049
28050
28051
28052
28053
28054
28055
28056
28057
28058
28059
28060
28061
28062 //<script type="text/javascript">
28063 /**
28064  * @class Roo.form.GridField
28065  * @extends Roo.form.Field
28066  * Embed a grid (or editable grid into a form)
28067  * STATUS ALPHA
28068  * 
28069  * This embeds a grid in a form, the value of the field should be the json encoded array of rows
28070  * it needs 
28071  * xgrid.store = Roo.data.Store
28072  * xgrid.store.proxy = Roo.data.MemoryProxy (data = [] )
28073  * xgrid.store.reader = Roo.data.JsonReader 
28074  * 
28075  * 
28076  * @constructor
28077  * Creates a new GridField
28078  * @param {Object} config Configuration options
28079  */
28080 Roo.form.GridField = function(config){
28081     Roo.form.GridField.superclass.constructor.call(this, config);
28082      
28083 };
28084
28085 Roo.extend(Roo.form.GridField, Roo.form.Field,  {
28086     /**
28087      * @cfg {Number} width  - used to restrict width of grid..
28088      */
28089     width : 100,
28090     /**
28091      * @cfg {Number} height - used to restrict height of grid..
28092      */
28093     height : 50,
28094      /**
28095      * @cfg {Object} xgrid (xtype'd description of grid) { xtype : 'Grid', dataSource: .... }
28096          * 
28097          *}
28098      */
28099     xgrid : false, 
28100     /**
28101      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
28102      * {tag: "input", type: "checkbox", autocomplete: "off"})
28103      */
28104    // defaultAutoCreate : { tag: 'div' },
28105     defaultAutoCreate : { tag: 'input', type: 'hidden', autocomplete: 'off'},
28106     /**
28107      * @cfg {String} addTitle Text to include for adding a title.
28108      */
28109     addTitle : false,
28110     //
28111     onResize : function(){
28112         Roo.form.Field.superclass.onResize.apply(this, arguments);
28113     },
28114
28115     initEvents : function(){
28116         // Roo.form.Checkbox.superclass.initEvents.call(this);
28117         // has no events...
28118        
28119     },
28120
28121
28122     getResizeEl : function(){
28123         return this.wrap;
28124     },
28125
28126     getPositionEl : function(){
28127         return this.wrap;
28128     },
28129
28130     // private
28131     onRender : function(ct, position){
28132         
28133         this.style = this.style || 'overflow: hidden; border:1px solid #c3daf9;';
28134         var style = this.style;
28135         delete this.style;
28136         
28137         Roo.form.GridField.superclass.onRender.call(this, ct, position);
28138         this.wrap = this.el.wrap({cls: ''}); // not sure why ive done thsi...
28139         this.viewEl = this.wrap.createChild({ tag: 'div' });
28140         if (style) {
28141             this.viewEl.applyStyles(style);
28142         }
28143         if (this.width) {
28144             this.viewEl.setWidth(this.width);
28145         }
28146         if (this.height) {
28147             this.viewEl.setHeight(this.height);
28148         }
28149         //if(this.inputValue !== undefined){
28150         //this.setValue(this.value);
28151         
28152         
28153         this.grid = new Roo.grid[this.xgrid.xtype](this.viewEl, this.xgrid);
28154         
28155         
28156         this.grid.render();
28157         this.grid.getDataSource().on('remove', this.refreshValue, this);
28158         this.grid.getDataSource().on('update', this.refreshValue, this);
28159         this.grid.on('afteredit', this.refreshValue, this);
28160  
28161     },
28162      
28163     
28164     /**
28165      * Sets the value of the item. 
28166      * @param {String} either an object  or a string..
28167      */
28168     setValue : function(v){
28169         //this.value = v;
28170         v = v || []; // empty set..
28171         // this does not seem smart - it really only affects memoryproxy grids..
28172         if (this.grid && this.grid.getDataSource() && typeof(v) != 'undefined') {
28173             var ds = this.grid.getDataSource();
28174             // assumes a json reader..
28175             var data = {}
28176             data[ds.reader.meta.root ] =  typeof(v) == 'string' ? Roo.decode(v) : v;
28177             ds.loadData( data);
28178         }
28179         Roo.form.GridField.superclass.setValue.call(this, v);
28180         this.refreshValue();
28181         // should load data in the grid really....
28182     },
28183     
28184     // private
28185     refreshValue: function() {
28186          var val = [];
28187         this.grid.getDataSource().each(function(r) {
28188             val.push(r.data);
28189         });
28190         this.el.dom.value = Roo.encode(val);
28191     }
28192     
28193      
28194     
28195     
28196 });/*
28197  * Based on:
28198  * Ext JS Library 1.1.1
28199  * Copyright(c) 2006-2007, Ext JS, LLC.
28200  *
28201  * Originally Released Under LGPL - original licence link has changed is not relivant.
28202  *
28203  * Fork - LGPL
28204  * <script type="text/javascript">
28205  */
28206 /**
28207  * @class Roo.form.DisplayField
28208  * @extends Roo.form.Field
28209  * A generic Field to display non-editable data.
28210  * @constructor
28211  * Creates a new Display Field item.
28212  * @param {Object} config Configuration options
28213  */
28214 Roo.form.DisplayField = function(config){
28215     Roo.form.DisplayField.superclass.constructor.call(this, config);
28216     
28217 };
28218
28219 Roo.extend(Roo.form.DisplayField, Roo.form.TextField,  {
28220     inputType:      'hidden',
28221     allowBlank:     true,
28222     readOnly:         true,
28223     
28224  
28225     /**
28226      * @cfg {String} focusClass The CSS class to use when the checkbox receives focus (defaults to undefined)
28227      */
28228     focusClass : undefined,
28229     /**
28230      * @cfg {String} fieldClass The default CSS class for the checkbox (defaults to "x-form-field")
28231      */
28232     fieldClass: 'x-form-field',
28233     
28234      /**
28235      * @cfg {Function} valueRenderer The renderer for the field (so you can reformat output). should return raw HTML
28236      */
28237     valueRenderer: undefined,
28238     
28239     width: 100,
28240     /**
28241      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
28242      * {tag: "input", type: "checkbox", autocomplete: "off"})
28243      */
28244      
28245  //   defaultAutoCreate : { tag: 'input', type: 'hidden', autocomplete: 'off'},
28246
28247     onResize : function(){
28248         Roo.form.DisplayField.superclass.onResize.apply(this, arguments);
28249         
28250     },
28251
28252     initEvents : function(){
28253         // Roo.form.Checkbox.superclass.initEvents.call(this);
28254         // has no events...
28255        
28256     },
28257
28258
28259     getResizeEl : function(){
28260         return this.wrap;
28261     },
28262
28263     getPositionEl : function(){
28264         return this.wrap;
28265     },
28266
28267     // private
28268     onRender : function(ct, position){
28269         
28270         Roo.form.DisplayField.superclass.onRender.call(this, ct, position);
28271         //if(this.inputValue !== undefined){
28272         this.wrap = this.el.wrap();
28273         
28274         this.viewEl = this.wrap.createChild({ tag: 'div', cls: 'x-form-displayfield'});
28275         
28276         if (this.bodyStyle) {
28277             this.viewEl.applyStyles(this.bodyStyle);
28278         }
28279         //this.viewEl.setStyle('padding', '2px');
28280         
28281         this.setValue(this.value);
28282         
28283     },
28284 /*
28285     // private
28286     initValue : Roo.emptyFn,
28287
28288   */
28289
28290         // private
28291     onClick : function(){
28292         
28293     },
28294
28295     /**
28296      * Sets the checked state of the checkbox.
28297      * @param {Boolean/String} checked True, 'true', '1', or 'on' to check the checkbox, any other value will uncheck it.
28298      */
28299     setValue : function(v){
28300         this.value = v;
28301         var html = this.valueRenderer ?  this.valueRenderer(v) : String.format('{0}', v);
28302         // this might be called before we have a dom element..
28303         if (!this.viewEl) {
28304             return;
28305         }
28306         this.viewEl.dom.innerHTML = html;
28307         Roo.form.DisplayField.superclass.setValue.call(this, v);
28308
28309     }
28310 });/*
28311  * 
28312  * Licence- LGPL
28313  * 
28314  */
28315
28316 /**
28317  * @class Roo.form.DayPicker
28318  * @extends Roo.form.Field
28319  * A Day picker show [M] [T] [W] ....
28320  * @constructor
28321  * Creates a new Day Picker
28322  * @param {Object} config Configuration options
28323  */
28324 Roo.form.DayPicker= function(config){
28325     Roo.form.DayPicker.superclass.constructor.call(this, config);
28326      
28327 };
28328
28329 Roo.extend(Roo.form.DayPicker, Roo.form.Field,  {
28330     /**
28331      * @cfg {String} focusClass The CSS class to use when the checkbox receives focus (defaults to undefined)
28332      */
28333     focusClass : undefined,
28334     /**
28335      * @cfg {String} fieldClass The default CSS class for the checkbox (defaults to "x-form-field")
28336      */
28337     fieldClass: "x-form-field",
28338    
28339     /**
28340      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
28341      * {tag: "input", type: "checkbox", autocomplete: "off"})
28342      */
28343     defaultAutoCreate : { tag: "input", type: 'hidden', autocomplete: "off"},
28344     
28345    
28346     actionMode : 'viewEl', 
28347     //
28348     // private
28349  
28350     inputType : 'hidden',
28351     
28352      
28353     inputElement: false, // real input element?
28354     basedOn: false, // ????
28355     
28356     isFormField: true, // not sure where this is needed!!!!
28357
28358     onResize : function(){
28359         Roo.form.Checkbox.superclass.onResize.apply(this, arguments);
28360         if(!this.boxLabel){
28361             this.el.alignTo(this.wrap, 'c-c');
28362         }
28363     },
28364
28365     initEvents : function(){
28366         Roo.form.Checkbox.superclass.initEvents.call(this);
28367         this.el.on("click", this.onClick,  this);
28368         this.el.on("change", this.onClick,  this);
28369     },
28370
28371
28372     getResizeEl : function(){
28373         return this.wrap;
28374     },
28375
28376     getPositionEl : function(){
28377         return this.wrap;
28378     },
28379
28380     
28381     // private
28382     onRender : function(ct, position){
28383         Roo.form.Checkbox.superclass.onRender.call(this, ct, position);
28384        
28385         this.wrap = this.el.wrap({cls: 'x-form-daypick-item '});
28386         
28387         var r1 = '<table><tr>';
28388         var r2 = '<tr class="x-form-daypick-icons">';
28389         for (var i=0; i < 7; i++) {
28390             r1+= '<td><div>' + Date.dayNames[i].substring(0,3) + '</div></td>';
28391             r2+= '<td><img class="x-menu-item-icon" src="' + Roo.BLANK_IMAGE_URL  +'"></td>';
28392         }
28393         
28394         var viewEl = this.wrap.createChild( r1 + '</tr>' + r2 + '</tr></table>');
28395         viewEl.select('img').on('click', this.onClick, this);
28396         this.viewEl = viewEl;   
28397         
28398         
28399         // this will not work on Chrome!!!
28400         this.el.on('DOMAttrModified', this.setFromHidden,  this); //ff
28401         this.el.on('propertychange', this.setFromHidden,  this);  //ie
28402         
28403         
28404           
28405
28406     },
28407
28408     // private
28409     initValue : Roo.emptyFn,
28410
28411     /**
28412      * Returns the checked state of the checkbox.
28413      * @return {Boolean} True if checked, else false
28414      */
28415     getValue : function(){
28416         return this.el.dom.value;
28417         
28418     },
28419
28420         // private
28421     onClick : function(e){ 
28422         //this.setChecked(!this.checked);
28423         Roo.get(e.target).toggleClass('x-menu-item-checked');
28424         this.refreshValue();
28425         //if(this.el.dom.checked != this.checked){
28426         //    this.setValue(this.el.dom.checked);
28427        // }
28428     },
28429     
28430     // private
28431     refreshValue : function()
28432     {
28433         var val = '';
28434         this.viewEl.select('img',true).each(function(e,i,n)  {
28435             val += e.is(".x-menu-item-checked") ? String(n) : '';
28436         });
28437         this.setValue(val, true);
28438     },
28439
28440     /**
28441      * Sets the checked state of the checkbox.
28442      * On is always based on a string comparison between inputValue and the param.
28443      * @param {Boolean/String} value - the value to set 
28444      * @param {Boolean/String} suppressEvent - whether to suppress the checkchange event.
28445      */
28446     setValue : function(v,suppressEvent){
28447         if (!this.el.dom) {
28448             return;
28449         }
28450         var old = this.el.dom.value ;
28451         this.el.dom.value = v;
28452         if (suppressEvent) {
28453             return ;
28454         }
28455          
28456         // update display..
28457         this.viewEl.select('img',true).each(function(e,i,n)  {
28458             
28459             var on = e.is(".x-menu-item-checked");
28460             var newv = v.indexOf(String(n)) > -1;
28461             if (on != newv) {
28462                 e.toggleClass('x-menu-item-checked');
28463             }
28464             
28465         });
28466         
28467         
28468         this.fireEvent('change', this, v, old);
28469         
28470         
28471     },
28472    
28473     // handle setting of hidden value by some other method!!?!?
28474     setFromHidden: function()
28475     {
28476         if(!this.el){
28477             return;
28478         }
28479         //console.log("SET FROM HIDDEN");
28480         //alert('setFrom hidden');
28481         this.setValue(this.el.dom.value);
28482     },
28483     
28484     onDestroy : function()
28485     {
28486         if(this.viewEl){
28487             Roo.get(this.viewEl).remove();
28488         }
28489          
28490         Roo.form.DayPicker.superclass.onDestroy.call(this);
28491     }
28492
28493 });//<script type="text/javasscript">
28494  
28495
28496 /**
28497  * @class Roo.DDView
28498  * A DnD enabled version of Roo.View.
28499  * @param {Element/String} container The Element in which to create the View.
28500  * @param {String} tpl The template string used to create the markup for each element of the View
28501  * @param {Object} config The configuration properties. These include all the config options of
28502  * {@link Roo.View} plus some specific to this class.<br>
28503  * <p>
28504  * Drag/drop is implemented by adding {@link Roo.data.Record}s to the target DDView. If copying is
28505  * not being performed, the original {@link Roo.data.Record} is removed from the source DDView.<br>
28506  * <p>
28507  * The following extra CSS rules are needed to provide insertion point highlighting:<pre><code>
28508 .x-view-drag-insert-above {
28509         border-top:1px dotted #3366cc;
28510 }
28511 .x-view-drag-insert-below {
28512         border-bottom:1px dotted #3366cc;
28513 }
28514 </code></pre>
28515  * 
28516  */
28517  
28518 Roo.DDView = function(container, tpl, config) {
28519     Roo.DDView.superclass.constructor.apply(this, arguments);
28520     this.getEl().setStyle("outline", "0px none");
28521     this.getEl().unselectable();
28522     if (this.dragGroup) {
28523                 this.setDraggable(this.dragGroup.split(","));
28524     }
28525     if (this.dropGroup) {
28526                 this.setDroppable(this.dropGroup.split(","));
28527     }
28528     if (this.deletable) {
28529         this.setDeletable();
28530     }
28531     this.isDirtyFlag = false;
28532         this.addEvents({
28533                 "drop" : true
28534         });
28535 };
28536
28537 Roo.extend(Roo.DDView, Roo.View, {
28538 /**     @cfg {String/Array} dragGroup The ddgroup name(s) for the View's DragZone. */
28539 /**     @cfg {String/Array} dropGroup The ddgroup name(s) for the View's DropZone. */
28540 /**     @cfg {Boolean} copy Causes drag operations to copy nodes rather than move. */
28541 /**     @cfg {Boolean} allowCopy Causes ctrl/drag operations to copy nodes rather than move. */
28542
28543         isFormField: true,
28544
28545         reset: Roo.emptyFn,
28546         
28547         clearInvalid: Roo.form.Field.prototype.clearInvalid,
28548
28549         validate: function() {
28550                 return true;
28551         },
28552         
28553         destroy: function() {
28554                 this.purgeListeners();
28555                 this.getEl.removeAllListeners();
28556                 this.getEl().remove();
28557                 if (this.dragZone) {
28558                         if (this.dragZone.destroy) {
28559                                 this.dragZone.destroy();
28560                         }
28561                 }
28562                 if (this.dropZone) {
28563                         if (this.dropZone.destroy) {
28564                                 this.dropZone.destroy();
28565                         }
28566                 }
28567         },
28568
28569 /**     Allows this class to be an Roo.form.Field so it can be found using {@link Roo.form.BasicForm#findField}. */
28570         getName: function() {
28571                 return this.name;
28572         },
28573
28574 /**     Loads the View from a JSON string representing the Records to put into the Store. */
28575         setValue: function(v) {
28576                 if (!this.store) {
28577                         throw "DDView.setValue(). DDView must be constructed with a valid Store";
28578                 }
28579                 var data = {};
28580                 data[this.store.reader.meta.root] = v ? [].concat(v) : [];
28581                 this.store.proxy = new Roo.data.MemoryProxy(data);
28582                 this.store.load();
28583         },
28584
28585 /**     @return {String} a parenthesised list of the ids of the Records in the View. */
28586         getValue: function() {
28587                 var result = '(';
28588                 this.store.each(function(rec) {
28589                         result += rec.id + ',';
28590                 });
28591                 return result.substr(0, result.length - 1) + ')';
28592         },
28593         
28594         getIds: function() {
28595                 var i = 0, result = new Array(this.store.getCount());
28596                 this.store.each(function(rec) {
28597                         result[i++] = rec.id;
28598                 });
28599                 return result;
28600         },
28601         
28602         isDirty: function() {
28603                 return this.isDirtyFlag;
28604         },
28605
28606 /**
28607  *      Part of the Roo.dd.DropZone interface. If no target node is found, the
28608  *      whole Element becomes the target, and this causes the drop gesture to append.
28609  */
28610     getTargetFromEvent : function(e) {
28611                 var target = e.getTarget();
28612                 while ((target !== null) && (target.parentNode != this.el.dom)) {
28613                 target = target.parentNode;
28614                 }
28615                 if (!target) {
28616                         target = this.el.dom.lastChild || this.el.dom;
28617                 }
28618                 return target;
28619     },
28620
28621 /**
28622  *      Create the drag data which consists of an object which has the property "ddel" as
28623  *      the drag proxy element. 
28624  */
28625     getDragData : function(e) {
28626         var target = this.findItemFromChild(e.getTarget());
28627                 if(target) {
28628                         this.handleSelection(e);
28629                         var selNodes = this.getSelectedNodes();
28630             var dragData = {
28631                 source: this,
28632                 copy: this.copy || (this.allowCopy && e.ctrlKey),
28633                 nodes: selNodes,
28634                 records: []
28635                         };
28636                         var selectedIndices = this.getSelectedIndexes();
28637                         for (var i = 0; i < selectedIndices.length; i++) {
28638                                 dragData.records.push(this.store.getAt(selectedIndices[i]));
28639                         }
28640                         if (selNodes.length == 1) {
28641                                 dragData.ddel = target.cloneNode(true); // the div element
28642                         } else {
28643                                 var div = document.createElement('div'); // create the multi element drag "ghost"
28644                                 div.className = 'multi-proxy';
28645                                 for (var i = 0, len = selNodes.length; i < len; i++) {
28646                                         div.appendChild(selNodes[i].cloneNode(true));
28647                                 }
28648                                 dragData.ddel = div;
28649                         }
28650             //console.log(dragData)
28651             //console.log(dragData.ddel.innerHTML)
28652                         return dragData;
28653                 }
28654         //console.log('nodragData')
28655                 return false;
28656     },
28657     
28658 /**     Specify to which ddGroup items in this DDView may be dragged. */
28659     setDraggable: function(ddGroup) {
28660         if (ddGroup instanceof Array) {
28661                 Roo.each(ddGroup, this.setDraggable, this);
28662                 return;
28663         }
28664         if (this.dragZone) {
28665                 this.dragZone.addToGroup(ddGroup);
28666         } else {
28667                         this.dragZone = new Roo.dd.DragZone(this.getEl(), {
28668                                 containerScroll: true,
28669                                 ddGroup: ddGroup 
28670
28671                         });
28672 //                      Draggability implies selection. DragZone's mousedown selects the element.
28673                         if (!this.multiSelect) { this.singleSelect = true; }
28674
28675 //                      Wire the DragZone's handlers up to methods in *this*
28676                         this.dragZone.getDragData = this.getDragData.createDelegate(this);
28677                 }
28678     },
28679
28680 /**     Specify from which ddGroup this DDView accepts drops. */
28681     setDroppable: function(ddGroup) {
28682         if (ddGroup instanceof Array) {
28683                 Roo.each(ddGroup, this.setDroppable, this);
28684                 return;
28685         }
28686         if (this.dropZone) {
28687                 this.dropZone.addToGroup(ddGroup);
28688         } else {
28689                         this.dropZone = new Roo.dd.DropZone(this.getEl(), {
28690                                 containerScroll: true,
28691                                 ddGroup: ddGroup
28692                         });
28693
28694 //                      Wire the DropZone's handlers up to methods in *this*
28695                         this.dropZone.getTargetFromEvent = this.getTargetFromEvent.createDelegate(this);
28696                         this.dropZone.onNodeEnter = this.onNodeEnter.createDelegate(this);
28697                         this.dropZone.onNodeOver = this.onNodeOver.createDelegate(this);
28698                         this.dropZone.onNodeOut = this.onNodeOut.createDelegate(this);
28699                         this.dropZone.onNodeDrop = this.onNodeDrop.createDelegate(this);
28700                 }
28701     },
28702
28703 /**     Decide whether to drop above or below a View node. */
28704     getDropPoint : function(e, n, dd){
28705         if (n == this.el.dom) { return "above"; }
28706                 var t = Roo.lib.Dom.getY(n), b = t + n.offsetHeight;
28707                 var c = t + (b - t) / 2;
28708                 var y = Roo.lib.Event.getPageY(e);
28709                 if(y <= c) {
28710                         return "above";
28711                 }else{
28712                         return "below";
28713                 }
28714     },
28715
28716     onNodeEnter : function(n, dd, e, data){
28717                 return false;
28718     },
28719     
28720     onNodeOver : function(n, dd, e, data){
28721                 var pt = this.getDropPoint(e, n, dd);
28722                 // set the insert point style on the target node
28723                 var dragElClass = this.dropNotAllowed;
28724                 if (pt) {
28725                         var targetElClass;
28726                         if (pt == "above"){
28727                                 dragElClass = n.previousSibling ? "x-tree-drop-ok-between" : "x-tree-drop-ok-above";
28728                                 targetElClass = "x-view-drag-insert-above";
28729                         } else {
28730                                 dragElClass = n.nextSibling ? "x-tree-drop-ok-between" : "x-tree-drop-ok-below";
28731                                 targetElClass = "x-view-drag-insert-below";
28732                         }
28733                         if (this.lastInsertClass != targetElClass){
28734                                 Roo.fly(n).replaceClass(this.lastInsertClass, targetElClass);
28735                                 this.lastInsertClass = targetElClass;
28736                         }
28737                 }
28738                 return dragElClass;
28739         },
28740
28741     onNodeOut : function(n, dd, e, data){
28742                 this.removeDropIndicators(n);
28743     },
28744
28745     onNodeDrop : function(n, dd, e, data){
28746         if (this.fireEvent("drop", this, n, dd, e, data) === false) {
28747                 return false;
28748         }
28749         var pt = this.getDropPoint(e, n, dd);
28750                 var insertAt = (n == this.el.dom) ? this.nodes.length : n.nodeIndex;
28751                 if (pt == "below") { insertAt++; }
28752                 for (var i = 0; i < data.records.length; i++) {
28753                         var r = data.records[i];
28754                         var dup = this.store.getById(r.id);
28755                         if (dup && (dd != this.dragZone)) {
28756                                 Roo.fly(this.getNode(this.store.indexOf(dup))).frame("red", 1);
28757                         } else {
28758                                 if (data.copy) {
28759                                         this.store.insert(insertAt++, r.copy());
28760                                 } else {
28761                                         data.source.isDirtyFlag = true;
28762                                         r.store.remove(r);
28763                                         this.store.insert(insertAt++, r);
28764                                 }
28765                                 this.isDirtyFlag = true;
28766                         }
28767                 }
28768                 this.dragZone.cachedTarget = null;
28769                 return true;
28770     },
28771
28772     removeDropIndicators : function(n){
28773                 if(n){
28774                         Roo.fly(n).removeClass([
28775                                 "x-view-drag-insert-above",
28776                                 "x-view-drag-insert-below"]);
28777                         this.lastInsertClass = "_noclass";
28778                 }
28779     },
28780
28781 /**
28782  *      Utility method. Add a delete option to the DDView's context menu.
28783  *      @param {String} imageUrl The URL of the "delete" icon image.
28784  */
28785         setDeletable: function(imageUrl) {
28786                 if (!this.singleSelect && !this.multiSelect) {
28787                         this.singleSelect = true;
28788                 }
28789                 var c = this.getContextMenu();
28790                 this.contextMenu.on("itemclick", function(item) {
28791                         switch (item.id) {
28792                                 case "delete":
28793                                         this.remove(this.getSelectedIndexes());
28794                                         break;
28795                         }
28796                 }, this);
28797                 this.contextMenu.add({
28798                         icon: imageUrl,
28799                         id: "delete",
28800                         text: 'Delete'
28801                 });
28802         },
28803         
28804 /**     Return the context menu for this DDView. */
28805         getContextMenu: function() {
28806                 if (!this.contextMenu) {
28807 //                      Create the View's context menu
28808                         this.contextMenu = new Roo.menu.Menu({
28809                                 id: this.id + "-contextmenu"
28810                         });
28811                         this.el.on("contextmenu", this.showContextMenu, this);
28812                 }
28813                 return this.contextMenu;
28814         },
28815         
28816         disableContextMenu: function() {
28817                 if (this.contextMenu) {
28818                         this.el.un("contextmenu", this.showContextMenu, this);
28819                 }
28820         },
28821
28822         showContextMenu: function(e, item) {
28823         item = this.findItemFromChild(e.getTarget());
28824                 if (item) {
28825                         e.stopEvent();
28826                         this.select(this.getNode(item), this.multiSelect && e.ctrlKey, true);
28827                         this.contextMenu.showAt(e.getXY());
28828             }
28829     },
28830
28831 /**
28832  *      Remove {@link Roo.data.Record}s at the specified indices.
28833  *      @param {Array/Number} selectedIndices The index (or Array of indices) of Records to remove.
28834  */
28835     remove: function(selectedIndices) {
28836                 selectedIndices = [].concat(selectedIndices);
28837                 for (var i = 0; i < selectedIndices.length; i++) {
28838                         var rec = this.store.getAt(selectedIndices[i]);
28839                         this.store.remove(rec);
28840                 }
28841     },
28842
28843 /**
28844  *      Double click fires the event, but also, if this is draggable, and there is only one other
28845  *      related DropZone, it transfers the selected node.
28846  */
28847     onDblClick : function(e){
28848         var item = this.findItemFromChild(e.getTarget());
28849         if(item){
28850             if (this.fireEvent("dblclick", this, this.indexOf(item), item, e) === false) {
28851                 return false;
28852             }
28853             if (this.dragGroup) {
28854                     var targets = Roo.dd.DragDropMgr.getRelated(this.dragZone, true);
28855                     while (targets.indexOf(this.dropZone) > -1) {
28856                             targets.remove(this.dropZone);
28857                                 }
28858                     if (targets.length == 1) {
28859                                         this.dragZone.cachedTarget = null;
28860                         var el = Roo.get(targets[0].getEl());
28861                         var box = el.getBox(true);
28862                         targets[0].onNodeDrop(el.dom, {
28863                                 target: el.dom,
28864                                 xy: [box.x, box.y + box.height - 1]
28865                         }, null, this.getDragData(e));
28866                     }
28867                 }
28868         }
28869     },
28870     
28871     handleSelection: function(e) {
28872                 this.dragZone.cachedTarget = null;
28873         var item = this.findItemFromChild(e.getTarget());
28874         if (!item) {
28875                 this.clearSelections(true);
28876                 return;
28877         }
28878                 if (item && (this.multiSelect || this.singleSelect)){
28879                         if(this.multiSelect && e.shiftKey && (!e.ctrlKey) && this.lastSelection){
28880                                 this.select(this.getNodes(this.indexOf(this.lastSelection), item.nodeIndex), false);
28881                         }else if (this.isSelected(this.getNode(item)) && e.ctrlKey){
28882                                 this.unselect(item);
28883                         } else {
28884                                 this.select(item, this.multiSelect && e.ctrlKey);
28885                                 this.lastSelection = item;
28886                         }
28887                 }
28888     },
28889
28890     onItemClick : function(item, index, e){
28891                 if(this.fireEvent("beforeclick", this, index, item, e) === false){
28892                         return false;
28893                 }
28894                 return true;
28895     },
28896
28897     unselect : function(nodeInfo, suppressEvent){
28898                 var node = this.getNode(nodeInfo);
28899                 if(node && this.isSelected(node)){
28900                         if(this.fireEvent("beforeselect", this, node, this.selections) !== false){
28901                                 Roo.fly(node).removeClass(this.selectedClass);
28902                                 this.selections.remove(node);
28903                                 if(!suppressEvent){
28904                                         this.fireEvent("selectionchange", this, this.selections);
28905                                 }
28906                         }
28907                 }
28908     }
28909 });
28910 /*
28911  * Based on:
28912  * Ext JS Library 1.1.1
28913  * Copyright(c) 2006-2007, Ext JS, LLC.
28914  *
28915  * Originally Released Under LGPL - original licence link has changed is not relivant.
28916  *
28917  * Fork - LGPL
28918  * <script type="text/javascript">
28919  */
28920  
28921 /**
28922  * @class Roo.LayoutManager
28923  * @extends Roo.util.Observable
28924  * Base class for layout managers.
28925  */
28926 Roo.LayoutManager = function(container, config){
28927     Roo.LayoutManager.superclass.constructor.call(this);
28928     this.el = Roo.get(container);
28929     // ie scrollbar fix
28930     if(this.el.dom == document.body && Roo.isIE && !config.allowScroll){
28931         document.body.scroll = "no";
28932     }else if(this.el.dom != document.body && this.el.getStyle('position') == 'static'){
28933         this.el.position('relative');
28934     }
28935     this.id = this.el.id;
28936     this.el.addClass("x-layout-container");
28937     /** false to disable window resize monitoring @type Boolean */
28938     this.monitorWindowResize = true;
28939     this.regions = {};
28940     this.addEvents({
28941         /**
28942          * @event layout
28943          * Fires when a layout is performed. 
28944          * @param {Roo.LayoutManager} this
28945          */
28946         "layout" : true,
28947         /**
28948          * @event regionresized
28949          * Fires when the user resizes a region. 
28950          * @param {Roo.LayoutRegion} region The resized region
28951          * @param {Number} newSize The new size (width for east/west, height for north/south)
28952          */
28953         "regionresized" : true,
28954         /**
28955          * @event regioncollapsed
28956          * Fires when a region is collapsed. 
28957          * @param {Roo.LayoutRegion} region The collapsed region
28958          */
28959         "regioncollapsed" : true,
28960         /**
28961          * @event regionexpanded
28962          * Fires when a region is expanded.  
28963          * @param {Roo.LayoutRegion} region The expanded region
28964          */
28965         "regionexpanded" : true
28966     });
28967     this.updating = false;
28968     Roo.EventManager.onWindowResize(this.onWindowResize, this, true);
28969 };
28970
28971 Roo.extend(Roo.LayoutManager, Roo.util.Observable, {
28972     /**
28973      * Returns true if this layout is currently being updated
28974      * @return {Boolean}
28975      */
28976     isUpdating : function(){
28977         return this.updating; 
28978     },
28979     
28980     /**
28981      * Suspend the LayoutManager from doing auto-layouts while
28982      * making multiple add or remove calls
28983      */
28984     beginUpdate : function(){
28985         this.updating = true;    
28986     },
28987     
28988     /**
28989      * Restore auto-layouts and optionally disable the manager from performing a layout
28990      * @param {Boolean} noLayout true to disable a layout update 
28991      */
28992     endUpdate : function(noLayout){
28993         this.updating = false;
28994         if(!noLayout){
28995             this.layout();
28996         }    
28997     },
28998     
28999     layout: function(){
29000         
29001     },
29002     
29003     onRegionResized : function(region, newSize){
29004         this.fireEvent("regionresized", region, newSize);
29005         this.layout();
29006     },
29007     
29008     onRegionCollapsed : function(region){
29009         this.fireEvent("regioncollapsed", region);
29010     },
29011     
29012     onRegionExpanded : function(region){
29013         this.fireEvent("regionexpanded", region);
29014     },
29015         
29016     /**
29017      * Returns the size of the current view. This method normalizes document.body and element embedded layouts and
29018      * performs box-model adjustments.
29019      * @return {Object} The size as an object {width: (the width), height: (the height)}
29020      */
29021     getViewSize : function(){
29022         var size;
29023         if(this.el.dom != document.body){
29024             size = this.el.getSize();
29025         }else{
29026             size = {width: Roo.lib.Dom.getViewWidth(), height: Roo.lib.Dom.getViewHeight()};
29027         }
29028         size.width -= this.el.getBorderWidth("lr")-this.el.getPadding("lr");
29029         size.height -= this.el.getBorderWidth("tb")-this.el.getPadding("tb");
29030         return size;
29031     },
29032     
29033     /**
29034      * Returns the Element this layout is bound to.
29035      * @return {Roo.Element}
29036      */
29037     getEl : function(){
29038         return this.el;
29039     },
29040     
29041     /**
29042      * Returns the specified region.
29043      * @param {String} target The region key ('center', 'north', 'south', 'east' or 'west')
29044      * @return {Roo.LayoutRegion}
29045      */
29046     getRegion : function(target){
29047         return this.regions[target.toLowerCase()];
29048     },
29049     
29050     onWindowResize : function(){
29051         if(this.monitorWindowResize){
29052             this.layout();
29053         }
29054     }
29055 });/*
29056  * Based on:
29057  * Ext JS Library 1.1.1
29058  * Copyright(c) 2006-2007, Ext JS, LLC.
29059  *
29060  * Originally Released Under LGPL - original licence link has changed is not relivant.
29061  *
29062  * Fork - LGPL
29063  * <script type="text/javascript">
29064  */
29065 /**
29066  * @class Roo.BorderLayout
29067  * @extends Roo.LayoutManager
29068  * This class represents a common layout manager used in desktop applications. For screenshots and more details,
29069  * please see: <br><br>
29070  * <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>
29071  * <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>
29072  * Example:
29073  <pre><code>
29074  var layout = new Roo.BorderLayout(document.body, {
29075     north: {
29076         initialSize: 25,
29077         titlebar: false
29078     },
29079     west: {
29080         split:true,
29081         initialSize: 200,
29082         minSize: 175,
29083         maxSize: 400,
29084         titlebar: true,
29085         collapsible: true
29086     },
29087     east: {
29088         split:true,
29089         initialSize: 202,
29090         minSize: 175,
29091         maxSize: 400,
29092         titlebar: true,
29093         collapsible: true
29094     },
29095     south: {
29096         split:true,
29097         initialSize: 100,
29098         minSize: 100,
29099         maxSize: 200,
29100         titlebar: true,
29101         collapsible: true
29102     },
29103     center: {
29104         titlebar: true,
29105         autoScroll:true,
29106         resizeTabs: true,
29107         minTabWidth: 50,
29108         preferredTabWidth: 150
29109     }
29110 });
29111
29112 // shorthand
29113 var CP = Roo.ContentPanel;
29114
29115 layout.beginUpdate();
29116 layout.add("north", new CP("north", "North"));
29117 layout.add("south", new CP("south", {title: "South", closable: true}));
29118 layout.add("west", new CP("west", {title: "West"}));
29119 layout.add("east", new CP("autoTabs", {title: "Auto Tabs", closable: true}));
29120 layout.add("center", new CP("center1", {title: "Close Me", closable: true}));
29121 layout.add("center", new CP("center2", {title: "Center Panel", closable: false}));
29122 layout.getRegion("center").showPanel("center1");
29123 layout.endUpdate();
29124 </code></pre>
29125
29126 <b>The container the layout is rendered into can be either the body element or any other element.
29127 If it is not the body element, the container needs to either be an absolute positioned element,
29128 or you will need to add "position:relative" to the css of the container.  You will also need to specify
29129 the container size if it is not the body element.</b>
29130
29131 * @constructor
29132 * Create a new BorderLayout
29133 * @param {String/HTMLElement/Element} container The container this layout is bound to
29134 * @param {Object} config Configuration options
29135  */
29136 Roo.BorderLayout = function(container, config){
29137     config = config || {};
29138     Roo.BorderLayout.superclass.constructor.call(this, container, config);
29139     this.factory = config.factory || Roo.BorderLayout.RegionFactory;
29140     for(var i = 0, len = this.factory.validRegions.length; i < len; i++) {
29141         var target = this.factory.validRegions[i];
29142         if(config[target]){
29143             this.addRegion(target, config[target]);
29144         }
29145     }
29146 };
29147
29148 Roo.extend(Roo.BorderLayout, Roo.LayoutManager, {
29149     /**
29150      * Creates and adds a new region if it doesn't already exist.
29151      * @param {String} target The target region key (north, south, east, west or center).
29152      * @param {Object} config The regions config object
29153      * @return {BorderLayoutRegion} The new region
29154      */
29155     addRegion : function(target, config){
29156         if(!this.regions[target]){
29157             var r = this.factory.create(target, this, config);
29158             this.bindRegion(target, r);
29159         }
29160         return this.regions[target];
29161     },
29162
29163     // private (kinda)
29164     bindRegion : function(name, r){
29165         this.regions[name] = r;
29166         r.on("visibilitychange", this.layout, this);
29167         r.on("paneladded", this.layout, this);
29168         r.on("panelremoved", this.layout, this);
29169         r.on("invalidated", this.layout, this);
29170         r.on("resized", this.onRegionResized, this);
29171         r.on("collapsed", this.onRegionCollapsed, this);
29172         r.on("expanded", this.onRegionExpanded, this);
29173     },
29174
29175     /**
29176      * Performs a layout update.
29177      */
29178     layout : function(){
29179         if(this.updating) return;
29180         var size = this.getViewSize();
29181         var w = size.width;
29182         var h = size.height;
29183         var centerW = w;
29184         var centerH = h;
29185         var centerY = 0;
29186         var centerX = 0;
29187         //var x = 0, y = 0;
29188
29189         var rs = this.regions;
29190         var north = rs["north"];
29191         var south = rs["south"]; 
29192         var west = rs["west"];
29193         var east = rs["east"];
29194         var center = rs["center"];
29195         //if(this.hideOnLayout){ // not supported anymore
29196             //c.el.setStyle("display", "none");
29197         //}
29198         if(north && north.isVisible()){
29199             var b = north.getBox();
29200             var m = north.getMargins();
29201             b.width = w - (m.left+m.right);
29202             b.x = m.left;
29203             b.y = m.top;
29204             centerY = b.height + b.y + m.bottom;
29205             centerH -= centerY;
29206             north.updateBox(this.safeBox(b));
29207         }
29208         if(south && south.isVisible()){
29209             var b = south.getBox();
29210             var m = south.getMargins();
29211             b.width = w - (m.left+m.right);
29212             b.x = m.left;
29213             var totalHeight = (b.height + m.top + m.bottom);
29214             b.y = h - totalHeight + m.top;
29215             centerH -= totalHeight;
29216             south.updateBox(this.safeBox(b));
29217         }
29218         if(west && west.isVisible()){
29219             var b = west.getBox();
29220             var m = west.getMargins();
29221             b.height = centerH - (m.top+m.bottom);
29222             b.x = m.left;
29223             b.y = centerY + m.top;
29224             var totalWidth = (b.width + m.left + m.right);
29225             centerX += totalWidth;
29226             centerW -= totalWidth;
29227             west.updateBox(this.safeBox(b));
29228         }
29229         if(east && east.isVisible()){
29230             var b = east.getBox();
29231             var m = east.getMargins();
29232             b.height = centerH - (m.top+m.bottom);
29233             var totalWidth = (b.width + m.left + m.right);
29234             b.x = w - totalWidth + m.left;
29235             b.y = centerY + m.top;
29236             centerW -= totalWidth;
29237             east.updateBox(this.safeBox(b));
29238         }
29239         if(center){
29240             var m = center.getMargins();
29241             var centerBox = {
29242                 x: centerX + m.left,
29243                 y: centerY + m.top,
29244                 width: centerW - (m.left+m.right),
29245                 height: centerH - (m.top+m.bottom)
29246             };
29247             //if(this.hideOnLayout){
29248                 //center.el.setStyle("display", "block");
29249             //}
29250             center.updateBox(this.safeBox(centerBox));
29251         }
29252         this.el.repaint();
29253         this.fireEvent("layout", this);
29254     },
29255
29256     // private
29257     safeBox : function(box){
29258         box.width = Math.max(0, box.width);
29259         box.height = Math.max(0, box.height);
29260         return box;
29261     },
29262
29263     /**
29264      * Adds a ContentPanel (or subclass) to this layout.
29265      * @param {String} target The target region key (north, south, east, west or center).
29266      * @param {Roo.ContentPanel} panel The panel to add
29267      * @return {Roo.ContentPanel} The added panel
29268      */
29269     add : function(target, panel){
29270          
29271         target = target.toLowerCase();
29272         return this.regions[target].add(panel);
29273     },
29274
29275     /**
29276      * Remove a ContentPanel (or subclass) to this layout.
29277      * @param {String} target The target region key (north, south, east, west or center).
29278      * @param {Number/String/Roo.ContentPanel} panel The index, id or panel to remove
29279      * @return {Roo.ContentPanel} The removed panel
29280      */
29281     remove : function(target, panel){
29282         target = target.toLowerCase();
29283         return this.regions[target].remove(panel);
29284     },
29285
29286     /**
29287      * Searches all regions for a panel with the specified id
29288      * @param {String} panelId
29289      * @return {Roo.ContentPanel} The panel or null if it wasn't found
29290      */
29291     findPanel : function(panelId){
29292         var rs = this.regions;
29293         for(var target in rs){
29294             if(typeof rs[target] != "function"){
29295                 var p = rs[target].getPanel(panelId);
29296                 if(p){
29297                     return p;
29298                 }
29299             }
29300         }
29301         return null;
29302     },
29303
29304     /**
29305      * Searches all regions for a panel with the specified id and activates (shows) it.
29306      * @param {String/ContentPanel} panelId The panels id or the panel itself
29307      * @return {Roo.ContentPanel} The shown panel or null
29308      */
29309     showPanel : function(panelId) {
29310       var rs = this.regions;
29311       for(var target in rs){
29312          var r = rs[target];
29313          if(typeof r != "function"){
29314             if(r.hasPanel(panelId)){
29315                return r.showPanel(panelId);
29316             }
29317          }
29318       }
29319       return null;
29320    },
29321
29322    /**
29323      * Restores this layout's state using Roo.state.Manager or the state provided by the passed provider.
29324      * @param {Roo.state.Provider} provider (optional) An alternate state provider
29325      */
29326     restoreState : function(provider){
29327         if(!provider){
29328             provider = Roo.state.Manager;
29329         }
29330         var sm = new Roo.LayoutStateManager();
29331         sm.init(this, provider);
29332     },
29333
29334     /**
29335      * Adds a batch of multiple ContentPanels dynamically by passing a special regions config object.  This config
29336      * object should contain properties for each region to add ContentPanels to, and each property's value should be
29337      * a valid ContentPanel config object.  Example:
29338      * <pre><code>
29339 // Create the main layout
29340 var layout = new Roo.BorderLayout('main-ct', {
29341     west: {
29342         split:true,
29343         minSize: 175,
29344         titlebar: true
29345     },
29346     center: {
29347         title:'Components'
29348     }
29349 }, 'main-ct');
29350
29351 // Create and add multiple ContentPanels at once via configs
29352 layout.batchAdd({
29353    west: {
29354        id: 'source-files',
29355        autoCreate:true,
29356        title:'Ext Source Files',
29357        autoScroll:true,
29358        fitToFrame:true
29359    },
29360    center : {
29361        el: cview,
29362        autoScroll:true,
29363        fitToFrame:true,
29364        toolbar: tb,
29365        resizeEl:'cbody'
29366    }
29367 });
29368 </code></pre>
29369      * @param {Object} regions An object containing ContentPanel configs by region name
29370      */
29371     batchAdd : function(regions){
29372         this.beginUpdate();
29373         for(var rname in regions){
29374             var lr = this.regions[rname];
29375             if(lr){
29376                 this.addTypedPanels(lr, regions[rname]);
29377             }
29378         }
29379         this.endUpdate();
29380     },
29381
29382     // private
29383     addTypedPanels : function(lr, ps){
29384         if(typeof ps == 'string'){
29385             lr.add(new Roo.ContentPanel(ps));
29386         }
29387         else if(ps instanceof Array){
29388             for(var i =0, len = ps.length; i < len; i++){
29389                 this.addTypedPanels(lr, ps[i]);
29390             }
29391         }
29392         else if(!ps.events){ // raw config?
29393             var el = ps.el;
29394             delete ps.el; // prevent conflict
29395             lr.add(new Roo.ContentPanel(el || Roo.id(), ps));
29396         }
29397         else {  // panel object assumed!
29398             lr.add(ps);
29399         }
29400     },
29401     /**
29402      * Adds a xtype elements to the layout.
29403      * <pre><code>
29404
29405 layout.addxtype({
29406        xtype : 'ContentPanel',
29407        region: 'west',
29408        items: [ .... ]
29409    }
29410 );
29411
29412 layout.addxtype({
29413         xtype : 'NestedLayoutPanel',
29414         region: 'west',
29415         layout: {
29416            center: { },
29417            west: { }   
29418         },
29419         items : [ ... list of content panels or nested layout panels.. ]
29420    }
29421 );
29422 </code></pre>
29423      * @param {Object} cfg Xtype definition of item to add.
29424      */
29425     addxtype : function(cfg)
29426     {
29427         // basically accepts a pannel...
29428         // can accept a layout region..!?!?
29429        // console.log('BorderLayout add ' + cfg.xtype)
29430         
29431         if (!cfg.xtype.match(/Panel$/)) {
29432             return false;
29433         }
29434         var ret = false;
29435         var region = cfg.region;
29436         delete cfg.region;
29437         
29438           
29439         var xitems = [];
29440         if (cfg.items) {
29441             xitems = cfg.items;
29442             delete cfg.items;
29443         }
29444         
29445         
29446         switch(cfg.xtype) 
29447         {
29448             case 'ContentPanel':  // ContentPanel (el, cfg)
29449             case 'ScrollPanel':  // ContentPanel (el, cfg)
29450                 if(cfg.autoCreate) {
29451                     ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
29452                 } else {
29453                     var el = this.el.createChild();
29454                     ret = new Roo[cfg.xtype](el, cfg); // new panel!!!!!
29455                 }
29456                 
29457                 this.add(region, ret);
29458                 break;
29459             
29460             
29461             case 'TreePanel': // our new panel!
29462                 cfg.el = this.el.createChild();
29463                 ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
29464                 this.add(region, ret);
29465                 break;
29466             
29467             case 'NestedLayoutPanel': 
29468                 // create a new Layout (which is  a Border Layout...
29469                 var el = this.el.createChild();
29470                 var clayout = cfg.layout;
29471                 delete cfg.layout;
29472                 clayout.items   = clayout.items  || [];
29473                 // replace this exitems with the clayout ones..
29474                 xitems = clayout.items;
29475                  
29476                 
29477                 if (region == 'center' && this.active && this.getRegion('center').panels.length < 1) {
29478                     cfg.background = false;
29479                 }
29480                 var layout = new Roo.BorderLayout(el, clayout);
29481                 
29482                 ret = new Roo[cfg.xtype](layout, cfg); // new panel!!!!!
29483                 //console.log('adding nested layout panel '  + cfg.toSource());
29484                 this.add(region, ret);
29485                 
29486                 break;
29487                 
29488             case 'GridPanel': 
29489             
29490                 // needs grid and region
29491                 
29492                 //var el = this.getRegion(region).el.createChild();
29493                 var el = this.el.createChild();
29494                 // create the grid first...
29495                 
29496                 var grid = new Roo.grid[cfg.grid.xtype](el, cfg.grid);
29497                 delete cfg.grid;
29498                 if (region == 'center' && this.active ) {
29499                     cfg.background = false;
29500                 }
29501                 ret = new Roo[cfg.xtype](grid, cfg); // new panel!!!!!
29502                 
29503                 this.add(region, ret);
29504                 if (cfg.background) {
29505                     ret.on('activate', function(gp) {
29506                         if (!gp.grid.rendered) {
29507                             gp.grid.render();
29508                         }
29509                     });
29510                 } else {
29511                     grid.render();
29512                 }
29513                 break;
29514            
29515                
29516                 
29517                 
29518             default: 
29519                 alert("Can not add '" + cfg.xtype + "' to BorderLayout");
29520                 return null;
29521              // GridPanel (grid, cfg)
29522             
29523         }
29524         this.beginUpdate();
29525         // add children..
29526         Roo.each(xitems, function(i)  {
29527             ret.addxtype(i);
29528         });
29529         this.endUpdate();
29530         return ret;
29531         
29532     }
29533 });
29534
29535 /**
29536  * Shortcut for creating a new BorderLayout object and adding one or more ContentPanels to it in a single step, handling
29537  * the beginUpdate and endUpdate calls internally.  The key to this method is the <b>panels</b> property that can be
29538  * provided with each region config, which allows you to add ContentPanel configs in addition to the region configs
29539  * during creation.  The following code is equivalent to the constructor-based example at the beginning of this class:
29540  * <pre><code>
29541 // shorthand
29542 var CP = Roo.ContentPanel;
29543
29544 var layout = Roo.BorderLayout.create({
29545     north: {
29546         initialSize: 25,
29547         titlebar: false,
29548         panels: [new CP("north", "North")]
29549     },
29550     west: {
29551         split:true,
29552         initialSize: 200,
29553         minSize: 175,
29554         maxSize: 400,
29555         titlebar: true,
29556         collapsible: true,
29557         panels: [new CP("west", {title: "West"})]
29558     },
29559     east: {
29560         split:true,
29561         initialSize: 202,
29562         minSize: 175,
29563         maxSize: 400,
29564         titlebar: true,
29565         collapsible: true,
29566         panels: [new CP("autoTabs", {title: "Auto Tabs", closable: true})]
29567     },
29568     south: {
29569         split:true,
29570         initialSize: 100,
29571         minSize: 100,
29572         maxSize: 200,
29573         titlebar: true,
29574         collapsible: true,
29575         panels: [new CP("south", {title: "South", closable: true})]
29576     },
29577     center: {
29578         titlebar: true,
29579         autoScroll:true,
29580         resizeTabs: true,
29581         minTabWidth: 50,
29582         preferredTabWidth: 150,
29583         panels: [
29584             new CP("center1", {title: "Close Me", closable: true}),
29585             new CP("center2", {title: "Center Panel", closable: false})
29586         ]
29587     }
29588 }, document.body);
29589
29590 layout.getRegion("center").showPanel("center1");
29591 </code></pre>
29592  * @param config
29593  * @param targetEl
29594  */
29595 Roo.BorderLayout.create = function(config, targetEl){
29596     var layout = new Roo.BorderLayout(targetEl || document.body, config);
29597     layout.beginUpdate();
29598     var regions = Roo.BorderLayout.RegionFactory.validRegions;
29599     for(var j = 0, jlen = regions.length; j < jlen; j++){
29600         var lr = regions[j];
29601         if(layout.regions[lr] && config[lr].panels){
29602             var r = layout.regions[lr];
29603             var ps = config[lr].panels;
29604             layout.addTypedPanels(r, ps);
29605         }
29606     }
29607     layout.endUpdate();
29608     return layout;
29609 };
29610
29611 // private
29612 Roo.BorderLayout.RegionFactory = {
29613     // private
29614     validRegions : ["north","south","east","west","center"],
29615
29616     // private
29617     create : function(target, mgr, config){
29618         target = target.toLowerCase();
29619         if(config.lightweight || config.basic){
29620             return new Roo.BasicLayoutRegion(mgr, config, target);
29621         }
29622         switch(target){
29623             case "north":
29624                 return new Roo.NorthLayoutRegion(mgr, config);
29625             case "south":
29626                 return new Roo.SouthLayoutRegion(mgr, config);
29627             case "east":
29628                 return new Roo.EastLayoutRegion(mgr, config);
29629             case "west":
29630                 return new Roo.WestLayoutRegion(mgr, config);
29631             case "center":
29632                 return new Roo.CenterLayoutRegion(mgr, config);
29633         }
29634         throw 'Layout region "'+target+'" not supported.';
29635     }
29636 };/*
29637  * Based on:
29638  * Ext JS Library 1.1.1
29639  * Copyright(c) 2006-2007, Ext JS, LLC.
29640  *
29641  * Originally Released Under LGPL - original licence link has changed is not relivant.
29642  *
29643  * Fork - LGPL
29644  * <script type="text/javascript">
29645  */
29646  
29647 /**
29648  * @class Roo.BasicLayoutRegion
29649  * @extends Roo.util.Observable
29650  * This class represents a lightweight region in a layout manager. This region does not move dom nodes
29651  * and does not have a titlebar, tabs or any other features. All it does is size and position 
29652  * panels. To create a BasicLayoutRegion, add lightweight:true or basic:true to your regions config.
29653  */
29654 Roo.BasicLayoutRegion = function(mgr, config, pos, skipConfig){
29655     this.mgr = mgr;
29656     this.position  = pos;
29657     this.events = {
29658         /**
29659          * @scope Roo.BasicLayoutRegion
29660          */
29661         
29662         /**
29663          * @event beforeremove
29664          * Fires before a panel is removed (or closed). To cancel the removal set "e.cancel = true" on the event argument.
29665          * @param {Roo.LayoutRegion} this
29666          * @param {Roo.ContentPanel} panel The panel
29667          * @param {Object} e The cancel event object
29668          */
29669         "beforeremove" : true,
29670         /**
29671          * @event invalidated
29672          * Fires when the layout for this region is changed.
29673          * @param {Roo.LayoutRegion} this
29674          */
29675         "invalidated" : true,
29676         /**
29677          * @event visibilitychange
29678          * Fires when this region is shown or hidden 
29679          * @param {Roo.LayoutRegion} this
29680          * @param {Boolean} visibility true or false
29681          */
29682         "visibilitychange" : true,
29683         /**
29684          * @event paneladded
29685          * Fires when a panel is added. 
29686          * @param {Roo.LayoutRegion} this
29687          * @param {Roo.ContentPanel} panel The panel
29688          */
29689         "paneladded" : true,
29690         /**
29691          * @event panelremoved
29692          * Fires when a panel is removed. 
29693          * @param {Roo.LayoutRegion} this
29694          * @param {Roo.ContentPanel} panel The panel
29695          */
29696         "panelremoved" : true,
29697         /**
29698          * @event collapsed
29699          * Fires when this region is collapsed.
29700          * @param {Roo.LayoutRegion} this
29701          */
29702         "collapsed" : true,
29703         /**
29704          * @event expanded
29705          * Fires when this region is expanded.
29706          * @param {Roo.LayoutRegion} this
29707          */
29708         "expanded" : true,
29709         /**
29710          * @event slideshow
29711          * Fires when this region is slid into view.
29712          * @param {Roo.LayoutRegion} this
29713          */
29714         "slideshow" : true,
29715         /**
29716          * @event slidehide
29717          * Fires when this region slides out of view. 
29718          * @param {Roo.LayoutRegion} this
29719          */
29720         "slidehide" : true,
29721         /**
29722          * @event panelactivated
29723          * Fires when a panel is activated. 
29724          * @param {Roo.LayoutRegion} this
29725          * @param {Roo.ContentPanel} panel The activated panel
29726          */
29727         "panelactivated" : true,
29728         /**
29729          * @event resized
29730          * Fires when the user resizes this region. 
29731          * @param {Roo.LayoutRegion} this
29732          * @param {Number} newSize The new size (width for east/west, height for north/south)
29733          */
29734         "resized" : true
29735     };
29736     /** A collection of panels in this region. @type Roo.util.MixedCollection */
29737     this.panels = new Roo.util.MixedCollection();
29738     this.panels.getKey = this.getPanelId.createDelegate(this);
29739     this.box = null;
29740     this.activePanel = null;
29741     // ensure listeners are added...
29742     
29743     if (config.listeners || config.events) {
29744         Roo.BasicLayoutRegion.superclass.constructor.call(this, {
29745             listeners : config.listeners || {},
29746             events : config.events || {}
29747         });
29748     }
29749     
29750     if(skipConfig !== true){
29751         this.applyConfig(config);
29752     }
29753 };
29754
29755 Roo.extend(Roo.BasicLayoutRegion, Roo.util.Observable, {
29756     getPanelId : function(p){
29757         return p.getId();
29758     },
29759     
29760     applyConfig : function(config){
29761         this.margins = config.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
29762         this.config = config;
29763         
29764     },
29765     
29766     /**
29767      * Resizes the region to the specified size. For vertical regions (west, east) this adjusts 
29768      * the width, for horizontal (north, south) the height.
29769      * @param {Number} newSize The new width or height
29770      */
29771     resizeTo : function(newSize){
29772         var el = this.el ? this.el :
29773                  (this.activePanel ? this.activePanel.getEl() : null);
29774         if(el){
29775             switch(this.position){
29776                 case "east":
29777                 case "west":
29778                     el.setWidth(newSize);
29779                     this.fireEvent("resized", this, newSize);
29780                 break;
29781                 case "north":
29782                 case "south":
29783                     el.setHeight(newSize);
29784                     this.fireEvent("resized", this, newSize);
29785                 break;                
29786             }
29787         }
29788     },
29789     
29790     getBox : function(){
29791         return this.activePanel ? this.activePanel.getEl().getBox(false, true) : null;
29792     },
29793     
29794     getMargins : function(){
29795         return this.margins;
29796     },
29797     
29798     updateBox : function(box){
29799         this.box = box;
29800         var el = this.activePanel.getEl();
29801         el.dom.style.left = box.x + "px";
29802         el.dom.style.top = box.y + "px";
29803         this.activePanel.setSize(box.width, box.height);
29804     },
29805     
29806     /**
29807      * Returns the container element for this region.
29808      * @return {Roo.Element}
29809      */
29810     getEl : function(){
29811         return this.activePanel;
29812     },
29813     
29814     /**
29815      * Returns true if this region is currently visible.
29816      * @return {Boolean}
29817      */
29818     isVisible : function(){
29819         return this.activePanel ? true : false;
29820     },
29821     
29822     setActivePanel : function(panel){
29823         panel = this.getPanel(panel);
29824         if(this.activePanel && this.activePanel != panel){
29825             this.activePanel.setActiveState(false);
29826             this.activePanel.getEl().setLeftTop(-10000,-10000);
29827         }
29828         this.activePanel = panel;
29829         panel.setActiveState(true);
29830         if(this.box){
29831             panel.setSize(this.box.width, this.box.height);
29832         }
29833         this.fireEvent("panelactivated", this, panel);
29834         this.fireEvent("invalidated");
29835     },
29836     
29837     /**
29838      * Show the specified panel.
29839      * @param {Number/String/ContentPanel} panelId The panels index, id or the panel itself
29840      * @return {Roo.ContentPanel} The shown panel or null
29841      */
29842     showPanel : function(panel){
29843         if(panel = this.getPanel(panel)){
29844             this.setActivePanel(panel);
29845         }
29846         return panel;
29847     },
29848     
29849     /**
29850      * Get the active panel for this region.
29851      * @return {Roo.ContentPanel} The active panel or null
29852      */
29853     getActivePanel : function(){
29854         return this.activePanel;
29855     },
29856     
29857     /**
29858      * Add the passed ContentPanel(s)
29859      * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
29860      * @return {Roo.ContentPanel} The panel added (if only one was added)
29861      */
29862     add : function(panel){
29863         if(arguments.length > 1){
29864             for(var i = 0, len = arguments.length; i < len; i++) {
29865                 this.add(arguments[i]);
29866             }
29867             return null;
29868         }
29869         if(this.hasPanel(panel)){
29870             this.showPanel(panel);
29871             return panel;
29872         }
29873         var el = panel.getEl();
29874         if(el.dom.parentNode != this.mgr.el.dom){
29875             this.mgr.el.dom.appendChild(el.dom);
29876         }
29877         if(panel.setRegion){
29878             panel.setRegion(this);
29879         }
29880         this.panels.add(panel);
29881         el.setStyle("position", "absolute");
29882         if(!panel.background){
29883             this.setActivePanel(panel);
29884             if(this.config.initialSize && this.panels.getCount()==1){
29885                 this.resizeTo(this.config.initialSize);
29886             }
29887         }
29888         this.fireEvent("paneladded", this, panel);
29889         return panel;
29890     },
29891     
29892     /**
29893      * Returns true if the panel is in this region.
29894      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
29895      * @return {Boolean}
29896      */
29897     hasPanel : function(panel){
29898         if(typeof panel == "object"){ // must be panel obj
29899             panel = panel.getId();
29900         }
29901         return this.getPanel(panel) ? true : false;
29902     },
29903     
29904     /**
29905      * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
29906      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
29907      * @param {Boolean} preservePanel Overrides the config preservePanel option
29908      * @return {Roo.ContentPanel} The panel that was removed
29909      */
29910     remove : function(panel, preservePanel){
29911         panel = this.getPanel(panel);
29912         if(!panel){
29913             return null;
29914         }
29915         var e = {};
29916         this.fireEvent("beforeremove", this, panel, e);
29917         if(e.cancel === true){
29918             return null;
29919         }
29920         var panelId = panel.getId();
29921         this.panels.removeKey(panelId);
29922         return panel;
29923     },
29924     
29925     /**
29926      * Returns the panel specified or null if it's not in this region.
29927      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
29928      * @return {Roo.ContentPanel}
29929      */
29930     getPanel : function(id){
29931         if(typeof id == "object"){ // must be panel obj
29932             return id;
29933         }
29934         return this.panels.get(id);
29935     },
29936     
29937     /**
29938      * Returns this regions position (north/south/east/west/center).
29939      * @return {String} 
29940      */
29941     getPosition: function(){
29942         return this.position;    
29943     }
29944 });/*
29945  * Based on:
29946  * Ext JS Library 1.1.1
29947  * Copyright(c) 2006-2007, Ext JS, LLC.
29948  *
29949  * Originally Released Under LGPL - original licence link has changed is not relivant.
29950  *
29951  * Fork - LGPL
29952  * <script type="text/javascript">
29953  */
29954  
29955 /**
29956  * @class Roo.LayoutRegion
29957  * @extends Roo.BasicLayoutRegion
29958  * This class represents a region in a layout manager.
29959  * @cfg {Boolean}   collapsible     False to disable collapsing (defaults to true)
29960  * @cfg {Boolean}   collapsed       True to set the initial display to collapsed (defaults to false)
29961  * @cfg {Boolean}   floatable       False to disable floating (defaults to true)
29962  * @cfg {Object}    margins         Margins for the element (defaults to {top: 0, left: 0, right:0, bottom: 0})
29963  * @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})
29964  * @cfg {String}    tabPosition     "top" or "bottom" (defaults to "bottom")
29965  * @cfg {String}    collapsedTitle  Optional string message to display in the collapsed block of a north or south region
29966  * @cfg {Boolean}   alwaysShowTabs  True to always display tabs even when there is only 1 panel (defaults to false)
29967  * @cfg {Boolean}   autoScroll      True to enable overflow scrolling (defaults to false)
29968  * @cfg {Boolean}   titlebar        True to display a title bar (defaults to true)
29969  * @cfg {String}    title           The title for the region (overrides panel titles)
29970  * @cfg {Boolean}   animate         True to animate expand/collapse (defaults to false)
29971  * @cfg {Boolean}   autoHide        False to disable auto hiding when the mouse leaves the "floated" region (defaults to true)
29972  * @cfg {Boolean}   preservePanels  True to preserve removed panels so they can be readded later (defaults to false)
29973  * @cfg {Boolean}   closeOnTab      True to place the close icon on the tabs instead of the region titlebar (defaults to false)
29974  * @cfg {Boolean}   hideTabs        True to hide the tab strip (defaults to false)
29975  * @cfg {Boolean}   resizeTabs      True to enable automatic tab resizing. This will resize the tabs so they are all the same size and fit within
29976  *                      the space available, similar to FireFox 1.5 tabs (defaults to false)
29977  * @cfg {Number}    minTabWidth     The minimum tab width (defaults to 40)
29978  * @cfg {Number}    preferredTabWidth The preferred tab width (defaults to 150)
29979  * @cfg {Boolean}   showPin         True to show a pin button
29980  * @cfg {Boolean}   hidden          True to start the region hidden (defaults to false)
29981  * @cfg {Boolean}   hideWhenEmpty   True to hide the region when it has no panels
29982  * @cfg {Boolean}   disableTabTips  True to disable tab tooltips
29983  * @cfg {Number}    width           For East/West panels
29984  * @cfg {Number}    height          For North/South panels
29985  * @cfg {Boolean}   split           To show the splitter
29986  * @cfg {Boolean}   toolbar         xtype configuration for a toolbar - shows on right of tabbar
29987  */
29988 Roo.LayoutRegion = function(mgr, config, pos){
29989     Roo.LayoutRegion.superclass.constructor.call(this, mgr, config, pos, true);
29990     var dh = Roo.DomHelper;
29991     /** This region's container element 
29992     * @type Roo.Element */
29993     this.el = dh.append(mgr.el.dom, {tag: "div", cls: "x-layout-panel x-layout-panel-" + this.position}, true);
29994     /** This region's title element 
29995     * @type Roo.Element */
29996
29997     this.titleEl = dh.append(this.el.dom, {tag: "div", unselectable: "on", cls: "x-unselectable x-layout-panel-hd x-layout-title-"+this.position, children:[
29998         {tag: "span", cls: "x-unselectable x-layout-panel-hd-text", unselectable: "on", html: "&#160;"},
29999         {tag: "div", cls: "x-unselectable x-layout-panel-hd-tools", unselectable: "on"}
30000     ]}, true);
30001     this.titleEl.enableDisplayMode();
30002     /** This region's title text element 
30003     * @type HTMLElement */
30004     this.titleTextEl = this.titleEl.dom.firstChild;
30005     this.tools = Roo.get(this.titleEl.dom.childNodes[1], true);
30006     this.closeBtn = this.createTool(this.tools.dom, "x-layout-close");
30007     this.closeBtn.enableDisplayMode();
30008     this.closeBtn.on("click", this.closeClicked, this);
30009     this.closeBtn.hide();
30010
30011     this.createBody(config);
30012     this.visible = true;
30013     this.collapsed = false;
30014
30015     if(config.hideWhenEmpty){
30016         this.hide();
30017         this.on("paneladded", this.validateVisibility, this);
30018         this.on("panelremoved", this.validateVisibility, this);
30019     }
30020     this.applyConfig(config);
30021 };
30022
30023 Roo.extend(Roo.LayoutRegion, Roo.BasicLayoutRegion, {
30024
30025     createBody : function(){
30026         /** This region's body element 
30027         * @type Roo.Element */
30028         this.bodyEl = this.el.createChild({tag: "div", cls: "x-layout-panel-body"});
30029     },
30030
30031     applyConfig : function(c){
30032         if(c.collapsible && this.position != "center" && !this.collapsedEl){
30033             var dh = Roo.DomHelper;
30034             if(c.titlebar !== false){
30035                 this.collapseBtn = this.createTool(this.tools.dom, "x-layout-collapse-"+this.position);
30036                 this.collapseBtn.on("click", this.collapse, this);
30037                 this.collapseBtn.enableDisplayMode();
30038
30039                 if(c.showPin === true || this.showPin){
30040                     this.stickBtn = this.createTool(this.tools.dom, "x-layout-stick");
30041                     this.stickBtn.enableDisplayMode();
30042                     this.stickBtn.on("click", this.expand, this);
30043                     this.stickBtn.hide();
30044                 }
30045             }
30046             /** This region's collapsed element
30047             * @type Roo.Element */
30048             this.collapsedEl = dh.append(this.mgr.el.dom, {cls: "x-layout-collapsed x-layout-collapsed-"+this.position, children:[
30049                 {cls: "x-layout-collapsed-tools", children:[{cls: "x-layout-ctools-inner"}]}
30050             ]}, true);
30051             if(c.floatable !== false){
30052                this.collapsedEl.addClassOnOver("x-layout-collapsed-over");
30053                this.collapsedEl.on("click", this.collapseClick, this);
30054             }
30055
30056             if(c.collapsedTitle && (this.position == "north" || this.position== "south")) {
30057                 this.collapsedTitleTextEl = dh.append(this.collapsedEl.dom, {tag: "div", cls: "x-unselectable x-layout-panel-hd-text",
30058                    id: "message", unselectable: "on", style:{"float":"left"}});
30059                this.collapsedTitleTextEl.innerHTML = c.collapsedTitle;
30060              }
30061             this.expandBtn = this.createTool(this.collapsedEl.dom.firstChild.firstChild, "x-layout-expand-"+this.position);
30062             this.expandBtn.on("click", this.expand, this);
30063         }
30064         if(this.collapseBtn){
30065             this.collapseBtn.setVisible(c.collapsible == true);
30066         }
30067         this.cmargins = c.cmargins || this.cmargins ||
30068                          (this.position == "west" || this.position == "east" ?
30069                              {top: 0, left: 2, right:2, bottom: 0} :
30070                              {top: 2, left: 0, right:0, bottom: 2});
30071         this.margins = c.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
30072         this.bottomTabs = c.tabPosition != "top";
30073         this.autoScroll = c.autoScroll || false;
30074         if(this.autoScroll){
30075             this.bodyEl.setStyle("overflow", "auto");
30076         }else{
30077             this.bodyEl.setStyle("overflow", "hidden");
30078         }
30079         //if(c.titlebar !== false){
30080             if((!c.titlebar && !c.title) || c.titlebar === false){
30081                 this.titleEl.hide();
30082             }else{
30083                 this.titleEl.show();
30084                 if(c.title){
30085                     this.titleTextEl.innerHTML = c.title;
30086                 }
30087             }
30088         //}
30089         this.duration = c.duration || .30;
30090         this.slideDuration = c.slideDuration || .45;
30091         this.config = c;
30092         if(c.collapsed){
30093             this.collapse(true);
30094         }
30095         if(c.hidden){
30096             this.hide();
30097         }
30098     },
30099     /**
30100      * Returns true if this region is currently visible.
30101      * @return {Boolean}
30102      */
30103     isVisible : function(){
30104         return this.visible;
30105     },
30106
30107     /**
30108      * Updates the title for collapsed north/south regions (used with {@link #collapsedTitle} config option)
30109      * @param {String} title (optional) The title text (accepts HTML markup, defaults to the numeric character reference for a non-breaking space, "&amp;#160;")
30110      */
30111     setCollapsedTitle : function(title){
30112         title = title || "&#160;";
30113         if(this.collapsedTitleTextEl){
30114             this.collapsedTitleTextEl.innerHTML = title;
30115         }
30116     },
30117
30118     getBox : function(){
30119         var b;
30120         if(!this.collapsed){
30121             b = this.el.getBox(false, true);
30122         }else{
30123             b = this.collapsedEl.getBox(false, true);
30124         }
30125         return b;
30126     },
30127
30128     getMargins : function(){
30129         return this.collapsed ? this.cmargins : this.margins;
30130     },
30131
30132     highlight : function(){
30133         this.el.addClass("x-layout-panel-dragover");
30134     },
30135
30136     unhighlight : function(){
30137         this.el.removeClass("x-layout-panel-dragover");
30138     },
30139
30140     updateBox : function(box){
30141         this.box = box;
30142         if(!this.collapsed){
30143             this.el.dom.style.left = box.x + "px";
30144             this.el.dom.style.top = box.y + "px";
30145             this.updateBody(box.width, box.height);
30146         }else{
30147             this.collapsedEl.dom.style.left = box.x + "px";
30148             this.collapsedEl.dom.style.top = box.y + "px";
30149             this.collapsedEl.setSize(box.width, box.height);
30150         }
30151         if(this.tabs){
30152             this.tabs.autoSizeTabs();
30153         }
30154     },
30155
30156     updateBody : function(w, h){
30157         if(w !== null){
30158             this.el.setWidth(w);
30159             w -= this.el.getBorderWidth("rl");
30160             if(this.config.adjustments){
30161                 w += this.config.adjustments[0];
30162             }
30163         }
30164         if(h !== null){
30165             this.el.setHeight(h);
30166             h = this.titleEl && this.titleEl.isDisplayed() ? h - (this.titleEl.getHeight()||0) : h;
30167             h -= this.el.getBorderWidth("tb");
30168             if(this.config.adjustments){
30169                 h += this.config.adjustments[1];
30170             }
30171             this.bodyEl.setHeight(h);
30172             if(this.tabs){
30173                 h = this.tabs.syncHeight(h);
30174             }
30175         }
30176         if(this.panelSize){
30177             w = w !== null ? w : this.panelSize.width;
30178             h = h !== null ? h : this.panelSize.height;
30179         }
30180         if(this.activePanel){
30181             var el = this.activePanel.getEl();
30182             w = w !== null ? w : el.getWidth();
30183             h = h !== null ? h : el.getHeight();
30184             this.panelSize = {width: w, height: h};
30185             this.activePanel.setSize(w, h);
30186         }
30187         if(Roo.isIE && this.tabs){
30188             this.tabs.el.repaint();
30189         }
30190     },
30191
30192     /**
30193      * Returns the container element for this region.
30194      * @return {Roo.Element}
30195      */
30196     getEl : function(){
30197         return this.el;
30198     },
30199
30200     /**
30201      * Hides this region.
30202      */
30203     hide : function(){
30204         if(!this.collapsed){
30205             this.el.dom.style.left = "-2000px";
30206             this.el.hide();
30207         }else{
30208             this.collapsedEl.dom.style.left = "-2000px";
30209             this.collapsedEl.hide();
30210         }
30211         this.visible = false;
30212         this.fireEvent("visibilitychange", this, false);
30213     },
30214
30215     /**
30216      * Shows this region if it was previously hidden.
30217      */
30218     show : function(){
30219         if(!this.collapsed){
30220             this.el.show();
30221         }else{
30222             this.collapsedEl.show();
30223         }
30224         this.visible = true;
30225         this.fireEvent("visibilitychange", this, true);
30226     },
30227
30228     closeClicked : function(){
30229         if(this.activePanel){
30230             this.remove(this.activePanel);
30231         }
30232     },
30233
30234     collapseClick : function(e){
30235         if(this.isSlid){
30236            e.stopPropagation();
30237            this.slideIn();
30238         }else{
30239            e.stopPropagation();
30240            this.slideOut();
30241         }
30242     },
30243
30244     /**
30245      * Collapses this region.
30246      * @param {Boolean} skipAnim (optional) true to collapse the element without animation (if animate is true)
30247      */
30248     collapse : function(skipAnim){
30249         if(this.collapsed) return;
30250         this.collapsed = true;
30251         if(this.split){
30252             this.split.el.hide();
30253         }
30254         if(this.config.animate && skipAnim !== true){
30255             this.fireEvent("invalidated", this);
30256             this.animateCollapse();
30257         }else{
30258             this.el.setLocation(-20000,-20000);
30259             this.el.hide();
30260             this.collapsedEl.show();
30261             this.fireEvent("collapsed", this);
30262             this.fireEvent("invalidated", this);
30263         }
30264     },
30265
30266     animateCollapse : function(){
30267         // overridden
30268     },
30269
30270     /**
30271      * Expands this region if it was previously collapsed.
30272      * @param {Roo.EventObject} e The event that triggered the expand (or null if calling manually)
30273      * @param {Boolean} skipAnim (optional) true to expand the element without animation (if animate is true)
30274      */
30275     expand : function(e, skipAnim){
30276         if(e) e.stopPropagation();
30277         if(!this.collapsed || this.el.hasActiveFx()) return;
30278         if(this.isSlid){
30279             this.afterSlideIn();
30280             skipAnim = true;
30281         }
30282         this.collapsed = false;
30283         if(this.config.animate && skipAnim !== true){
30284             this.animateExpand();
30285         }else{
30286             this.el.show();
30287             if(this.split){
30288                 this.split.el.show();
30289             }
30290             this.collapsedEl.setLocation(-2000,-2000);
30291             this.collapsedEl.hide();
30292             this.fireEvent("invalidated", this);
30293             this.fireEvent("expanded", this);
30294         }
30295     },
30296
30297     animateExpand : function(){
30298         // overridden
30299     },
30300
30301     initTabs : function()
30302     {
30303         this.bodyEl.setStyle("overflow", "hidden");
30304         var ts = new Roo.TabPanel(
30305                 this.bodyEl.dom,
30306                 {
30307                     tabPosition: this.bottomTabs ? 'bottom' : 'top',
30308                     disableTooltips: this.config.disableTabTips,
30309                     toolbar : this.config.toolbar
30310                 }
30311         );
30312         if(this.config.hideTabs){
30313             ts.stripWrap.setDisplayed(false);
30314         }
30315         this.tabs = ts;
30316         ts.resizeTabs = this.config.resizeTabs === true;
30317         ts.minTabWidth = this.config.minTabWidth || 40;
30318         ts.maxTabWidth = this.config.maxTabWidth || 250;
30319         ts.preferredTabWidth = this.config.preferredTabWidth || 150;
30320         ts.monitorResize = false;
30321         ts.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden");
30322         ts.bodyEl.addClass('x-layout-tabs-body');
30323         this.panels.each(this.initPanelAsTab, this);
30324     },
30325
30326     initPanelAsTab : function(panel){
30327         var ti = this.tabs.addTab(panel.getEl().id, panel.getTitle(), null,
30328                     this.config.closeOnTab && panel.isClosable());
30329         if(panel.tabTip !== undefined){
30330             ti.setTooltip(panel.tabTip);
30331         }
30332         ti.on("activate", function(){
30333               this.setActivePanel(panel);
30334         }, this);
30335         if(this.config.closeOnTab){
30336             ti.on("beforeclose", function(t, e){
30337                 e.cancel = true;
30338                 this.remove(panel);
30339             }, this);
30340         }
30341         return ti;
30342     },
30343
30344     updatePanelTitle : function(panel, title){
30345         if(this.activePanel == panel){
30346             this.updateTitle(title);
30347         }
30348         if(this.tabs){
30349             var ti = this.tabs.getTab(panel.getEl().id);
30350             ti.setText(title);
30351             if(panel.tabTip !== undefined){
30352                 ti.setTooltip(panel.tabTip);
30353             }
30354         }
30355     },
30356
30357     updateTitle : function(title){
30358         if(this.titleTextEl && !this.config.title){
30359             this.titleTextEl.innerHTML = (typeof title != "undefined" && title.length > 0 ? title : "&#160;");
30360         }
30361     },
30362
30363     setActivePanel : function(panel){
30364         panel = this.getPanel(panel);
30365         if(this.activePanel && this.activePanel != panel){
30366             this.activePanel.setActiveState(false);
30367         }
30368         this.activePanel = panel;
30369         panel.setActiveState(true);
30370         if(this.panelSize){
30371             panel.setSize(this.panelSize.width, this.panelSize.height);
30372         }
30373         if(this.closeBtn){
30374             this.closeBtn.setVisible(!this.config.closeOnTab && !this.isSlid && panel.isClosable());
30375         }
30376         this.updateTitle(panel.getTitle());
30377         if(this.tabs){
30378             this.fireEvent("invalidated", this);
30379         }
30380         this.fireEvent("panelactivated", this, panel);
30381     },
30382
30383     /**
30384      * Shows the specified panel.
30385      * @param {Number/String/ContentPanel} panelId The panel's index, id or the panel itself
30386      * @return {Roo.ContentPanel} The shown panel, or null if a panel could not be found from panelId
30387      */
30388     showPanel : function(panel){
30389         if(panel = this.getPanel(panel)){
30390             if(this.tabs){
30391                 var tab = this.tabs.getTab(panel.getEl().id);
30392                 if(tab.isHidden()){
30393                     this.tabs.unhideTab(tab.id);
30394                 }
30395                 tab.activate();
30396             }else{
30397                 this.setActivePanel(panel);
30398             }
30399         }
30400         return panel;
30401     },
30402
30403     /**
30404      * Get the active panel for this region.
30405      * @return {Roo.ContentPanel} The active panel or null
30406      */
30407     getActivePanel : function(){
30408         return this.activePanel;
30409     },
30410
30411     validateVisibility : function(){
30412         if(this.panels.getCount() < 1){
30413             this.updateTitle("&#160;");
30414             this.closeBtn.hide();
30415             this.hide();
30416         }else{
30417             if(!this.isVisible()){
30418                 this.show();
30419             }
30420         }
30421     },
30422
30423     /**
30424      * Adds the passed ContentPanel(s) to this region.
30425      * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
30426      * @return {Roo.ContentPanel} The panel added (if only one was added; null otherwise)
30427      */
30428     add : function(panel){
30429         if(arguments.length > 1){
30430             for(var i = 0, len = arguments.length; i < len; i++) {
30431                 this.add(arguments[i]);
30432             }
30433             return null;
30434         }
30435         if(this.hasPanel(panel)){
30436             this.showPanel(panel);
30437             return panel;
30438         }
30439         panel.setRegion(this);
30440         this.panels.add(panel);
30441         if(this.panels.getCount() == 1 && !this.config.alwaysShowTabs){
30442             this.bodyEl.dom.appendChild(panel.getEl().dom);
30443             if(panel.background !== true){
30444                 this.setActivePanel(panel);
30445             }
30446             this.fireEvent("paneladded", this, panel);
30447             return panel;
30448         }
30449         if(!this.tabs){
30450             this.initTabs();
30451         }else{
30452             this.initPanelAsTab(panel);
30453         }
30454         if(panel.background !== true){
30455             this.tabs.activate(panel.getEl().id);
30456         }
30457         this.fireEvent("paneladded", this, panel);
30458         return panel;
30459     },
30460
30461     /**
30462      * Hides the tab for the specified panel.
30463      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
30464      */
30465     hidePanel : function(panel){
30466         if(this.tabs && (panel = this.getPanel(panel))){
30467             this.tabs.hideTab(panel.getEl().id);
30468         }
30469     },
30470
30471     /**
30472      * Unhides the tab for a previously hidden panel.
30473      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
30474      */
30475     unhidePanel : function(panel){
30476         if(this.tabs && (panel = this.getPanel(panel))){
30477             this.tabs.unhideTab(panel.getEl().id);
30478         }
30479     },
30480
30481     clearPanels : function(){
30482         while(this.panels.getCount() > 0){
30483              this.remove(this.panels.first());
30484         }
30485     },
30486
30487     /**
30488      * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
30489      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
30490      * @param {Boolean} preservePanel Overrides the config preservePanel option
30491      * @return {Roo.ContentPanel} The panel that was removed
30492      */
30493     remove : function(panel, preservePanel){
30494         panel = this.getPanel(panel);
30495         if(!panel){
30496             return null;
30497         }
30498         var e = {};
30499         this.fireEvent("beforeremove", this, panel, e);
30500         if(e.cancel === true){
30501             return null;
30502         }
30503         preservePanel = (typeof preservePanel != "undefined" ? preservePanel : (this.config.preservePanels === true || panel.preserve === true));
30504         var panelId = panel.getId();
30505         this.panels.removeKey(panelId);
30506         if(preservePanel){
30507             document.body.appendChild(panel.getEl().dom);
30508         }
30509         if(this.tabs){
30510             this.tabs.removeTab(panel.getEl().id);
30511         }else if (!preservePanel){
30512             this.bodyEl.dom.removeChild(panel.getEl().dom);
30513         }
30514         if(this.panels.getCount() == 1 && this.tabs && !this.config.alwaysShowTabs){
30515             var p = this.panels.first();
30516             var tempEl = document.createElement("div"); // temp holder to keep IE from deleting the node
30517             tempEl.appendChild(p.getEl().dom);
30518             this.bodyEl.update("");
30519             this.bodyEl.dom.appendChild(p.getEl().dom);
30520             tempEl = null;
30521             this.updateTitle(p.getTitle());
30522             this.tabs = null;
30523             this.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden");
30524             this.setActivePanel(p);
30525         }
30526         panel.setRegion(null);
30527         if(this.activePanel == panel){
30528             this.activePanel = null;
30529         }
30530         if(this.config.autoDestroy !== false && preservePanel !== true){
30531             try{panel.destroy();}catch(e){}
30532         }
30533         this.fireEvent("panelremoved", this, panel);
30534         return panel;
30535     },
30536
30537     /**
30538      * Returns the TabPanel component used by this region
30539      * @return {Roo.TabPanel}
30540      */
30541     getTabs : function(){
30542         return this.tabs;
30543     },
30544
30545     createTool : function(parentEl, className){
30546         var btn = Roo.DomHelper.append(parentEl, {tag: "div", cls: "x-layout-tools-button",
30547             children: [{tag: "div", cls: "x-layout-tools-button-inner " + className, html: "&#160;"}]}, true);
30548         btn.addClassOnOver("x-layout-tools-button-over");
30549         return btn;
30550     }
30551 });/*
30552  * Based on:
30553  * Ext JS Library 1.1.1
30554  * Copyright(c) 2006-2007, Ext JS, LLC.
30555  *
30556  * Originally Released Under LGPL - original licence link has changed is not relivant.
30557  *
30558  * Fork - LGPL
30559  * <script type="text/javascript">
30560  */
30561  
30562
30563
30564 /**
30565  * @class Roo.SplitLayoutRegion
30566  * @extends Roo.LayoutRegion
30567  * Adds a splitbar and other (private) useful functionality to a {@link Roo.LayoutRegion}.
30568  */
30569 Roo.SplitLayoutRegion = function(mgr, config, pos, cursor){
30570     this.cursor = cursor;
30571     Roo.SplitLayoutRegion.superclass.constructor.call(this, mgr, config, pos);
30572 };
30573
30574 Roo.extend(Roo.SplitLayoutRegion, Roo.LayoutRegion, {
30575     splitTip : "Drag to resize.",
30576     collapsibleSplitTip : "Drag to resize. Double click to hide.",
30577     useSplitTips : false,
30578
30579     applyConfig : function(config){
30580         Roo.SplitLayoutRegion.superclass.applyConfig.call(this, config);
30581         if(config.split){
30582             if(!this.split){
30583                 var splitEl = Roo.DomHelper.append(this.mgr.el.dom, 
30584                         {tag: "div", id: this.el.id + "-split", cls: "x-layout-split x-layout-split-"+this.position, html: "&#160;"});
30585                 /** The SplitBar for this region 
30586                 * @type Roo.SplitBar */
30587                 this.split = new Roo.SplitBar(splitEl, this.el, this.orientation);
30588                 this.split.on("moved", this.onSplitMove, this);
30589                 this.split.useShim = config.useShim === true;
30590                 this.split.getMaximumSize = this[this.position == 'north' || this.position == 'south' ? 'getVMaxSize' : 'getHMaxSize'].createDelegate(this);
30591                 if(this.useSplitTips){
30592                     this.split.el.dom.title = config.collapsible ? this.collapsibleSplitTip : this.splitTip;
30593                 }
30594                 if(config.collapsible){
30595                     this.split.el.on("dblclick", this.collapse,  this);
30596                 }
30597             }
30598             if(typeof config.minSize != "undefined"){
30599                 this.split.minSize = config.minSize;
30600             }
30601             if(typeof config.maxSize != "undefined"){
30602                 this.split.maxSize = config.maxSize;
30603             }
30604             if(config.hideWhenEmpty || config.hidden || config.collapsed){
30605                 this.hideSplitter();
30606             }
30607         }
30608     },
30609
30610     getHMaxSize : function(){
30611          var cmax = this.config.maxSize || 10000;
30612          var center = this.mgr.getRegion("center");
30613          return Math.min(cmax, (this.el.getWidth()+center.getEl().getWidth())-center.getMinWidth());
30614     },
30615
30616     getVMaxSize : function(){
30617          var cmax = this.config.maxSize || 10000;
30618          var center = this.mgr.getRegion("center");
30619          return Math.min(cmax, (this.el.getHeight()+center.getEl().getHeight())-center.getMinHeight());
30620     },
30621
30622     onSplitMove : function(split, newSize){
30623         this.fireEvent("resized", this, newSize);
30624     },
30625     
30626     /** 
30627      * Returns the {@link Roo.SplitBar} for this region.
30628      * @return {Roo.SplitBar}
30629      */
30630     getSplitBar : function(){
30631         return this.split;
30632     },
30633     
30634     hide : function(){
30635         this.hideSplitter();
30636         Roo.SplitLayoutRegion.superclass.hide.call(this);
30637     },
30638
30639     hideSplitter : function(){
30640         if(this.split){
30641             this.split.el.setLocation(-2000,-2000);
30642             this.split.el.hide();
30643         }
30644     },
30645
30646     show : function(){
30647         if(this.split){
30648             this.split.el.show();
30649         }
30650         Roo.SplitLayoutRegion.superclass.show.call(this);
30651     },
30652     
30653     beforeSlide: function(){
30654         if(Roo.isGecko){// firefox overflow auto bug workaround
30655             this.bodyEl.clip();
30656             if(this.tabs) this.tabs.bodyEl.clip();
30657             if(this.activePanel){
30658                 this.activePanel.getEl().clip();
30659                 
30660                 if(this.activePanel.beforeSlide){
30661                     this.activePanel.beforeSlide();
30662                 }
30663             }
30664         }
30665     },
30666     
30667     afterSlide : function(){
30668         if(Roo.isGecko){// firefox overflow auto bug workaround
30669             this.bodyEl.unclip();
30670             if(this.tabs) this.tabs.bodyEl.unclip();
30671             if(this.activePanel){
30672                 this.activePanel.getEl().unclip();
30673                 if(this.activePanel.afterSlide){
30674                     this.activePanel.afterSlide();
30675                 }
30676             }
30677         }
30678     },
30679
30680     initAutoHide : function(){
30681         if(this.autoHide !== false){
30682             if(!this.autoHideHd){
30683                 var st = new Roo.util.DelayedTask(this.slideIn, this);
30684                 this.autoHideHd = {
30685                     "mouseout": function(e){
30686                         if(!e.within(this.el, true)){
30687                             st.delay(500);
30688                         }
30689                     },
30690                     "mouseover" : function(e){
30691                         st.cancel();
30692                     },
30693                     scope : this
30694                 };
30695             }
30696             this.el.on(this.autoHideHd);
30697         }
30698     },
30699
30700     clearAutoHide : function(){
30701         if(this.autoHide !== false){
30702             this.el.un("mouseout", this.autoHideHd.mouseout);
30703             this.el.un("mouseover", this.autoHideHd.mouseover);
30704         }
30705     },
30706
30707     clearMonitor : function(){
30708         Roo.get(document).un("click", this.slideInIf, this);
30709     },
30710
30711     // these names are backwards but not changed for compat
30712     slideOut : function(){
30713         if(this.isSlid || this.el.hasActiveFx()){
30714             return;
30715         }
30716         this.isSlid = true;
30717         if(this.collapseBtn){
30718             this.collapseBtn.hide();
30719         }
30720         this.closeBtnState = this.closeBtn.getStyle('display');
30721         this.closeBtn.hide();
30722         if(this.stickBtn){
30723             this.stickBtn.show();
30724         }
30725         this.el.show();
30726         this.el.alignTo(this.collapsedEl, this.getCollapseAnchor());
30727         this.beforeSlide();
30728         this.el.setStyle("z-index", 10001);
30729         this.el.slideIn(this.getSlideAnchor(), {
30730             callback: function(){
30731                 this.afterSlide();
30732                 this.initAutoHide();
30733                 Roo.get(document).on("click", this.slideInIf, this);
30734                 this.fireEvent("slideshow", this);
30735             },
30736             scope: this,
30737             block: true
30738         });
30739     },
30740
30741     afterSlideIn : function(){
30742         this.clearAutoHide();
30743         this.isSlid = false;
30744         this.clearMonitor();
30745         this.el.setStyle("z-index", "");
30746         if(this.collapseBtn){
30747             this.collapseBtn.show();
30748         }
30749         this.closeBtn.setStyle('display', this.closeBtnState);
30750         if(this.stickBtn){
30751             this.stickBtn.hide();
30752         }
30753         this.fireEvent("slidehide", this);
30754     },
30755
30756     slideIn : function(cb){
30757         if(!this.isSlid || this.el.hasActiveFx()){
30758             Roo.callback(cb);
30759             return;
30760         }
30761         this.isSlid = false;
30762         this.beforeSlide();
30763         this.el.slideOut(this.getSlideAnchor(), {
30764             callback: function(){
30765                 this.el.setLeftTop(-10000, -10000);
30766                 this.afterSlide();
30767                 this.afterSlideIn();
30768                 Roo.callback(cb);
30769             },
30770             scope: this,
30771             block: true
30772         });
30773     },
30774     
30775     slideInIf : function(e){
30776         if(!e.within(this.el)){
30777             this.slideIn();
30778         }
30779     },
30780
30781     animateCollapse : function(){
30782         this.beforeSlide();
30783         this.el.setStyle("z-index", 20000);
30784         var anchor = this.getSlideAnchor();
30785         this.el.slideOut(anchor, {
30786             callback : function(){
30787                 this.el.setStyle("z-index", "");
30788                 this.collapsedEl.slideIn(anchor, {duration:.3});
30789                 this.afterSlide();
30790                 this.el.setLocation(-10000,-10000);
30791                 this.el.hide();
30792                 this.fireEvent("collapsed", this);
30793             },
30794             scope: this,
30795             block: true
30796         });
30797     },
30798
30799     animateExpand : function(){
30800         this.beforeSlide();
30801         this.el.alignTo(this.collapsedEl, this.getCollapseAnchor(), this.getExpandAdj());
30802         this.el.setStyle("z-index", 20000);
30803         this.collapsedEl.hide({
30804             duration:.1
30805         });
30806         this.el.slideIn(this.getSlideAnchor(), {
30807             callback : function(){
30808                 this.el.setStyle("z-index", "");
30809                 this.afterSlide();
30810                 if(this.split){
30811                     this.split.el.show();
30812                 }
30813                 this.fireEvent("invalidated", this);
30814                 this.fireEvent("expanded", this);
30815             },
30816             scope: this,
30817             block: true
30818         });
30819     },
30820
30821     anchors : {
30822         "west" : "left",
30823         "east" : "right",
30824         "north" : "top",
30825         "south" : "bottom"
30826     },
30827
30828     sanchors : {
30829         "west" : "l",
30830         "east" : "r",
30831         "north" : "t",
30832         "south" : "b"
30833     },
30834
30835     canchors : {
30836         "west" : "tl-tr",
30837         "east" : "tr-tl",
30838         "north" : "tl-bl",
30839         "south" : "bl-tl"
30840     },
30841
30842     getAnchor : function(){
30843         return this.anchors[this.position];
30844     },
30845
30846     getCollapseAnchor : function(){
30847         return this.canchors[this.position];
30848     },
30849
30850     getSlideAnchor : function(){
30851         return this.sanchors[this.position];
30852     },
30853
30854     getAlignAdj : function(){
30855         var cm = this.cmargins;
30856         switch(this.position){
30857             case "west":
30858                 return [0, 0];
30859             break;
30860             case "east":
30861                 return [0, 0];
30862             break;
30863             case "north":
30864                 return [0, 0];
30865             break;
30866             case "south":
30867                 return [0, 0];
30868             break;
30869         }
30870     },
30871
30872     getExpandAdj : function(){
30873         var c = this.collapsedEl, cm = this.cmargins;
30874         switch(this.position){
30875             case "west":
30876                 return [-(cm.right+c.getWidth()+cm.left), 0];
30877             break;
30878             case "east":
30879                 return [cm.right+c.getWidth()+cm.left, 0];
30880             break;
30881             case "north":
30882                 return [0, -(cm.top+cm.bottom+c.getHeight())];
30883             break;
30884             case "south":
30885                 return [0, cm.top+cm.bottom+c.getHeight()];
30886             break;
30887         }
30888     }
30889 });/*
30890  * Based on:
30891  * Ext JS Library 1.1.1
30892  * Copyright(c) 2006-2007, Ext JS, LLC.
30893  *
30894  * Originally Released Under LGPL - original licence link has changed is not relivant.
30895  *
30896  * Fork - LGPL
30897  * <script type="text/javascript">
30898  */
30899 /*
30900  * These classes are private internal classes
30901  */
30902 Roo.CenterLayoutRegion = function(mgr, config){
30903     Roo.LayoutRegion.call(this, mgr, config, "center");
30904     this.visible = true;
30905     this.minWidth = config.minWidth || 20;
30906     this.minHeight = config.minHeight || 20;
30907 };
30908
30909 Roo.extend(Roo.CenterLayoutRegion, Roo.LayoutRegion, {
30910     hide : function(){
30911         // center panel can't be hidden
30912     },
30913     
30914     show : function(){
30915         // center panel can't be hidden
30916     },
30917     
30918     getMinWidth: function(){
30919         return this.minWidth;
30920     },
30921     
30922     getMinHeight: function(){
30923         return this.minHeight;
30924     }
30925 });
30926
30927
30928 Roo.NorthLayoutRegion = function(mgr, config){
30929     Roo.LayoutRegion.call(this, mgr, config, "north", "n-resize");
30930     if(this.split){
30931         this.split.placement = Roo.SplitBar.TOP;
30932         this.split.orientation = Roo.SplitBar.VERTICAL;
30933         this.split.el.addClass("x-layout-split-v");
30934     }
30935     var size = config.initialSize || config.height;
30936     if(typeof size != "undefined"){
30937         this.el.setHeight(size);
30938     }
30939 };
30940 Roo.extend(Roo.NorthLayoutRegion, Roo.SplitLayoutRegion, {
30941     orientation: Roo.SplitBar.VERTICAL,
30942     getBox : function(){
30943         if(this.collapsed){
30944             return this.collapsedEl.getBox();
30945         }
30946         var box = this.el.getBox();
30947         if(this.split){
30948             box.height += this.split.el.getHeight();
30949         }
30950         return box;
30951     },
30952     
30953     updateBox : function(box){
30954         if(this.split && !this.collapsed){
30955             box.height -= this.split.el.getHeight();
30956             this.split.el.setLeft(box.x);
30957             this.split.el.setTop(box.y+box.height);
30958             this.split.el.setWidth(box.width);
30959         }
30960         if(this.collapsed){
30961             this.updateBody(box.width, null);
30962         }
30963         Roo.LayoutRegion.prototype.updateBox.call(this, box);
30964     }
30965 });
30966
30967 Roo.SouthLayoutRegion = function(mgr, config){
30968     Roo.SplitLayoutRegion.call(this, mgr, config, "south", "s-resize");
30969     if(this.split){
30970         this.split.placement = Roo.SplitBar.BOTTOM;
30971         this.split.orientation = Roo.SplitBar.VERTICAL;
30972         this.split.el.addClass("x-layout-split-v");
30973     }
30974     var size = config.initialSize || config.height;
30975     if(typeof size != "undefined"){
30976         this.el.setHeight(size);
30977     }
30978 };
30979 Roo.extend(Roo.SouthLayoutRegion, Roo.SplitLayoutRegion, {
30980     orientation: Roo.SplitBar.VERTICAL,
30981     getBox : function(){
30982         if(this.collapsed){
30983             return this.collapsedEl.getBox();
30984         }
30985         var box = this.el.getBox();
30986         if(this.split){
30987             var sh = this.split.el.getHeight();
30988             box.height += sh;
30989             box.y -= sh;
30990         }
30991         return box;
30992     },
30993     
30994     updateBox : function(box){
30995         if(this.split && !this.collapsed){
30996             var sh = this.split.el.getHeight();
30997             box.height -= sh;
30998             box.y += sh;
30999             this.split.el.setLeft(box.x);
31000             this.split.el.setTop(box.y-sh);
31001             this.split.el.setWidth(box.width);
31002         }
31003         if(this.collapsed){
31004             this.updateBody(box.width, null);
31005         }
31006         Roo.LayoutRegion.prototype.updateBox.call(this, box);
31007     }
31008 });
31009
31010 Roo.EastLayoutRegion = function(mgr, config){
31011     Roo.SplitLayoutRegion.call(this, mgr, config, "east", "e-resize");
31012     if(this.split){
31013         this.split.placement = Roo.SplitBar.RIGHT;
31014         this.split.orientation = Roo.SplitBar.HORIZONTAL;
31015         this.split.el.addClass("x-layout-split-h");
31016     }
31017     var size = config.initialSize || config.width;
31018     if(typeof size != "undefined"){
31019         this.el.setWidth(size);
31020     }
31021 };
31022 Roo.extend(Roo.EastLayoutRegion, Roo.SplitLayoutRegion, {
31023     orientation: Roo.SplitBar.HORIZONTAL,
31024     getBox : function(){
31025         if(this.collapsed){
31026             return this.collapsedEl.getBox();
31027         }
31028         var box = this.el.getBox();
31029         if(this.split){
31030             var sw = this.split.el.getWidth();
31031             box.width += sw;
31032             box.x -= sw;
31033         }
31034         return box;
31035     },
31036
31037     updateBox : function(box){
31038         if(this.split && !this.collapsed){
31039             var sw = this.split.el.getWidth();
31040             box.width -= sw;
31041             this.split.el.setLeft(box.x);
31042             this.split.el.setTop(box.y);
31043             this.split.el.setHeight(box.height);
31044             box.x += sw;
31045         }
31046         if(this.collapsed){
31047             this.updateBody(null, box.height);
31048         }
31049         Roo.LayoutRegion.prototype.updateBox.call(this, box);
31050     }
31051 });
31052
31053 Roo.WestLayoutRegion = function(mgr, config){
31054     Roo.SplitLayoutRegion.call(this, mgr, config, "west", "w-resize");
31055     if(this.split){
31056         this.split.placement = Roo.SplitBar.LEFT;
31057         this.split.orientation = Roo.SplitBar.HORIZONTAL;
31058         this.split.el.addClass("x-layout-split-h");
31059     }
31060     var size = config.initialSize || config.width;
31061     if(typeof size != "undefined"){
31062         this.el.setWidth(size);
31063     }
31064 };
31065 Roo.extend(Roo.WestLayoutRegion, Roo.SplitLayoutRegion, {
31066     orientation: Roo.SplitBar.HORIZONTAL,
31067     getBox : function(){
31068         if(this.collapsed){
31069             return this.collapsedEl.getBox();
31070         }
31071         var box = this.el.getBox();
31072         if(this.split){
31073             box.width += this.split.el.getWidth();
31074         }
31075         return box;
31076     },
31077     
31078     updateBox : function(box){
31079         if(this.split && !this.collapsed){
31080             var sw = this.split.el.getWidth();
31081             box.width -= sw;
31082             this.split.el.setLeft(box.x+box.width);
31083             this.split.el.setTop(box.y);
31084             this.split.el.setHeight(box.height);
31085         }
31086         if(this.collapsed){
31087             this.updateBody(null, box.height);
31088         }
31089         Roo.LayoutRegion.prototype.updateBox.call(this, box);
31090     }
31091 });
31092 /*
31093  * Based on:
31094  * Ext JS Library 1.1.1
31095  * Copyright(c) 2006-2007, Ext JS, LLC.
31096  *
31097  * Originally Released Under LGPL - original licence link has changed is not relivant.
31098  *
31099  * Fork - LGPL
31100  * <script type="text/javascript">
31101  */
31102  
31103  
31104 /*
31105  * Private internal class for reading and applying state
31106  */
31107 Roo.LayoutStateManager = function(layout){
31108      // default empty state
31109      this.state = {
31110         north: {},
31111         south: {},
31112         east: {},
31113         west: {}       
31114     };
31115 };
31116
31117 Roo.LayoutStateManager.prototype = {
31118     init : function(layout, provider){
31119         this.provider = provider;
31120         var state = provider.get(layout.id+"-layout-state");
31121         if(state){
31122             var wasUpdating = layout.isUpdating();
31123             if(!wasUpdating){
31124                 layout.beginUpdate();
31125             }
31126             for(var key in state){
31127                 if(typeof state[key] != "function"){
31128                     var rstate = state[key];
31129                     var r = layout.getRegion(key);
31130                     if(r && rstate){
31131                         if(rstate.size){
31132                             r.resizeTo(rstate.size);
31133                         }
31134                         if(rstate.collapsed == true){
31135                             r.collapse(true);
31136                         }else{
31137                             r.expand(null, true);
31138                         }
31139                     }
31140                 }
31141             }
31142             if(!wasUpdating){
31143                 layout.endUpdate();
31144             }
31145             this.state = state; 
31146         }
31147         this.layout = layout;
31148         layout.on("regionresized", this.onRegionResized, this);
31149         layout.on("regioncollapsed", this.onRegionCollapsed, this);
31150         layout.on("regionexpanded", this.onRegionExpanded, this);
31151     },
31152     
31153     storeState : function(){
31154         this.provider.set(this.layout.id+"-layout-state", this.state);
31155     },
31156     
31157     onRegionResized : function(region, newSize){
31158         this.state[region.getPosition()].size = newSize;
31159         this.storeState();
31160     },
31161     
31162     onRegionCollapsed : function(region){
31163         this.state[region.getPosition()].collapsed = true;
31164         this.storeState();
31165     },
31166     
31167     onRegionExpanded : function(region){
31168         this.state[region.getPosition()].collapsed = false;
31169         this.storeState();
31170     }
31171 };/*
31172  * Based on:
31173  * Ext JS Library 1.1.1
31174  * Copyright(c) 2006-2007, Ext JS, LLC.
31175  *
31176  * Originally Released Under LGPL - original licence link has changed is not relivant.
31177  *
31178  * Fork - LGPL
31179  * <script type="text/javascript">
31180  */
31181 /**
31182  * @class Roo.ContentPanel
31183  * @extends Roo.util.Observable
31184  * A basic ContentPanel element.
31185  * @cfg {Boolean}   fitToFrame    True for this panel to adjust its size to fit when the region resizes  (defaults to false)
31186  * @cfg {Boolean}   fitContainer   When using {@link #fitToFrame} and {@link #resizeEl}, you can also fit the parent container  (defaults to false)
31187  * @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
31188  * @cfg {Boolean}   closable      True if the panel can be closed/removed
31189  * @cfg {Boolean}   background    True if the panel should not be activated when it is added (defaults to false)
31190  * @cfg {String/HTMLElement/Element} resizeEl An element to resize if {@link #fitToFrame} is true (instead of this panel's element)
31191  * @cfg {Toolbar}   toolbar       A toolbar for this panel
31192  * @cfg {Boolean} autoScroll    True to scroll overflow in this panel (use with {@link #fitToFrame})
31193  * @cfg {String} title          The title for this panel
31194  * @cfg {Array} adjustments     Values to <b>add</b> to the width/height when doing a {@link #fitToFrame} (default is [0, 0])
31195  * @cfg {String} url            Calls {@link #setUrl} with this value
31196  * @cfg {String} region         (center|north|south|east|west) which region to put this panel on (when used with xtype constructors)
31197  * @cfg {String/Object} params  When used with {@link #url}, calls {@link #setUrl} with this value
31198  * @cfg {Boolean} loadOnce      When used with {@link #url}, calls {@link #setUrl} with this value
31199  * @cfg {String} content        Raw content to fill content panel with (uses setContent on construction.)
31200
31201  * @constructor
31202  * Create a new ContentPanel.
31203  * @param {String/HTMLElement/Roo.Element} el The container element for this panel
31204  * @param {String/Object} config A string to set only the title or a config object
31205  * @param {String} content (optional) Set the HTML content for this panel
31206  * @param {String} region (optional) Used by xtype constructors to add to regions. (values center,east,west,south,north)
31207  */
31208 Roo.ContentPanel = function(el, config, content){
31209     
31210      
31211     /*
31212     if(el.autoCreate || el.xtype){ // xtype is available if this is called from factory
31213         config = el;
31214         el = Roo.id();
31215     }
31216     if (config && config.parentLayout) { 
31217         el = config.parentLayout.el.createChild(); 
31218     }
31219     */
31220     if(el.autoCreate){ // xtype is available if this is called from factory
31221         config = el;
31222         el = Roo.id();
31223     }
31224     this.el = Roo.get(el);
31225     if(!this.el && config && config.autoCreate){
31226         if(typeof config.autoCreate == "object"){
31227             if(!config.autoCreate.id){
31228                 config.autoCreate.id = config.id||el;
31229             }
31230             this.el = Roo.DomHelper.append(document.body,
31231                         config.autoCreate, true);
31232         }else{
31233             this.el = Roo.DomHelper.append(document.body,
31234                         {tag: "div", cls: "x-layout-inactive-content", id: config.id||el}, true);
31235         }
31236     }
31237     this.closable = false;
31238     this.loaded = false;
31239     this.active = false;
31240     if(typeof config == "string"){
31241         this.title = config;
31242     }else{
31243         Roo.apply(this, config);
31244     }
31245     
31246     if (this.toolbar && !this.toolbar.el && this.toolbar.xtype) {
31247         this.wrapEl = this.el.wrap();    
31248         this.toolbar = new Roo.Toolbar(this.el.insertSibling(false, 'before'), [] , this.toolbar);
31249         
31250     }
31251     
31252     
31253     
31254     if(this.resizeEl){
31255         this.resizeEl = Roo.get(this.resizeEl, true);
31256     }else{
31257         this.resizeEl = this.el;
31258     }
31259     this.addEvents({
31260         /**
31261          * @event activate
31262          * Fires when this panel is activated. 
31263          * @param {Roo.ContentPanel} this
31264          */
31265         "activate" : true,
31266         /**
31267          * @event deactivate
31268          * Fires when this panel is activated. 
31269          * @param {Roo.ContentPanel} this
31270          */
31271         "deactivate" : true,
31272
31273         /**
31274          * @event resize
31275          * Fires when this panel is resized if fitToFrame is true.
31276          * @param {Roo.ContentPanel} this
31277          * @param {Number} width The width after any component adjustments
31278          * @param {Number} height The height after any component adjustments
31279          */
31280         "resize" : true
31281     });
31282     if(this.autoScroll){
31283         this.resizeEl.setStyle("overflow", "auto");
31284     } else {
31285         // fix randome scrolling
31286         this.el.on('scroll', function() {
31287             Roo.log('fix random scolling');
31288             this.scrollTo('top',0); 
31289         });
31290     }
31291     content = content || this.content;
31292     if(content){
31293         this.setContent(content);
31294     }
31295     if(config && config.url){
31296         this.setUrl(this.url, this.params, this.loadOnce);
31297     }
31298     
31299     
31300     
31301     Roo.ContentPanel.superclass.constructor.call(this);
31302 };
31303
31304 Roo.extend(Roo.ContentPanel, Roo.util.Observable, {
31305     tabTip:'',
31306     setRegion : function(region){
31307         this.region = region;
31308         if(region){
31309            this.el.replaceClass("x-layout-inactive-content", "x-layout-active-content");
31310         }else{
31311            this.el.replaceClass("x-layout-active-content", "x-layout-inactive-content");
31312         } 
31313     },
31314     
31315     /**
31316      * Returns the toolbar for this Panel if one was configured. 
31317      * @return {Roo.Toolbar} 
31318      */
31319     getToolbar : function(){
31320         return this.toolbar;
31321     },
31322     
31323     setActiveState : function(active){
31324         this.active = active;
31325         if(!active){
31326             this.fireEvent("deactivate", this);
31327         }else{
31328             this.fireEvent("activate", this);
31329         }
31330     },
31331     /**
31332      * Updates this panel's element
31333      * @param {String} content The new content
31334      * @param {Boolean} loadScripts (optional) true to look for and process scripts
31335     */
31336     setContent : function(content, loadScripts){
31337         this.el.update(content, loadScripts);
31338     },
31339
31340     ignoreResize : function(w, h){
31341         if(this.lastSize && this.lastSize.width == w && this.lastSize.height == h){
31342             return true;
31343         }else{
31344             this.lastSize = {width: w, height: h};
31345             return false;
31346         }
31347     },
31348     /**
31349      * Get the {@link Roo.UpdateManager} for this panel. Enables you to perform Ajax updates.
31350      * @return {Roo.UpdateManager} The UpdateManager
31351      */
31352     getUpdateManager : function(){
31353         return this.el.getUpdateManager();
31354     },
31355      /**
31356      * Loads this content panel immediately with content from XHR. Note: to delay loading until the panel is activated, use {@link #setUrl}.
31357      * @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:
31358 <pre><code>
31359 panel.load({
31360     url: "your-url.php",
31361     params: {param1: "foo", param2: "bar"}, // or a URL encoded string
31362     callback: yourFunction,
31363     scope: yourObject, //(optional scope)
31364     discardUrl: false,
31365     nocache: false,
31366     text: "Loading...",
31367     timeout: 30,
31368     scripts: false
31369 });
31370 </code></pre>
31371      * The only required property is <i>url</i>. The optional properties <i>nocache</i>, <i>text</i> and <i>scripts</i>
31372      * 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.
31373      * @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}
31374      * @param {Function} callback (optional) Callback when transaction is complete -- called with signature (oElement, bSuccess, oResponse)
31375      * @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.
31376      * @return {Roo.ContentPanel} this
31377      */
31378     load : function(){
31379         var um = this.el.getUpdateManager();
31380         um.update.apply(um, arguments);
31381         return this;
31382     },
31383
31384
31385     /**
31386      * 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.
31387      * @param {String/Function} url The URL to load the content from or a function to call to get the URL
31388      * @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)
31389      * @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)
31390      * @return {Roo.UpdateManager} The UpdateManager
31391      */
31392     setUrl : function(url, params, loadOnce){
31393         if(this.refreshDelegate){
31394             this.removeListener("activate", this.refreshDelegate);
31395         }
31396         this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
31397         this.on("activate", this.refreshDelegate);
31398         return this.el.getUpdateManager();
31399     },
31400     
31401     _handleRefresh : function(url, params, loadOnce){
31402         if(!loadOnce || !this.loaded){
31403             var updater = this.el.getUpdateManager();
31404             updater.update(url, params, this._setLoaded.createDelegate(this));
31405         }
31406     },
31407     
31408     _setLoaded : function(){
31409         this.loaded = true;
31410     }, 
31411     
31412     /**
31413      * Returns this panel's id
31414      * @return {String} 
31415      */
31416     getId : function(){
31417         return this.el.id;
31418     },
31419     
31420     /** 
31421      * Returns this panel's element - used by regiosn to add.
31422      * @return {Roo.Element} 
31423      */
31424     getEl : function(){
31425         return this.wrapEl || this.el;
31426     },
31427     
31428     adjustForComponents : function(width, height){
31429         if(this.resizeEl != this.el){
31430             width -= this.el.getFrameWidth('lr');
31431             height -= this.el.getFrameWidth('tb');
31432         }
31433         if(this.toolbar){
31434             var te = this.toolbar.getEl();
31435             height -= te.getHeight();
31436             te.setWidth(width);
31437         }
31438         if(this.adjustments){
31439             width += this.adjustments[0];
31440             height += this.adjustments[1];
31441         }
31442         return {"width": width, "height": height};
31443     },
31444     
31445     setSize : function(width, height){
31446         if(this.fitToFrame && !this.ignoreResize(width, height)){
31447             if(this.fitContainer && this.resizeEl != this.el){
31448                 this.el.setSize(width, height);
31449             }
31450             var size = this.adjustForComponents(width, height);
31451             this.resizeEl.setSize(this.autoWidth ? "auto" : size.width, this.autoHeight ? "auto" : size.height);
31452             this.fireEvent('resize', this, size.width, size.height);
31453         }
31454     },
31455     
31456     /**
31457      * Returns this panel's title
31458      * @return {String} 
31459      */
31460     getTitle : function(){
31461         return this.title;
31462     },
31463     
31464     /**
31465      * Set this panel's title
31466      * @param {String} title
31467      */
31468     setTitle : function(title){
31469         this.title = title;
31470         if(this.region){
31471             this.region.updatePanelTitle(this, title);
31472         }
31473     },
31474     
31475     /**
31476      * Returns true is this panel was configured to be closable
31477      * @return {Boolean} 
31478      */
31479     isClosable : function(){
31480         return this.closable;
31481     },
31482     
31483     beforeSlide : function(){
31484         this.el.clip();
31485         this.resizeEl.clip();
31486     },
31487     
31488     afterSlide : function(){
31489         this.el.unclip();
31490         this.resizeEl.unclip();
31491     },
31492     
31493     /**
31494      *   Force a content refresh from the URL specified in the {@link #setUrl} method.
31495      *   Will fail silently if the {@link #setUrl} method has not been called.
31496      *   This does not activate the panel, just updates its content.
31497      */
31498     refresh : function(){
31499         if(this.refreshDelegate){
31500            this.loaded = false;
31501            this.refreshDelegate();
31502         }
31503     },
31504     
31505     /**
31506      * Destroys this panel
31507      */
31508     destroy : function(){
31509         this.el.removeAllListeners();
31510         var tempEl = document.createElement("span");
31511         tempEl.appendChild(this.el.dom);
31512         tempEl.innerHTML = "";
31513         this.el.remove();
31514         this.el = null;
31515     },
31516     
31517     /**
31518      * form - if the content panel contains a form - this is a reference to it.
31519      * @type {Roo.form.Form}
31520      */
31521     form : false,
31522     /**
31523      * view - if the content panel contains a view (Roo.DatePicker / Roo.View / Roo.JsonView)
31524      *    This contains a reference to it.
31525      * @type {Roo.View}
31526      */
31527     view : false,
31528     
31529       /**
31530      * Adds a xtype elements to the panel - currently only supports Forms, View, JsonView.
31531      * <pre><code>
31532
31533 layout.addxtype({
31534        xtype : 'Form',
31535        items: [ .... ]
31536    }
31537 );
31538
31539 </code></pre>
31540      * @param {Object} cfg Xtype definition of item to add.
31541      */
31542     
31543     addxtype : function(cfg) {
31544         // add form..
31545         if (cfg.xtype.match(/^Form$/)) {
31546             var el = this.el.createChild();
31547
31548             this.form = new  Roo.form.Form(cfg);
31549             
31550             
31551             if ( this.form.allItems.length) this.form.render(el.dom);
31552             return this.form;
31553         }
31554         // should only have one of theses..
31555         if (['View', 'JsonView', 'DatePicker'].indexOf(cfg.xtype) > -1) {
31556             // views..
31557             cfg.el = this.el.appendChild(document.createElement("div"));
31558             // factory?
31559             var ret = new Roo[cfg.xtype](cfg);
31560             ret.render && ret.render(false, ''); // render blank..
31561             this.view = ret;
31562             return ret;
31563         }
31564         return false;
31565     }
31566 });
31567
31568 /**
31569  * @class Roo.GridPanel
31570  * @extends Roo.ContentPanel
31571  * @constructor
31572  * Create a new GridPanel.
31573  * @param {Roo.grid.Grid} grid The grid for this panel
31574  * @param {String/Object} config A string to set only the panel's title, or a config object
31575  */
31576 Roo.GridPanel = function(grid, config){
31577     
31578   
31579     this.wrapper = Roo.DomHelper.append(document.body, // wrapper for IE7 strict & safari scroll issue
31580         {tag: "div", cls: "x-layout-grid-wrapper x-layout-inactive-content"}, true);
31581         
31582     this.wrapper.dom.appendChild(grid.getGridEl().dom);
31583     
31584     Roo.GridPanel.superclass.constructor.call(this, this.wrapper, config);
31585     
31586     if(this.toolbar){
31587         this.toolbar.el.insertBefore(this.wrapper.dom.firstChild);
31588     }
31589     // xtype created footer. - not sure if will work as we normally have to render first..
31590     if (this.footer && !this.footer.el && this.footer.xtype) {
31591         
31592         this.footer.container = this.grid.getView().getFooterPanel(true);
31593         this.footer.dataSource = this.grid.dataSource;
31594         this.footer = Roo.factory(this.footer, Roo);
31595         
31596     }
31597     
31598     grid.monitorWindowResize = false; // turn off autosizing
31599     grid.autoHeight = false;
31600     grid.autoWidth = false;
31601     this.grid = grid;
31602     this.grid.getGridEl().replaceClass("x-layout-inactive-content", "x-layout-component-panel");
31603 };
31604
31605 Roo.extend(Roo.GridPanel, Roo.ContentPanel, {
31606     getId : function(){
31607         return this.grid.id;
31608     },
31609     
31610     /**
31611      * Returns the grid for this panel
31612      * @return {Roo.grid.Grid} 
31613      */
31614     getGrid : function(){
31615         return this.grid;    
31616     },
31617     
31618     setSize : function(width, height){
31619         if(!this.ignoreResize(width, height)){
31620             var grid = this.grid;
31621             var size = this.adjustForComponents(width, height);
31622             grid.getGridEl().setSize(size.width, size.height);
31623             grid.autoSize();
31624         }
31625     },
31626     
31627     beforeSlide : function(){
31628         this.grid.getView().scroller.clip();
31629     },
31630     
31631     afterSlide : function(){
31632         this.grid.getView().scroller.unclip();
31633     },
31634     
31635     destroy : function(){
31636         this.grid.destroy();
31637         delete this.grid;
31638         Roo.GridPanel.superclass.destroy.call(this); 
31639     }
31640 });
31641
31642
31643 /**
31644  * @class Roo.NestedLayoutPanel
31645  * @extends Roo.ContentPanel
31646  * @constructor
31647  * Create a new NestedLayoutPanel.
31648  * 
31649  * 
31650  * @param {Roo.BorderLayout} layout The layout for this panel
31651  * @param {String/Object} config A string to set only the title or a config object
31652  */
31653 Roo.NestedLayoutPanel = function(layout, config)
31654 {
31655     // construct with only one argument..
31656     /* FIXME - implement nicer consturctors
31657     if (layout.layout) {
31658         config = layout;
31659         layout = config.layout;
31660         delete config.layout;
31661     }
31662     if (layout.xtype && !layout.getEl) {
31663         // then layout needs constructing..
31664         layout = Roo.factory(layout, Roo);
31665     }
31666     */
31667     
31668     
31669     Roo.NestedLayoutPanel.superclass.constructor.call(this, layout.getEl(), config);
31670     
31671     layout.monitorWindowResize = false; // turn off autosizing
31672     this.layout = layout;
31673     this.layout.getEl().addClass("x-layout-nested-layout");
31674     
31675     
31676     
31677     
31678 };
31679
31680 Roo.extend(Roo.NestedLayoutPanel, Roo.ContentPanel, {
31681
31682     setSize : function(width, height){
31683         if(!this.ignoreResize(width, height)){
31684             var size = this.adjustForComponents(width, height);
31685             var el = this.layout.getEl();
31686             el.setSize(size.width, size.height);
31687             var touch = el.dom.offsetWidth;
31688             this.layout.layout();
31689             // ie requires a double layout on the first pass
31690             if(Roo.isIE && !this.initialized){
31691                 this.initialized = true;
31692                 this.layout.layout();
31693             }
31694         }
31695     },
31696     
31697     // activate all subpanels if not currently active..
31698     
31699     setActiveState : function(active){
31700         this.active = active;
31701         if(!active){
31702             this.fireEvent("deactivate", this);
31703             return;
31704         }
31705         
31706         this.fireEvent("activate", this);
31707         // not sure if this should happen before or after..
31708         if (!this.layout) {
31709             return; // should not happen..
31710         }
31711         var reg = false;
31712         for (var r in this.layout.regions) {
31713             reg = this.layout.getRegion(r);
31714             if (reg.getActivePanel()) {
31715                 //reg.showPanel(reg.getActivePanel()); // force it to activate.. 
31716                 reg.setActivePanel(reg.getActivePanel());
31717                 continue;
31718             }
31719             if (!reg.panels.length) {
31720                 continue;
31721             }
31722             reg.showPanel(reg.getPanel(0));
31723         }
31724         
31725         
31726         
31727         
31728     },
31729     
31730     /**
31731      * Returns the nested BorderLayout for this panel
31732      * @return {Roo.BorderLayout} 
31733      */
31734     getLayout : function(){
31735         return this.layout;
31736     },
31737     
31738      /**
31739      * Adds a xtype elements to the layout of the nested panel
31740      * <pre><code>
31741
31742 panel.addxtype({
31743        xtype : 'ContentPanel',
31744        region: 'west',
31745        items: [ .... ]
31746    }
31747 );
31748
31749 panel.addxtype({
31750         xtype : 'NestedLayoutPanel',
31751         region: 'west',
31752         layout: {
31753            center: { },
31754            west: { }   
31755         },
31756         items : [ ... list of content panels or nested layout panels.. ]
31757    }
31758 );
31759 </code></pre>
31760      * @param {Object} cfg Xtype definition of item to add.
31761      */
31762     addxtype : function(cfg) {
31763         return this.layout.addxtype(cfg);
31764     
31765     }
31766 });
31767
31768 Roo.ScrollPanel = function(el, config, content){
31769     config = config || {};
31770     config.fitToFrame = true;
31771     Roo.ScrollPanel.superclass.constructor.call(this, el, config, content);
31772     
31773     this.el.dom.style.overflow = "hidden";
31774     var wrap = this.el.wrap({cls: "x-scroller x-layout-inactive-content"});
31775     this.el.removeClass("x-layout-inactive-content");
31776     this.el.on("mousewheel", this.onWheel, this);
31777
31778     var up = wrap.createChild({cls: "x-scroller-up", html: "&#160;"}, this.el.dom);
31779     var down = wrap.createChild({cls: "x-scroller-down", html: "&#160;"});
31780     up.unselectable(); down.unselectable();
31781     up.on("click", this.scrollUp, this);
31782     down.on("click", this.scrollDown, this);
31783     up.addClassOnOver("x-scroller-btn-over");
31784     down.addClassOnOver("x-scroller-btn-over");
31785     up.addClassOnClick("x-scroller-btn-click");
31786     down.addClassOnClick("x-scroller-btn-click");
31787     this.adjustments = [0, -(up.getHeight() + down.getHeight())];
31788
31789     this.resizeEl = this.el;
31790     this.el = wrap; this.up = up; this.down = down;
31791 };
31792
31793 Roo.extend(Roo.ScrollPanel, Roo.ContentPanel, {
31794     increment : 100,
31795     wheelIncrement : 5,
31796     scrollUp : function(){
31797         this.resizeEl.scroll("up", this.increment, {callback: this.afterScroll, scope: this});
31798     },
31799
31800     scrollDown : function(){
31801         this.resizeEl.scroll("down", this.increment, {callback: this.afterScroll, scope: this});
31802     },
31803
31804     afterScroll : function(){
31805         var el = this.resizeEl;
31806         var t = el.dom.scrollTop, h = el.dom.scrollHeight, ch = el.dom.clientHeight;
31807         this.up[t == 0 ? "addClass" : "removeClass"]("x-scroller-btn-disabled");
31808         this.down[h - t <= ch ? "addClass" : "removeClass"]("x-scroller-btn-disabled");
31809     },
31810
31811     setSize : function(){
31812         Roo.ScrollPanel.superclass.setSize.apply(this, arguments);
31813         this.afterScroll();
31814     },
31815
31816     onWheel : function(e){
31817         var d = e.getWheelDelta();
31818         this.resizeEl.dom.scrollTop -= (d*this.wheelIncrement);
31819         this.afterScroll();
31820         e.stopEvent();
31821     },
31822
31823     setContent : function(content, loadScripts){
31824         this.resizeEl.update(content, loadScripts);
31825     }
31826
31827 });
31828
31829
31830
31831
31832
31833
31834
31835
31836
31837 /**
31838  * @class Roo.TreePanel
31839  * @extends Roo.ContentPanel
31840  * @constructor
31841  * Create a new TreePanel. - defaults to fit/scoll contents.
31842  * @param {String/Object} config A string to set only the panel's title, or a config object
31843  * @cfg {Roo.tree.TreePanel} tree The tree TreePanel, with config etc.
31844  */
31845 Roo.TreePanel = function(config){
31846     var el = config.el;
31847     var tree = config.tree;
31848     delete config.tree; 
31849     delete config.el; // hopefull!
31850     
31851     // wrapper for IE7 strict & safari scroll issue
31852     
31853     var treeEl = el.createChild();
31854     config.resizeEl = treeEl;
31855     
31856     
31857     
31858     Roo.TreePanel.superclass.constructor.call(this, el, config);
31859  
31860  
31861     this.tree = new Roo.tree.TreePanel(treeEl , tree);
31862     //console.log(tree);
31863     this.on('activate', function()
31864     {
31865         if (this.tree.rendered) {
31866             return;
31867         }
31868         //console.log('render tree');
31869         this.tree.render();
31870     });
31871     
31872     this.on('resize',  function (cp, w, h) {
31873             this.tree.innerCt.setWidth(w);
31874             this.tree.innerCt.setHeight(h);
31875             this.tree.innerCt.setStyle('overflow-y', 'auto');
31876     });
31877
31878         
31879     
31880 };
31881
31882 Roo.extend(Roo.TreePanel, Roo.ContentPanel, {   
31883     fitToFrame : true,
31884     autoScroll : true
31885 });
31886
31887
31888
31889
31890
31891
31892
31893
31894
31895
31896
31897 /*
31898  * Based on:
31899  * Ext JS Library 1.1.1
31900  * Copyright(c) 2006-2007, Ext JS, LLC.
31901  *
31902  * Originally Released Under LGPL - original licence link has changed is not relivant.
31903  *
31904  * Fork - LGPL
31905  * <script type="text/javascript">
31906  */
31907  
31908
31909 /**
31910  * @class Roo.ReaderLayout
31911  * @extends Roo.BorderLayout
31912  * This is a pre-built layout that represents a classic, 5-pane application.  It consists of a header, a primary
31913  * center region containing two nested regions (a top one for a list view and one for item preview below),
31914  * and regions on either side that can be used for navigation, application commands, informational displays, etc.
31915  * The setup and configuration work exactly the same as it does for a {@link Roo.BorderLayout} - this class simply
31916  * expedites the setup of the overall layout and regions for this common application style.
31917  * Example:
31918  <pre><code>
31919 var reader = new Roo.ReaderLayout();
31920 var CP = Roo.ContentPanel;  // shortcut for adding
31921
31922 reader.beginUpdate();
31923 reader.add("north", new CP("north", "North"));
31924 reader.add("west", new CP("west", {title: "West"}));
31925 reader.add("east", new CP("east", {title: "East"}));
31926
31927 reader.regions.listView.add(new CP("listView", "List"));
31928 reader.regions.preview.add(new CP("preview", "Preview"));
31929 reader.endUpdate();
31930 </code></pre>
31931 * @constructor
31932 * Create a new ReaderLayout
31933 * @param {Object} config Configuration options
31934 * @param {String/HTMLElement/Element} container (optional) The container this layout is bound to (defaults to
31935 * document.body if omitted)
31936 */
31937 Roo.ReaderLayout = function(config, renderTo){
31938     var c = config || {size:{}};
31939     Roo.ReaderLayout.superclass.constructor.call(this, renderTo || document.body, {
31940         north: c.north !== false ? Roo.apply({
31941             split:false,
31942             initialSize: 32,
31943             titlebar: false
31944         }, c.north) : false,
31945         west: c.west !== false ? Roo.apply({
31946             split:true,
31947             initialSize: 200,
31948             minSize: 175,
31949             maxSize: 400,
31950             titlebar: true,
31951             collapsible: true,
31952             animate: true,
31953             margins:{left:5,right:0,bottom:5,top:5},
31954             cmargins:{left:5,right:5,bottom:5,top:5}
31955         }, c.west) : false,
31956         east: c.east !== false ? Roo.apply({
31957             split:true,
31958             initialSize: 200,
31959             minSize: 175,
31960             maxSize: 400,
31961             titlebar: true,
31962             collapsible: true,
31963             animate: true,
31964             margins:{left:0,right:5,bottom:5,top:5},
31965             cmargins:{left:5,right:5,bottom:5,top:5}
31966         }, c.east) : false,
31967         center: Roo.apply({
31968             tabPosition: 'top',
31969             autoScroll:false,
31970             closeOnTab: true,
31971             titlebar:false,
31972             margins:{left:c.west!==false ? 0 : 5,right:c.east!==false ? 0 : 5,bottom:5,top:2}
31973         }, c.center)
31974     });
31975
31976     this.el.addClass('x-reader');
31977
31978     this.beginUpdate();
31979
31980     var inner = new Roo.BorderLayout(Roo.get(document.body).createChild(), {
31981         south: c.preview !== false ? Roo.apply({
31982             split:true,
31983             initialSize: 200,
31984             minSize: 100,
31985             autoScroll:true,
31986             collapsible:true,
31987             titlebar: true,
31988             cmargins:{top:5,left:0, right:0, bottom:0}
31989         }, c.preview) : false,
31990         center: Roo.apply({
31991             autoScroll:false,
31992             titlebar:false,
31993             minHeight:200
31994         }, c.listView)
31995     });
31996     this.add('center', new Roo.NestedLayoutPanel(inner,
31997             Roo.apply({title: c.mainTitle || '',tabTip:''},c.innerPanelCfg)));
31998
31999     this.endUpdate();
32000
32001     this.regions.preview = inner.getRegion('south');
32002     this.regions.listView = inner.getRegion('center');
32003 };
32004
32005 Roo.extend(Roo.ReaderLayout, Roo.BorderLayout);/*
32006  * Based on:
32007  * Ext JS Library 1.1.1
32008  * Copyright(c) 2006-2007, Ext JS, LLC.
32009  *
32010  * Originally Released Under LGPL - original licence link has changed is not relivant.
32011  *
32012  * Fork - LGPL
32013  * <script type="text/javascript">
32014  */
32015  
32016 /**
32017  * @class Roo.grid.Grid
32018  * @extends Roo.util.Observable
32019  * This class represents the primary interface of a component based grid control.
32020  * <br><br>Usage:<pre><code>
32021  var grid = new Roo.grid.Grid("my-container-id", {
32022      ds: myDataStore,
32023      cm: myColModel,
32024      selModel: mySelectionModel,
32025      autoSizeColumns: true,
32026      monitorWindowResize: false,
32027      trackMouseOver: true
32028  });
32029  // set any options
32030  grid.render();
32031  * </code></pre>
32032  * <b>Common Problems:</b><br/>
32033  * - Grid does not resize properly when going smaller: Setting overflow hidden on the container
32034  * element will correct this<br/>
32035  * - If you get el.style[camel]= NaNpx or -2px or something related, be certain you have given your container element
32036  * dimensions. The grid adapts to your container's size, if your container has no size defined then the results
32037  * are unpredictable.<br/>
32038  * - Do not render the grid into an element with display:none. Try using visibility:hidden. Otherwise there is no way for the
32039  * grid to calculate dimensions/offsets.<br/>
32040   * @constructor
32041  * @param {String/HTMLElement/Roo.Element} container The element into which this grid will be rendered -
32042  * The container MUST have some type of size defined for the grid to fill. The container will be
32043  * automatically set to position relative if it isn't already.
32044  * @param {Object} config A config object that sets properties on this grid.
32045  */
32046 Roo.grid.Grid = function(container, config){
32047         // initialize the container
32048         this.container = Roo.get(container);
32049         this.container.update("");
32050         this.container.setStyle("overflow", "hidden");
32051     this.container.addClass('x-grid-container');
32052
32053     this.id = this.container.id;
32054
32055     Roo.apply(this, config);
32056     // check and correct shorthanded configs
32057     if(this.ds){
32058         this.dataSource = this.ds;
32059         delete this.ds;
32060     }
32061     if(this.cm){
32062         this.colModel = this.cm;
32063         delete this.cm;
32064     }
32065     if(this.sm){
32066         this.selModel = this.sm;
32067         delete this.sm;
32068     }
32069
32070     if (this.selModel) {
32071         this.selModel = Roo.factory(this.selModel, Roo.grid);
32072         this.sm = this.selModel;
32073         this.sm.xmodule = this.xmodule || false;
32074     }
32075     if (typeof(this.colModel.config) == 'undefined') {
32076         this.colModel = new Roo.grid.ColumnModel(this.colModel);
32077         this.cm = this.colModel;
32078         this.cm.xmodule = this.xmodule || false;
32079     }
32080     if (this.dataSource) {
32081         this.dataSource= Roo.factory(this.dataSource, Roo.data);
32082         this.ds = this.dataSource;
32083         this.ds.xmodule = this.xmodule || false;
32084          
32085     }
32086     
32087     
32088     
32089     if(this.width){
32090         this.container.setWidth(this.width);
32091     }
32092
32093     if(this.height){
32094         this.container.setHeight(this.height);
32095     }
32096     /** @private */
32097         this.addEvents({
32098         // raw events
32099         /**
32100          * @event click
32101          * The raw click event for the entire grid.
32102          * @param {Roo.EventObject} e
32103          */
32104         "click" : true,
32105         /**
32106          * @event dblclick
32107          * The raw dblclick event for the entire grid.
32108          * @param {Roo.EventObject} e
32109          */
32110         "dblclick" : true,
32111         /**
32112          * @event contextmenu
32113          * The raw contextmenu event for the entire grid.
32114          * @param {Roo.EventObject} e
32115          */
32116         "contextmenu" : true,
32117         /**
32118          * @event mousedown
32119          * The raw mousedown event for the entire grid.
32120          * @param {Roo.EventObject} e
32121          */
32122         "mousedown" : true,
32123         /**
32124          * @event mouseup
32125          * The raw mouseup event for the entire grid.
32126          * @param {Roo.EventObject} e
32127          */
32128         "mouseup" : true,
32129         /**
32130          * @event mouseover
32131          * The raw mouseover event for the entire grid.
32132          * @param {Roo.EventObject} e
32133          */
32134         "mouseover" : true,
32135         /**
32136          * @event mouseout
32137          * The raw mouseout event for the entire grid.
32138          * @param {Roo.EventObject} e
32139          */
32140         "mouseout" : true,
32141         /**
32142          * @event keypress
32143          * The raw keypress event for the entire grid.
32144          * @param {Roo.EventObject} e
32145          */
32146         "keypress" : true,
32147         /**
32148          * @event keydown
32149          * The raw keydown event for the entire grid.
32150          * @param {Roo.EventObject} e
32151          */
32152         "keydown" : true,
32153
32154         // custom events
32155
32156         /**
32157          * @event cellclick
32158          * Fires when a cell is clicked
32159          * @param {Grid} this
32160          * @param {Number} rowIndex
32161          * @param {Number} columnIndex
32162          * @param {Roo.EventObject} e
32163          */
32164         "cellclick" : true,
32165         /**
32166          * @event celldblclick
32167          * Fires when a cell is double clicked
32168          * @param {Grid} this
32169          * @param {Number} rowIndex
32170          * @param {Number} columnIndex
32171          * @param {Roo.EventObject} e
32172          */
32173         "celldblclick" : true,
32174         /**
32175          * @event rowclick
32176          * Fires when a row is clicked
32177          * @param {Grid} this
32178          * @param {Number} rowIndex
32179          * @param {Roo.EventObject} e
32180          */
32181         "rowclick" : true,
32182         /**
32183          * @event rowdblclick
32184          * Fires when a row is double clicked
32185          * @param {Grid} this
32186          * @param {Number} rowIndex
32187          * @param {Roo.EventObject} e
32188          */
32189         "rowdblclick" : true,
32190         /**
32191          * @event headerclick
32192          * Fires when a header is clicked
32193          * @param {Grid} this
32194          * @param {Number} columnIndex
32195          * @param {Roo.EventObject} e
32196          */
32197         "headerclick" : true,
32198         /**
32199          * @event headerdblclick
32200          * Fires when a header cell is double clicked
32201          * @param {Grid} this
32202          * @param {Number} columnIndex
32203          * @param {Roo.EventObject} e
32204          */
32205         "headerdblclick" : true,
32206         /**
32207          * @event rowcontextmenu
32208          * Fires when a row is right clicked
32209          * @param {Grid} this
32210          * @param {Number} rowIndex
32211          * @param {Roo.EventObject} e
32212          */
32213         "rowcontextmenu" : true,
32214         /**
32215          * @event cellcontextmenu
32216          * Fires when a cell is right clicked
32217          * @param {Grid} this
32218          * @param {Number} rowIndex
32219          * @param {Number} cellIndex
32220          * @param {Roo.EventObject} e
32221          */
32222          "cellcontextmenu" : true,
32223         /**
32224          * @event headercontextmenu
32225          * Fires when a header is right clicked
32226          * @param {Grid} this
32227          * @param {Number} columnIndex
32228          * @param {Roo.EventObject} e
32229          */
32230         "headercontextmenu" : true,
32231         /**
32232          * @event bodyscroll
32233          * Fires when the body element is scrolled
32234          * @param {Number} scrollLeft
32235          * @param {Number} scrollTop
32236          */
32237         "bodyscroll" : true,
32238         /**
32239          * @event columnresize
32240          * Fires when the user resizes a column
32241          * @param {Number} columnIndex
32242          * @param {Number} newSize
32243          */
32244         "columnresize" : true,
32245         /**
32246          * @event columnmove
32247          * Fires when the user moves a column
32248          * @param {Number} oldIndex
32249          * @param {Number} newIndex
32250          */
32251         "columnmove" : true,
32252         /**
32253          * @event startdrag
32254          * Fires when row(s) start being dragged
32255          * @param {Grid} this
32256          * @param {Roo.GridDD} dd The drag drop object
32257          * @param {event} e The raw browser event
32258          */
32259         "startdrag" : true,
32260         /**
32261          * @event enddrag
32262          * Fires when a drag operation is complete
32263          * @param {Grid} this
32264          * @param {Roo.GridDD} dd The drag drop object
32265          * @param {event} e The raw browser event
32266          */
32267         "enddrag" : true,
32268         /**
32269          * @event dragdrop
32270          * Fires when dragged row(s) are dropped on a valid DD target
32271          * @param {Grid} this
32272          * @param {Roo.GridDD} dd The drag drop object
32273          * @param {String} targetId The target drag drop object
32274          * @param {event} e The raw browser event
32275          */
32276         "dragdrop" : true,
32277         /**
32278          * @event dragover
32279          * Fires while row(s) are being dragged. "targetId" is the id of the Yahoo.util.DD object the selected rows are being dragged over.
32280          * @param {Grid} this
32281          * @param {Roo.GridDD} dd The drag drop object
32282          * @param {String} targetId The target drag drop object
32283          * @param {event} e The raw browser event
32284          */
32285         "dragover" : true,
32286         /**
32287          * @event dragenter
32288          *  Fires when the dragged row(s) first cross another DD target while being dragged
32289          * @param {Grid} this
32290          * @param {Roo.GridDD} dd The drag drop object
32291          * @param {String} targetId The target drag drop object
32292          * @param {event} e The raw browser event
32293          */
32294         "dragenter" : true,
32295         /**
32296          * @event dragout
32297          * Fires when the dragged row(s) leave another DD target while being dragged
32298          * @param {Grid} this
32299          * @param {Roo.GridDD} dd The drag drop object
32300          * @param {String} targetId The target drag drop object
32301          * @param {event} e The raw browser event
32302          */
32303         "dragout" : true,
32304         /**
32305          * @event rowclass
32306          * Fires when a row is rendered, so you can change add a style to it.
32307          * @param {GridView} gridview   The grid view
32308          * @param {Object} rowcfg   contains record  rowIndex and rowClass - set rowClass to add a style.
32309          */
32310         'rowclass' : true,
32311
32312         /**
32313          * @event render
32314          * Fires when the grid is rendered
32315          * @param {Grid} grid
32316          */
32317         'render' : true
32318     });
32319
32320     Roo.grid.Grid.superclass.constructor.call(this);
32321 };
32322 Roo.extend(Roo.grid.Grid, Roo.util.Observable, {
32323     
32324     /**
32325      * @cfg {String} ddGroup - drag drop group.
32326      */
32327
32328     /**
32329      * @cfg {Number} minColumnWidth The minimum width a column can be resized to. Default is 25.
32330      */
32331     minColumnWidth : 25,
32332
32333     /**
32334      * @cfg {Boolean} autoSizeColumns True to automatically resize the columns to fit their content
32335      * <b>on initial render.</b> It is more efficient to explicitly size the columns
32336      * through the ColumnModel's {@link Roo.grid.ColumnModel#width} config option.  Default is false.
32337      */
32338     autoSizeColumns : false,
32339
32340     /**
32341      * @cfg {Boolean} autoSizeHeaders True to measure headers with column data when auto sizing columns. Default is true.
32342      */
32343     autoSizeHeaders : true,
32344
32345     /**
32346      * @cfg {Boolean} monitorWindowResize True to autoSize the grid when the window resizes. Default is true.
32347      */
32348     monitorWindowResize : true,
32349
32350     /**
32351      * @cfg {Boolean} maxRowsToMeasure If autoSizeColumns is on, maxRowsToMeasure can be used to limit the number of
32352      * rows measured to get a columns size. Default is 0 (all rows).
32353      */
32354     maxRowsToMeasure : 0,
32355
32356     /**
32357      * @cfg {Boolean} trackMouseOver True to highlight rows when the mouse is over. Default is true.
32358      */
32359     trackMouseOver : true,
32360
32361     /**
32362     * @cfg {Boolean} enableDrag  True to enable drag of rows. Default is false. (double check if this is needed?)
32363     */
32364     
32365     /**
32366     * @cfg {Boolean} enableDragDrop True to enable drag and drop of rows. Default is false.
32367     */
32368     enableDragDrop : false,
32369     
32370     /**
32371     * @cfg {Boolean} enableColumnMove True to enable drag and drop reorder of columns. Default is true.
32372     */
32373     enableColumnMove : true,
32374     
32375     /**
32376     * @cfg {Boolean} enableColumnHide True to enable hiding of columns with the header context menu. Default is true.
32377     */
32378     enableColumnHide : true,
32379     
32380     /**
32381     * @cfg {Boolean} enableRowHeightSync True to manually sync row heights across locked and not locked rows. Default is false.
32382     */
32383     enableRowHeightSync : false,
32384     
32385     /**
32386     * @cfg {Boolean} stripeRows True to stripe the rows.  Default is true.
32387     */
32388     stripeRows : true,
32389     
32390     /**
32391     * @cfg {Boolean} autoHeight True to fit the height of the grid container to the height of the data. Default is false.
32392     */
32393     autoHeight : false,
32394
32395     /**
32396      * @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.
32397      */
32398     autoExpandColumn : false,
32399
32400     /**
32401     * @cfg {Number} autoExpandMin The minimum width the autoExpandColumn can have (if enabled).
32402     * Default is 50.
32403     */
32404     autoExpandMin : 50,
32405
32406     /**
32407     * @cfg {Number} autoExpandMax The maximum width the autoExpandColumn can have (if enabled). Default is 1000.
32408     */
32409     autoExpandMax : 1000,
32410
32411     /**
32412     * @cfg {Object} view The {@link Roo.grid.GridView} used by the grid. This can be set before a call to render().
32413     */
32414     view : null,
32415
32416     /**
32417     * @cfg {Object} loadMask An {@link Roo.LoadMask} config or true to mask the grid while loading. Default is false.
32418     */
32419     loadMask : false,
32420     /**
32421     * @cfg {Roo.dd.DropTarget} dragTarget An {@link Roo.dd.DragTarget} config
32422     */
32423     dropTarget: false,
32424     
32425    
32426     
32427     // private
32428     rendered : false,
32429
32430     /**
32431     * @cfg {Boolean} autoWidth True to set the grid's width to the default total width of the grid's columns instead
32432     * of a fixed width. Default is false.
32433     */
32434     /**
32435     * @cfg {Number} maxHeight Sets the maximum height of the grid - ignored if autoHeight is not on.
32436     */
32437     /**
32438      * Called once after all setup has been completed and the grid is ready to be rendered.
32439      * @return {Roo.grid.Grid} this
32440      */
32441     render : function()
32442     {
32443         var c = this.container;
32444         // try to detect autoHeight/width mode
32445         if((!c.dom.offsetHeight || c.dom.offsetHeight < 20) || c.getStyle("height") == "auto"){
32446             this.autoHeight = true;
32447         }
32448         var view = this.getView();
32449         view.init(this);
32450
32451         c.on("click", this.onClick, this);
32452         c.on("dblclick", this.onDblClick, this);
32453         c.on("contextmenu", this.onContextMenu, this);
32454         c.on("keydown", this.onKeyDown, this);
32455
32456         this.relayEvents(c, ["mousedown","mouseup","mouseover","mouseout","keypress"]);
32457
32458         this.getSelectionModel().init(this);
32459
32460         view.render();
32461
32462         if(this.loadMask){
32463             this.loadMask = new Roo.LoadMask(this.container,
32464                     Roo.apply({store:this.dataSource}, this.loadMask));
32465         }
32466         
32467         
32468         if (this.toolbar && this.toolbar.xtype) {
32469             this.toolbar.container = this.getView().getHeaderPanel(true);
32470             this.toolbar = new Roo.Toolbar(this.toolbar);
32471         }
32472         if (this.footer && this.footer.xtype) {
32473             this.footer.dataSource = this.getDataSource();
32474             this.footer.container = this.getView().getFooterPanel(true);
32475             this.footer = Roo.factory(this.footer, Roo);
32476         }
32477         if (this.dropTarget && this.dropTarget.xtype) {
32478             delete this.dropTarget.xtype;
32479             this.dropTarget =  new Ext.dd.DropTarget(this.getView().mainBody, this.dropTarget);
32480         }
32481         
32482         
32483         this.rendered = true;
32484         this.fireEvent('render', this);
32485         return this;
32486     },
32487
32488         /**
32489          * Reconfigures the grid to use a different Store and Column Model.
32490          * The View will be bound to the new objects and refreshed.
32491          * @param {Roo.data.Store} dataSource The new {@link Roo.data.Store} object
32492          * @param {Roo.grid.ColumnModel} The new {@link Roo.grid.ColumnModel} object
32493          */
32494     reconfigure : function(dataSource, colModel){
32495         if(this.loadMask){
32496             this.loadMask.destroy();
32497             this.loadMask = new Roo.LoadMask(this.container,
32498                     Roo.apply({store:dataSource}, this.loadMask));
32499         }
32500         this.view.bind(dataSource, colModel);
32501         this.dataSource = dataSource;
32502         this.colModel = colModel;
32503         this.view.refresh(true);
32504     },
32505
32506     // private
32507     onKeyDown : function(e){
32508         this.fireEvent("keydown", e);
32509     },
32510
32511     /**
32512      * Destroy this grid.
32513      * @param {Boolean} removeEl True to remove the element
32514      */
32515     destroy : function(removeEl, keepListeners){
32516         if(this.loadMask){
32517             this.loadMask.destroy();
32518         }
32519         var c = this.container;
32520         c.removeAllListeners();
32521         this.view.destroy();
32522         this.colModel.purgeListeners();
32523         if(!keepListeners){
32524             this.purgeListeners();
32525         }
32526         c.update("");
32527         if(removeEl === true){
32528             c.remove();
32529         }
32530     },
32531
32532     // private
32533     processEvent : function(name, e){
32534         this.fireEvent(name, e);
32535         var t = e.getTarget();
32536         var v = this.view;
32537         var header = v.findHeaderIndex(t);
32538         if(header !== false){
32539             this.fireEvent("header" + name, this, header, e);
32540         }else{
32541             var row = v.findRowIndex(t);
32542             var cell = v.findCellIndex(t);
32543             if(row !== false){
32544                 this.fireEvent("row" + name, this, row, e);
32545                 if(cell !== false){
32546                     this.fireEvent("cell" + name, this, row, cell, e);
32547                 }
32548             }
32549         }
32550     },
32551
32552     // private
32553     onClick : function(e){
32554         this.processEvent("click", e);
32555     },
32556
32557     // private
32558     onContextMenu : function(e, t){
32559         this.processEvent("contextmenu", e);
32560     },
32561
32562     // private
32563     onDblClick : function(e){
32564         this.processEvent("dblclick", e);
32565     },
32566
32567     // private
32568     walkCells : function(row, col, step, fn, scope){
32569         var cm = this.colModel, clen = cm.getColumnCount();
32570         var ds = this.dataSource, rlen = ds.getCount(), first = true;
32571         if(step < 0){
32572             if(col < 0){
32573                 row--;
32574                 first = false;
32575             }
32576             while(row >= 0){
32577                 if(!first){
32578                     col = clen-1;
32579                 }
32580                 first = false;
32581                 while(col >= 0){
32582                     if(fn.call(scope || this, row, col, cm) === true){
32583                         return [row, col];
32584                     }
32585                     col--;
32586                 }
32587                 row--;
32588             }
32589         } else {
32590             if(col >= clen){
32591                 row++;
32592                 first = false;
32593             }
32594             while(row < rlen){
32595                 if(!first){
32596                     col = 0;
32597                 }
32598                 first = false;
32599                 while(col < clen){
32600                     if(fn.call(scope || this, row, col, cm) === true){
32601                         return [row, col];
32602                     }
32603                     col++;
32604                 }
32605                 row++;
32606             }
32607         }
32608         return null;
32609     },
32610
32611     // private
32612     getSelections : function(){
32613         return this.selModel.getSelections();
32614     },
32615
32616     /**
32617      * Causes the grid to manually recalculate its dimensions. Generally this is done automatically,
32618      * but if manual update is required this method will initiate it.
32619      */
32620     autoSize : function(){
32621         if(this.rendered){
32622             this.view.layout();
32623             if(this.view.adjustForScroll){
32624                 this.view.adjustForScroll();
32625             }
32626         }
32627     },
32628
32629     /**
32630      * Returns the grid's underlying element.
32631      * @return {Element} The element
32632      */
32633     getGridEl : function(){
32634         return this.container;
32635     },
32636
32637     // private for compatibility, overridden by editor grid
32638     stopEditing : function(){},
32639
32640     /**
32641      * Returns the grid's SelectionModel.
32642      * @return {SelectionModel}
32643      */
32644     getSelectionModel : function(){
32645         if(!this.selModel){
32646             this.selModel = new Roo.grid.RowSelectionModel();
32647         }
32648         return this.selModel;
32649     },
32650
32651     /**
32652      * Returns the grid's DataSource.
32653      * @return {DataSource}
32654      */
32655     getDataSource : function(){
32656         return this.dataSource;
32657     },
32658
32659     /**
32660      * Returns the grid's ColumnModel.
32661      * @return {ColumnModel}
32662      */
32663     getColumnModel : function(){
32664         return this.colModel;
32665     },
32666
32667     /**
32668      * Returns the grid's GridView object.
32669      * @return {GridView}
32670      */
32671     getView : function(){
32672         if(!this.view){
32673             this.view = new Roo.grid.GridView(this.viewConfig);
32674         }
32675         return this.view;
32676     },
32677     /**
32678      * Called to get grid's drag proxy text, by default returns this.ddText.
32679      * @return {String}
32680      */
32681     getDragDropText : function(){
32682         var count = this.selModel.getCount();
32683         return String.format(this.ddText, count, count == 1 ? '' : 's');
32684     }
32685 });
32686 /**
32687  * Configures the text is the drag proxy (defaults to "%0 selected row(s)").
32688  * %0 is replaced with the number of selected rows.
32689  * @type String
32690  */
32691 Roo.grid.Grid.prototype.ddText = "{0} selected row{1}";/*
32692  * Based on:
32693  * Ext JS Library 1.1.1
32694  * Copyright(c) 2006-2007, Ext JS, LLC.
32695  *
32696  * Originally Released Under LGPL - original licence link has changed is not relivant.
32697  *
32698  * Fork - LGPL
32699  * <script type="text/javascript">
32700  */
32701  
32702 Roo.grid.AbstractGridView = function(){
32703         this.grid = null;
32704         
32705         this.events = {
32706             "beforerowremoved" : true,
32707             "beforerowsinserted" : true,
32708             "beforerefresh" : true,
32709             "rowremoved" : true,
32710             "rowsinserted" : true,
32711             "rowupdated" : true,
32712             "refresh" : true
32713         };
32714     Roo.grid.AbstractGridView.superclass.constructor.call(this);
32715 };
32716
32717 Roo.extend(Roo.grid.AbstractGridView, Roo.util.Observable, {
32718     rowClass : "x-grid-row",
32719     cellClass : "x-grid-cell",
32720     tdClass : "x-grid-td",
32721     hdClass : "x-grid-hd",
32722     splitClass : "x-grid-hd-split",
32723     
32724         init: function(grid){
32725         this.grid = grid;
32726                 var cid = this.grid.getGridEl().id;
32727         this.colSelector = "#" + cid + " ." + this.cellClass + "-";
32728         this.tdSelector = "#" + cid + " ." + this.tdClass + "-";
32729         this.hdSelector = "#" + cid + " ." + this.hdClass + "-";
32730         this.splitSelector = "#" + cid + " ." + this.splitClass + "-";
32731         },
32732         
32733         getColumnRenderers : function(){
32734         var renderers = [];
32735         var cm = this.grid.colModel;
32736         var colCount = cm.getColumnCount();
32737         for(var i = 0; i < colCount; i++){
32738             renderers[i] = cm.getRenderer(i);
32739         }
32740         return renderers;
32741     },
32742     
32743     getColumnIds : function(){
32744         var ids = [];
32745         var cm = this.grid.colModel;
32746         var colCount = cm.getColumnCount();
32747         for(var i = 0; i < colCount; i++){
32748             ids[i] = cm.getColumnId(i);
32749         }
32750         return ids;
32751     },
32752     
32753     getDataIndexes : function(){
32754         if(!this.indexMap){
32755             this.indexMap = this.buildIndexMap();
32756         }
32757         return this.indexMap.colToData;
32758     },
32759     
32760     getColumnIndexByDataIndex : function(dataIndex){
32761         if(!this.indexMap){
32762             this.indexMap = this.buildIndexMap();
32763         }
32764         return this.indexMap.dataToCol[dataIndex];
32765     },
32766     
32767     /**
32768      * Set a css style for a column dynamically. 
32769      * @param {Number} colIndex The index of the column
32770      * @param {String} name The css property name
32771      * @param {String} value The css value
32772      */
32773     setCSSStyle : function(colIndex, name, value){
32774         var selector = "#" + this.grid.id + " .x-grid-col-" + colIndex;
32775         Roo.util.CSS.updateRule(selector, name, value);
32776     },
32777     
32778     generateRules : function(cm){
32779         var ruleBuf = [], rulesId = this.grid.id + '-cssrules';
32780         Roo.util.CSS.removeStyleSheet(rulesId);
32781         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
32782             var cid = cm.getColumnId(i);
32783             ruleBuf.push(this.colSelector, cid, " {\n", cm.config[i].css, "}\n",
32784                          this.tdSelector, cid, " {\n}\n",
32785                          this.hdSelector, cid, " {\n}\n",
32786                          this.splitSelector, cid, " {\n}\n");
32787         }
32788         return Roo.util.CSS.createStyleSheet(ruleBuf.join(""), rulesId);
32789     }
32790 });/*
32791  * Based on:
32792  * Ext JS Library 1.1.1
32793  * Copyright(c) 2006-2007, Ext JS, LLC.
32794  *
32795  * Originally Released Under LGPL - original licence link has changed is not relivant.
32796  *
32797  * Fork - LGPL
32798  * <script type="text/javascript">
32799  */
32800
32801 // private
32802 // This is a support class used internally by the Grid components
32803 Roo.grid.HeaderDragZone = function(grid, hd, hd2){
32804     this.grid = grid;
32805     this.view = grid.getView();
32806     this.ddGroup = "gridHeader" + this.grid.getGridEl().id;
32807     Roo.grid.HeaderDragZone.superclass.constructor.call(this, hd);
32808     if(hd2){
32809         this.setHandleElId(Roo.id(hd));
32810         this.setOuterHandleElId(Roo.id(hd2));
32811     }
32812     this.scroll = false;
32813 };
32814 Roo.extend(Roo.grid.HeaderDragZone, Roo.dd.DragZone, {
32815     maxDragWidth: 120,
32816     getDragData : function(e){
32817         var t = Roo.lib.Event.getTarget(e);
32818         var h = this.view.findHeaderCell(t);
32819         if(h){
32820             return {ddel: h.firstChild, header:h};
32821         }
32822         return false;
32823     },
32824
32825     onInitDrag : function(e){
32826         this.view.headersDisabled = true;
32827         var clone = this.dragData.ddel.cloneNode(true);
32828         clone.id = Roo.id();
32829         clone.style.width = Math.min(this.dragData.header.offsetWidth,this.maxDragWidth) + "px";
32830         this.proxy.update(clone);
32831         return true;
32832     },
32833
32834     afterValidDrop : function(){
32835         var v = this.view;
32836         setTimeout(function(){
32837             v.headersDisabled = false;
32838         }, 50);
32839     },
32840
32841     afterInvalidDrop : function(){
32842         var v = this.view;
32843         setTimeout(function(){
32844             v.headersDisabled = false;
32845         }, 50);
32846     }
32847 });
32848 /*
32849  * Based on:
32850  * Ext JS Library 1.1.1
32851  * Copyright(c) 2006-2007, Ext JS, LLC.
32852  *
32853  * Originally Released Under LGPL - original licence link has changed is not relivant.
32854  *
32855  * Fork - LGPL
32856  * <script type="text/javascript">
32857  */
32858 // private
32859 // This is a support class used internally by the Grid components
32860 Roo.grid.HeaderDropZone = function(grid, hd, hd2){
32861     this.grid = grid;
32862     this.view = grid.getView();
32863     // split the proxies so they don't interfere with mouse events
32864     this.proxyTop = Roo.DomHelper.append(document.body, {
32865         cls:"col-move-top", html:"&#160;"
32866     }, true);
32867     this.proxyBottom = Roo.DomHelper.append(document.body, {
32868         cls:"col-move-bottom", html:"&#160;"
32869     }, true);
32870     this.proxyTop.hide = this.proxyBottom.hide = function(){
32871         this.setLeftTop(-100,-100);
32872         this.setStyle("visibility", "hidden");
32873     };
32874     this.ddGroup = "gridHeader" + this.grid.getGridEl().id;
32875     // temporarily disabled
32876     //Roo.dd.ScrollManager.register(this.view.scroller.dom);
32877     Roo.grid.HeaderDropZone.superclass.constructor.call(this, grid.getGridEl().dom);
32878 };
32879 Roo.extend(Roo.grid.HeaderDropZone, Roo.dd.DropZone, {
32880     proxyOffsets : [-4, -9],
32881     fly: Roo.Element.fly,
32882
32883     getTargetFromEvent : function(e){
32884         var t = Roo.lib.Event.getTarget(e);
32885         var cindex = this.view.findCellIndex(t);
32886         if(cindex !== false){
32887             return this.view.getHeaderCell(cindex);
32888         }
32889         return null;
32890     },
32891
32892     nextVisible : function(h){
32893         var v = this.view, cm = this.grid.colModel;
32894         h = h.nextSibling;
32895         while(h){
32896             if(!cm.isHidden(v.getCellIndex(h))){
32897                 return h;
32898             }
32899             h = h.nextSibling;
32900         }
32901         return null;
32902     },
32903
32904     prevVisible : function(h){
32905         var v = this.view, cm = this.grid.colModel;
32906         h = h.prevSibling;
32907         while(h){
32908             if(!cm.isHidden(v.getCellIndex(h))){
32909                 return h;
32910             }
32911             h = h.prevSibling;
32912         }
32913         return null;
32914     },
32915
32916     positionIndicator : function(h, n, e){
32917         var x = Roo.lib.Event.getPageX(e);
32918         var r = Roo.lib.Dom.getRegion(n.firstChild);
32919         var px, pt, py = r.top + this.proxyOffsets[1];
32920         if((r.right - x) <= (r.right-r.left)/2){
32921             px = r.right+this.view.borderWidth;
32922             pt = "after";
32923         }else{
32924             px = r.left;
32925             pt = "before";
32926         }
32927         var oldIndex = this.view.getCellIndex(h);
32928         var newIndex = this.view.getCellIndex(n);
32929
32930         if(this.grid.colModel.isFixed(newIndex)){
32931             return false;
32932         }
32933
32934         var locked = this.grid.colModel.isLocked(newIndex);
32935
32936         if(pt == "after"){
32937             newIndex++;
32938         }
32939         if(oldIndex < newIndex){
32940             newIndex--;
32941         }
32942         if(oldIndex == newIndex && (locked == this.grid.colModel.isLocked(oldIndex))){
32943             return false;
32944         }
32945         px +=  this.proxyOffsets[0];
32946         this.proxyTop.setLeftTop(px, py);
32947         this.proxyTop.show();
32948         if(!this.bottomOffset){
32949             this.bottomOffset = this.view.mainHd.getHeight();
32950         }
32951         this.proxyBottom.setLeftTop(px, py+this.proxyTop.dom.offsetHeight+this.bottomOffset);
32952         this.proxyBottom.show();
32953         return pt;
32954     },
32955
32956     onNodeEnter : function(n, dd, e, data){
32957         if(data.header != n){
32958             this.positionIndicator(data.header, n, e);
32959         }
32960     },
32961
32962     onNodeOver : function(n, dd, e, data){
32963         var result = false;
32964         if(data.header != n){
32965             result = this.positionIndicator(data.header, n, e);
32966         }
32967         if(!result){
32968             this.proxyTop.hide();
32969             this.proxyBottom.hide();
32970         }
32971         return result ? this.dropAllowed : this.dropNotAllowed;
32972     },
32973
32974     onNodeOut : function(n, dd, e, data){
32975         this.proxyTop.hide();
32976         this.proxyBottom.hide();
32977     },
32978
32979     onNodeDrop : function(n, dd, e, data){
32980         var h = data.header;
32981         if(h != n){
32982             var cm = this.grid.colModel;
32983             var x = Roo.lib.Event.getPageX(e);
32984             var r = Roo.lib.Dom.getRegion(n.firstChild);
32985             var pt = (r.right - x) <= ((r.right-r.left)/2) ? "after" : "before";
32986             var oldIndex = this.view.getCellIndex(h);
32987             var newIndex = this.view.getCellIndex(n);
32988             var locked = cm.isLocked(newIndex);
32989             if(pt == "after"){
32990                 newIndex++;
32991             }
32992             if(oldIndex < newIndex){
32993                 newIndex--;
32994             }
32995             if(oldIndex == newIndex && (locked == cm.isLocked(oldIndex))){
32996                 return false;
32997             }
32998             cm.setLocked(oldIndex, locked, true);
32999             cm.moveColumn(oldIndex, newIndex);
33000             this.grid.fireEvent("columnmove", oldIndex, newIndex);
33001             return true;
33002         }
33003         return false;
33004     }
33005 });
33006 /*
33007  * Based on:
33008  * Ext JS Library 1.1.1
33009  * Copyright(c) 2006-2007, Ext JS, LLC.
33010  *
33011  * Originally Released Under LGPL - original licence link has changed is not relivant.
33012  *
33013  * Fork - LGPL
33014  * <script type="text/javascript">
33015  */
33016   
33017 /**
33018  * @class Roo.grid.GridView
33019  * @extends Roo.util.Observable
33020  *
33021  * @constructor
33022  * @param {Object} config
33023  */
33024 Roo.grid.GridView = function(config){
33025     Roo.grid.GridView.superclass.constructor.call(this);
33026     this.el = null;
33027
33028     Roo.apply(this, config);
33029 };
33030
33031 Roo.extend(Roo.grid.GridView, Roo.grid.AbstractGridView, {
33032
33033     /**
33034      * Override this function to apply custom css classes to rows during rendering
33035      * @param {Record} record The record
33036      * @param {Number} index
33037      * @method getRowClass
33038      */
33039     rowClass : "x-grid-row",
33040
33041     cellClass : "x-grid-col",
33042
33043     tdClass : "x-grid-td",
33044
33045     hdClass : "x-grid-hd",
33046
33047     splitClass : "x-grid-split",
33048
33049     sortClasses : ["sort-asc", "sort-desc"],
33050
33051     enableMoveAnim : false,
33052
33053     hlColor: "C3DAF9",
33054
33055     dh : Roo.DomHelper,
33056
33057     fly : Roo.Element.fly,
33058
33059     css : Roo.util.CSS,
33060
33061     borderWidth: 1,
33062
33063     splitOffset: 3,
33064
33065     scrollIncrement : 22,
33066
33067     cellRE: /(?:.*?)x-grid-(?:hd|cell|csplit)-(?:[\d]+)-([\d]+)(?:.*?)/,
33068
33069     findRE: /\s?(?:x-grid-hd|x-grid-col|x-grid-csplit)\s/,
33070
33071     bind : function(ds, cm){
33072         if(this.ds){
33073             this.ds.un("load", this.onLoad, this);
33074             this.ds.un("datachanged", this.onDataChange, this);
33075             this.ds.un("add", this.onAdd, this);
33076             this.ds.un("remove", this.onRemove, this);
33077             this.ds.un("update", this.onUpdate, this);
33078             this.ds.un("clear", this.onClear, this);
33079         }
33080         if(ds){
33081             ds.on("load", this.onLoad, this);
33082             ds.on("datachanged", this.onDataChange, this);
33083             ds.on("add", this.onAdd, this);
33084             ds.on("remove", this.onRemove, this);
33085             ds.on("update", this.onUpdate, this);
33086             ds.on("clear", this.onClear, this);
33087         }
33088         this.ds = ds;
33089
33090         if(this.cm){
33091             this.cm.un("widthchange", this.onColWidthChange, this);
33092             this.cm.un("headerchange", this.onHeaderChange, this);
33093             this.cm.un("hiddenchange", this.onHiddenChange, this);
33094             this.cm.un("columnmoved", this.onColumnMove, this);
33095             this.cm.un("columnlockchange", this.onColumnLock, this);
33096         }
33097         if(cm){
33098             this.generateRules(cm);
33099             cm.on("widthchange", this.onColWidthChange, this);
33100             cm.on("headerchange", this.onHeaderChange, this);
33101             cm.on("hiddenchange", this.onHiddenChange, this);
33102             cm.on("columnmoved", this.onColumnMove, this);
33103             cm.on("columnlockchange", this.onColumnLock, this);
33104         }
33105         this.cm = cm;
33106     },
33107
33108     init: function(grid){
33109         Roo.grid.GridView.superclass.init.call(this, grid);
33110
33111         this.bind(grid.dataSource, grid.colModel);
33112
33113         grid.on("headerclick", this.handleHeaderClick, this);
33114
33115         if(grid.trackMouseOver){
33116             grid.on("mouseover", this.onRowOver, this);
33117             grid.on("mouseout", this.onRowOut, this);
33118         }
33119         grid.cancelTextSelection = function(){};
33120         this.gridId = grid.id;
33121
33122         var tpls = this.templates || {};
33123
33124         if(!tpls.master){
33125             tpls.master = new Roo.Template(
33126                '<div class="x-grid" hidefocus="true">',
33127                 '<a href="#" class="x-grid-focus" tabIndex="-1"></a>',
33128                   '<div class="x-grid-topbar"></div>',
33129                   '<div class="x-grid-scroller"><div></div></div>',
33130                   '<div class="x-grid-locked">',
33131                       '<div class="x-grid-header">{lockedHeader}</div>',
33132                       '<div class="x-grid-body">{lockedBody}</div>',
33133                   "</div>",
33134                   '<div class="x-grid-viewport">',
33135                       '<div class="x-grid-header">{header}</div>',
33136                       '<div class="x-grid-body">{body}</div>',
33137                   "</div>",
33138                   '<div class="x-grid-bottombar"></div>',
33139                  
33140                   '<div class="x-grid-resize-proxy">&#160;</div>',
33141                "</div>"
33142             );
33143             tpls.master.disableformats = true;
33144         }
33145
33146         if(!tpls.header){
33147             tpls.header = new Roo.Template(
33148                '<table border="0" cellspacing="0" cellpadding="0">',
33149                '<tbody><tr class="x-grid-hd-row">{cells}</tr></tbody>',
33150                "</table>{splits}"
33151             );
33152             tpls.header.disableformats = true;
33153         }
33154         tpls.header.compile();
33155
33156         if(!tpls.hcell){
33157             tpls.hcell = new Roo.Template(
33158                 '<td class="x-grid-hd x-grid-td-{id} {cellId}"><div title="{title}" class="x-grid-hd-inner x-grid-hd-{id}">',
33159                 '<div class="x-grid-hd-text" unselectable="on">{value}<img class="x-grid-sort-icon" src="', Roo.BLANK_IMAGE_URL, '" /></div>',
33160                 "</div></td>"
33161              );
33162              tpls.hcell.disableFormats = true;
33163         }
33164         tpls.hcell.compile();
33165
33166         if(!tpls.hsplit){
33167             tpls.hsplit = new Roo.Template('<div class="x-grid-split {splitId} x-grid-split-{id}" style="{style}" unselectable="on">&#160;</div>');
33168             tpls.hsplit.disableFormats = true;
33169         }
33170         tpls.hsplit.compile();
33171
33172         if(!tpls.body){
33173             tpls.body = new Roo.Template(
33174                '<table border="0" cellspacing="0" cellpadding="0">',
33175                "<tbody>{rows}</tbody>",
33176                "</table>"
33177             );
33178             tpls.body.disableFormats = true;
33179         }
33180         tpls.body.compile();
33181
33182         if(!tpls.row){
33183             tpls.row = new Roo.Template('<tr class="x-grid-row {alt}">{cells}</tr>');
33184             tpls.row.disableFormats = true;
33185         }
33186         tpls.row.compile();
33187
33188         if(!tpls.cell){
33189             tpls.cell = new Roo.Template(
33190                 '<td class="x-grid-col x-grid-td-{id} {cellId} {css}" tabIndex="0">',
33191                 '<div class="x-grid-col-{id} x-grid-cell-inner"><div class="x-grid-cell-text" unselectable="on" {attr}>{value}</div></div>',
33192                 "</td>"
33193             );
33194             tpls.cell.disableFormats = true;
33195         }
33196         tpls.cell.compile();
33197
33198         this.templates = tpls;
33199     },
33200
33201     // remap these for backwards compat
33202     onColWidthChange : function(){
33203         this.updateColumns.apply(this, arguments);
33204     },
33205     onHeaderChange : function(){
33206         this.updateHeaders.apply(this, arguments);
33207     }, 
33208     onHiddenChange : function(){
33209         this.handleHiddenChange.apply(this, arguments);
33210     },
33211     onColumnMove : function(){
33212         this.handleColumnMove.apply(this, arguments);
33213     },
33214     onColumnLock : function(){
33215         this.handleLockChange.apply(this, arguments);
33216     },
33217
33218     onDataChange : function(){
33219         this.refresh();
33220         this.updateHeaderSortState();
33221     },
33222
33223     onClear : function(){
33224         this.refresh();
33225     },
33226
33227     onUpdate : function(ds, record){
33228         this.refreshRow(record);
33229     },
33230
33231     refreshRow : function(record){
33232         var ds = this.ds, index;
33233         if(typeof record == 'number'){
33234             index = record;
33235             record = ds.getAt(index);
33236         }else{
33237             index = ds.indexOf(record);
33238         }
33239         this.insertRows(ds, index, index, true);
33240         this.onRemove(ds, record, index+1, true);
33241         this.syncRowHeights(index, index);
33242         this.layout();
33243         this.fireEvent("rowupdated", this, index, record);
33244     },
33245
33246     onAdd : function(ds, records, index){
33247         this.insertRows(ds, index, index + (records.length-1));
33248     },
33249
33250     onRemove : function(ds, record, index, isUpdate){
33251         if(isUpdate !== true){
33252             this.fireEvent("beforerowremoved", this, index, record);
33253         }
33254         var bt = this.getBodyTable(), lt = this.getLockedTable();
33255         if(bt.rows[index]){
33256             bt.firstChild.removeChild(bt.rows[index]);
33257         }
33258         if(lt.rows[index]){
33259             lt.firstChild.removeChild(lt.rows[index]);
33260         }
33261         if(isUpdate !== true){
33262             this.stripeRows(index);
33263             this.syncRowHeights(index, index);
33264             this.layout();
33265             this.fireEvent("rowremoved", this, index, record);
33266         }
33267     },
33268
33269     onLoad : function(){
33270         this.scrollToTop();
33271     },
33272
33273     /**
33274      * Scrolls the grid to the top
33275      */
33276     scrollToTop : function(){
33277         if(this.scroller){
33278             this.scroller.dom.scrollTop = 0;
33279             this.syncScroll();
33280         }
33281     },
33282
33283     /**
33284      * Gets a panel in the header of the grid that can be used for toolbars etc.
33285      * After modifying the contents of this panel a call to grid.autoSize() may be
33286      * required to register any changes in size.
33287      * @param {Boolean} doShow By default the header is hidden. Pass true to show the panel
33288      * @return Roo.Element
33289      */
33290     getHeaderPanel : function(doShow){
33291         if(doShow){
33292             this.headerPanel.show();
33293         }
33294         return this.headerPanel;
33295     },
33296
33297     /**
33298      * Gets a panel in the footer of the grid that can be used for toolbars etc.
33299      * After modifying the contents of this panel a call to grid.autoSize() may be
33300      * required to register any changes in size.
33301      * @param {Boolean} doShow By default the footer is hidden. Pass true to show the panel
33302      * @return Roo.Element
33303      */
33304     getFooterPanel : function(doShow){
33305         if(doShow){
33306             this.footerPanel.show();
33307         }
33308         return this.footerPanel;
33309     },
33310
33311     initElements : function(){
33312         var E = Roo.Element;
33313         var el = this.grid.getGridEl().dom.firstChild;
33314         var cs = el.childNodes;
33315
33316         this.el = new E(el);
33317         
33318          this.focusEl = new E(el.firstChild);
33319         this.focusEl.swallowEvent("click", true);
33320         
33321         this.headerPanel = new E(cs[1]);
33322         this.headerPanel.enableDisplayMode("block");
33323
33324         this.scroller = new E(cs[2]);
33325         this.scrollSizer = new E(this.scroller.dom.firstChild);
33326
33327         this.lockedWrap = new E(cs[3]);
33328         this.lockedHd = new E(this.lockedWrap.dom.firstChild);
33329         this.lockedBody = new E(this.lockedWrap.dom.childNodes[1]);
33330
33331         this.mainWrap = new E(cs[4]);
33332         this.mainHd = new E(this.mainWrap.dom.firstChild);
33333         this.mainBody = new E(this.mainWrap.dom.childNodes[1]);
33334
33335         this.footerPanel = new E(cs[5]);
33336         this.footerPanel.enableDisplayMode("block");
33337
33338         this.resizeProxy = new E(cs[6]);
33339
33340         this.headerSelector = String.format(
33341            '#{0} td.x-grid-hd, #{1} td.x-grid-hd',
33342            this.lockedHd.id, this.mainHd.id
33343         );
33344
33345         this.splitterSelector = String.format(
33346            '#{0} div.x-grid-split, #{1} div.x-grid-split',
33347            this.idToCssName(this.lockedHd.id), this.idToCssName(this.mainHd.id)
33348         );
33349     },
33350     idToCssName : function(s)
33351     {
33352         return s.replace(/[^a-z0-9]+/ig, '-');
33353     },
33354
33355     getHeaderCell : function(index){
33356         return Roo.DomQuery.select(this.headerSelector)[index];
33357     },
33358
33359     getHeaderCellMeasure : function(index){
33360         return this.getHeaderCell(index).firstChild;
33361     },
33362
33363     getHeaderCellText : function(index){
33364         return this.getHeaderCell(index).firstChild.firstChild;
33365     },
33366
33367     getLockedTable : function(){
33368         return this.lockedBody.dom.firstChild;
33369     },
33370
33371     getBodyTable : function(){
33372         return this.mainBody.dom.firstChild;
33373     },
33374
33375     getLockedRow : function(index){
33376         return this.getLockedTable().rows[index];
33377     },
33378
33379     getRow : function(index){
33380         return this.getBodyTable().rows[index];
33381     },
33382
33383     getRowComposite : function(index){
33384         if(!this.rowEl){
33385             this.rowEl = new Roo.CompositeElementLite();
33386         }
33387         var els = [], lrow, mrow;
33388         if(lrow = this.getLockedRow(index)){
33389             els.push(lrow);
33390         }
33391         if(mrow = this.getRow(index)){
33392             els.push(mrow);
33393         }
33394         this.rowEl.elements = els;
33395         return this.rowEl;
33396     },
33397
33398     getCell : function(rowIndex, colIndex){
33399         var locked = this.cm.getLockedCount();
33400         var source;
33401         if(colIndex < locked){
33402             source = this.lockedBody.dom.firstChild;
33403         }else{
33404             source = this.mainBody.dom.firstChild;
33405             colIndex -= locked;
33406         }
33407         return source.rows[rowIndex].childNodes[colIndex];
33408     },
33409
33410     getCellText : function(rowIndex, colIndex){
33411         return this.getCell(rowIndex, colIndex).firstChild.firstChild;
33412     },
33413
33414     getCellBox : function(cell){
33415         var b = this.fly(cell).getBox();
33416         if(Roo.isOpera){ // opera fails to report the Y
33417             b.y = cell.offsetTop + this.mainBody.getY();
33418         }
33419         return b;
33420     },
33421
33422     getCellIndex : function(cell){
33423         var id = String(cell.className).match(this.cellRE);
33424         if(id){
33425             return parseInt(id[1], 10);
33426         }
33427         return 0;
33428     },
33429
33430     findHeaderIndex : function(n){
33431         var r = Roo.fly(n).findParent("td." + this.hdClass, 6);
33432         return r ? this.getCellIndex(r) : false;
33433     },
33434
33435     findHeaderCell : function(n){
33436         var r = Roo.fly(n).findParent("td." + this.hdClass, 6);
33437         return r ? r : false;
33438     },
33439
33440     findRowIndex : function(n){
33441         if(!n){
33442             return false;
33443         }
33444         var r = Roo.fly(n).findParent("tr." + this.rowClass, 6);
33445         return r ? r.rowIndex : false;
33446     },
33447
33448     findCellIndex : function(node){
33449         var stop = this.el.dom;
33450         while(node && node != stop){
33451             if(this.findRE.test(node.className)){
33452                 return this.getCellIndex(node);
33453             }
33454             node = node.parentNode;
33455         }
33456         return false;
33457     },
33458
33459     getColumnId : function(index){
33460         return this.cm.getColumnId(index);
33461     },
33462
33463     getSplitters : function()
33464     {
33465         if(this.splitterSelector){
33466            return Roo.DomQuery.select(this.splitterSelector);
33467         }else{
33468             return null;
33469       }
33470     },
33471
33472     getSplitter : function(index){
33473         return this.getSplitters()[index];
33474     },
33475
33476     onRowOver : function(e, t){
33477         var row;
33478         if((row = this.findRowIndex(t)) !== false){
33479             this.getRowComposite(row).addClass("x-grid-row-over");
33480         }
33481     },
33482
33483     onRowOut : function(e, t){
33484         var row;
33485         if((row = this.findRowIndex(t)) !== false && row !== this.findRowIndex(e.getRelatedTarget())){
33486             this.getRowComposite(row).removeClass("x-grid-row-over");
33487         }
33488     },
33489
33490     renderHeaders : function(){
33491         var cm = this.cm;
33492         var ct = this.templates.hcell, ht = this.templates.header, st = this.templates.hsplit;
33493         var cb = [], lb = [], sb = [], lsb = [], p = {};
33494         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
33495             p.cellId = "x-grid-hd-0-" + i;
33496             p.splitId = "x-grid-csplit-0-" + i;
33497             p.id = cm.getColumnId(i);
33498             p.title = cm.getColumnTooltip(i) || "";
33499             p.value = cm.getColumnHeader(i) || "";
33500             p.style = (this.grid.enableColumnResize === false || !cm.isResizable(i) || cm.isFixed(i)) ? 'cursor:default' : '';
33501             if(!cm.isLocked(i)){
33502                 cb[cb.length] = ct.apply(p);
33503                 sb[sb.length] = st.apply(p);
33504             }else{
33505                 lb[lb.length] = ct.apply(p);
33506                 lsb[lsb.length] = st.apply(p);
33507             }
33508         }
33509         return [ht.apply({cells: lb.join(""), splits:lsb.join("")}),
33510                 ht.apply({cells: cb.join(""), splits:sb.join("")})];
33511     },
33512
33513     updateHeaders : function(){
33514         var html = this.renderHeaders();
33515         this.lockedHd.update(html[0]);
33516         this.mainHd.update(html[1]);
33517     },
33518
33519     /**
33520      * Focuses the specified row.
33521      * @param {Number} row The row index
33522      */
33523     focusRow : function(row)
33524     {
33525         //Roo.log('GridView.focusRow');
33526         var x = this.scroller.dom.scrollLeft;
33527         this.focusCell(row, 0, false);
33528         this.scroller.dom.scrollLeft = x;
33529     },
33530
33531     /**
33532      * Focuses the specified cell.
33533      * @param {Number} row The row index
33534      * @param {Number} col The column index
33535      * @param {Boolean} hscroll false to disable horizontal scrolling
33536      */
33537     focusCell : function(row, col, hscroll)
33538     {
33539         //Roo.log('GridView.focusCell');
33540         var el = this.ensureVisible(row, col, hscroll);
33541         this.focusEl.alignTo(el, "tl-tl");
33542         if(Roo.isGecko){
33543             this.focusEl.focus();
33544         }else{
33545             this.focusEl.focus.defer(1, this.focusEl);
33546         }
33547     },
33548
33549     /**
33550      * Scrolls the specified cell into view
33551      * @param {Number} row The row index
33552      * @param {Number} col The column index
33553      * @param {Boolean} hscroll false to disable horizontal scrolling
33554      */
33555     ensureVisible : function(row, col, hscroll)
33556     {
33557         //Roo.log('GridView.ensureVisible,' + row + ',' + col);
33558         //return null; //disable for testing.
33559         if(typeof row != "number"){
33560             row = row.rowIndex;
33561         }
33562         if(row < 0 && row >= this.ds.getCount()){
33563             return  null;
33564         }
33565         col = (col !== undefined ? col : 0);
33566         var cm = this.grid.colModel;
33567         while(cm.isHidden(col)){
33568             col++;
33569         }
33570
33571         var el = this.getCell(row, col);
33572         if(!el){
33573             return null;
33574         }
33575         var c = this.scroller.dom;
33576
33577         var ctop = parseInt(el.offsetTop, 10);
33578         var cleft = parseInt(el.offsetLeft, 10);
33579         var cbot = ctop + el.offsetHeight;
33580         var cright = cleft + el.offsetWidth;
33581         
33582         var ch = c.clientHeight - this.mainHd.dom.offsetHeight;
33583         var stop = parseInt(c.scrollTop, 10);
33584         var sleft = parseInt(c.scrollLeft, 10);
33585         var sbot = stop + ch;
33586         var sright = sleft + c.clientWidth;
33587         /*
33588         Roo.log('GridView.ensureVisible:' +
33589                 ' ctop:' + ctop +
33590                 ' c.clientHeight:' + c.clientHeight +
33591                 ' this.mainHd.dom.offsetHeight:' + this.mainHd.dom.offsetHeight +
33592                 ' stop:' + stop +
33593                 ' cbot:' + cbot +
33594                 ' sbot:' + sbot +
33595                 ' ch:' + ch  
33596                 );
33597         */
33598         if(ctop < stop){
33599              c.scrollTop = ctop;
33600             //Roo.log("set scrolltop to ctop DISABLE?");
33601         }else if(cbot > sbot){
33602             //Roo.log("set scrolltop to cbot-ch");
33603             c.scrollTop = cbot-ch;
33604         }
33605         
33606         if(hscroll !== false){
33607             if(cleft < sleft){
33608                 c.scrollLeft = cleft;
33609             }else if(cright > sright){
33610                 c.scrollLeft = cright-c.clientWidth;
33611             }
33612         }
33613          
33614         return el;
33615     },
33616
33617     updateColumns : function(){
33618         this.grid.stopEditing();
33619         var cm = this.grid.colModel, colIds = this.getColumnIds();
33620         //var totalWidth = cm.getTotalWidth();
33621         var pos = 0;
33622         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
33623             //if(cm.isHidden(i)) continue;
33624             var w = cm.getColumnWidth(i);
33625             this.css.updateRule(this.colSelector+this.idToCssName(colIds[i]), "width", (w - this.borderWidth) + "px");
33626             this.css.updateRule(this.hdSelector+this.idToCssName(colIds[i]), "width", (w - this.borderWidth) + "px");
33627         }
33628         this.updateSplitters();
33629     },
33630
33631     generateRules : function(cm){
33632         var ruleBuf = [], rulesId = this.idToCssName(this.grid.id)+ '-cssrules';
33633         Roo.util.CSS.removeStyleSheet(rulesId);
33634         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
33635             var cid = cm.getColumnId(i);
33636             var align = '';
33637             if(cm.config[i].align){
33638                 align = 'text-align:'+cm.config[i].align+';';
33639             }
33640             var hidden = '';
33641             if(cm.isHidden(i)){
33642                 hidden = 'display:none;';
33643             }
33644             var width = "width:" + (cm.getColumnWidth(i) - this.borderWidth) + "px;";
33645             ruleBuf.push(
33646                     this.colSelector, cid, " {\n", cm.config[i].css, align, width, "\n}\n",
33647                     this.hdSelector, cid, " {\n", align, width, "}\n",
33648                     this.tdSelector, cid, " {\n",hidden,"\n}\n",
33649                     this.splitSelector, cid, " {\n", hidden , "\n}\n");
33650         }
33651         return Roo.util.CSS.createStyleSheet(ruleBuf.join(""), rulesId);
33652     },
33653
33654     updateSplitters : function(){
33655         var cm = this.cm, s = this.getSplitters();
33656         if(s){ // splitters not created yet
33657             var pos = 0, locked = true;
33658             for(var i = 0, len = cm.getColumnCount(); i < len; i++){
33659                 if(cm.isHidden(i)) continue;
33660                 var w = cm.getColumnWidth(i); // make sure it's a number
33661                 if(!cm.isLocked(i) && locked){
33662                     pos = 0;
33663                     locked = false;
33664                 }
33665                 pos += w;
33666                 s[i].style.left = (pos-this.splitOffset) + "px";
33667             }
33668         }
33669     },
33670
33671     handleHiddenChange : function(colModel, colIndex, hidden){
33672         if(hidden){
33673             this.hideColumn(colIndex);
33674         }else{
33675             this.unhideColumn(colIndex);
33676         }
33677     },
33678
33679     hideColumn : function(colIndex){
33680         var cid = this.getColumnId(colIndex);
33681         this.css.updateRule(this.tdSelector+this.idToCssName(cid), "display", "none");
33682         this.css.updateRule(this.splitSelector+this.idToCssName(cid), "display", "none");
33683         if(Roo.isSafari){
33684             this.updateHeaders();
33685         }
33686         this.updateSplitters();
33687         this.layout();
33688     },
33689
33690     unhideColumn : function(colIndex){
33691         var cid = this.getColumnId(colIndex);
33692         this.css.updateRule(this.tdSelector+this.idToCssName(cid), "display", "");
33693         this.css.updateRule(this.splitSelector+this.idToCssName(cid), "display", "");
33694
33695         if(Roo.isSafari){
33696             this.updateHeaders();
33697         }
33698         this.updateSplitters();
33699         this.layout();
33700     },
33701
33702     insertRows : function(dm, firstRow, lastRow, isUpdate){
33703         if(firstRow == 0 && lastRow == dm.getCount()-1){
33704             this.refresh();
33705         }else{
33706             if(!isUpdate){
33707                 this.fireEvent("beforerowsinserted", this, firstRow, lastRow);
33708             }
33709             var s = this.getScrollState();
33710             var markup = this.renderRows(firstRow, lastRow);
33711             this.bufferRows(markup[0], this.getLockedTable(), firstRow);
33712             this.bufferRows(markup[1], this.getBodyTable(), firstRow);
33713             this.restoreScroll(s);
33714             if(!isUpdate){
33715                 this.fireEvent("rowsinserted", this, firstRow, lastRow);
33716                 this.syncRowHeights(firstRow, lastRow);
33717                 this.stripeRows(firstRow);
33718                 this.layout();
33719             }
33720         }
33721     },
33722
33723     bufferRows : function(markup, target, index){
33724         var before = null, trows = target.rows, tbody = target.tBodies[0];
33725         if(index < trows.length){
33726             before = trows[index];
33727         }
33728         var b = document.createElement("div");
33729         b.innerHTML = "<table><tbody>"+markup+"</tbody></table>";
33730         var rows = b.firstChild.rows;
33731         for(var i = 0, len = rows.length; i < len; i++){
33732             if(before){
33733                 tbody.insertBefore(rows[0], before);
33734             }else{
33735                 tbody.appendChild(rows[0]);
33736             }
33737         }
33738         b.innerHTML = "";
33739         b = null;
33740     },
33741
33742     deleteRows : function(dm, firstRow, lastRow){
33743         if(dm.getRowCount()<1){
33744             this.fireEvent("beforerefresh", this);
33745             this.mainBody.update("");
33746             this.lockedBody.update("");
33747             this.fireEvent("refresh", this);
33748         }else{
33749             this.fireEvent("beforerowsdeleted", this, firstRow, lastRow);
33750             var bt = this.getBodyTable();
33751             var tbody = bt.firstChild;
33752             var rows = bt.rows;
33753             for(var rowIndex = firstRow; rowIndex <= lastRow; rowIndex++){
33754                 tbody.removeChild(rows[firstRow]);
33755             }
33756             this.stripeRows(firstRow);
33757             this.fireEvent("rowsdeleted", this, firstRow, lastRow);
33758         }
33759     },
33760
33761     updateRows : function(dataSource, firstRow, lastRow){
33762         var s = this.getScrollState();
33763         this.refresh();
33764         this.restoreScroll(s);
33765     },
33766
33767     handleSort : function(dataSource, sortColumnIndex, sortDir, noRefresh){
33768         if(!noRefresh){
33769            this.refresh();
33770         }
33771         this.updateHeaderSortState();
33772     },
33773
33774     getScrollState : function(){
33775         
33776         var sb = this.scroller.dom;
33777         return {left: sb.scrollLeft, top: sb.scrollTop};
33778     },
33779
33780     stripeRows : function(startRow){
33781         if(!this.grid.stripeRows || this.ds.getCount() < 1){
33782             return;
33783         }
33784         startRow = startRow || 0;
33785         var rows = this.getBodyTable().rows;
33786         var lrows = this.getLockedTable().rows;
33787         var cls = ' x-grid-row-alt ';
33788         for(var i = startRow, len = rows.length; i < len; i++){
33789             var row = rows[i], lrow = lrows[i];
33790             var isAlt = ((i+1) % 2 == 0);
33791             var hasAlt = (' '+row.className + ' ').indexOf(cls) != -1;
33792             if(isAlt == hasAlt){
33793                 continue;
33794             }
33795             if(isAlt){
33796                 row.className += " x-grid-row-alt";
33797             }else{
33798                 row.className = row.className.replace("x-grid-row-alt", "");
33799             }
33800             if(lrow){
33801                 lrow.className = row.className;
33802             }
33803         }
33804     },
33805
33806     restoreScroll : function(state){
33807         //Roo.log('GridView.restoreScroll');
33808         var sb = this.scroller.dom;
33809         sb.scrollLeft = state.left;
33810         sb.scrollTop = state.top;
33811         this.syncScroll();
33812     },
33813
33814     syncScroll : function(){
33815         //Roo.log('GridView.syncScroll');
33816         var sb = this.scroller.dom;
33817         var sh = this.mainHd.dom;
33818         var bs = this.mainBody.dom;
33819         var lv = this.lockedBody.dom;
33820         sh.scrollLeft = bs.scrollLeft = sb.scrollLeft;
33821         lv.scrollTop = bs.scrollTop = sb.scrollTop;
33822     },
33823
33824     handleScroll : function(e){
33825         this.syncScroll();
33826         var sb = this.scroller.dom;
33827         this.grid.fireEvent("bodyscroll", sb.scrollLeft, sb.scrollTop);
33828         e.stopEvent();
33829     },
33830
33831     handleWheel : function(e){
33832         var d = e.getWheelDelta();
33833         this.scroller.dom.scrollTop -= d*22;
33834         // set this here to prevent jumpy scrolling on large tables
33835         this.lockedBody.dom.scrollTop = this.mainBody.dom.scrollTop = this.scroller.dom.scrollTop;
33836         e.stopEvent();
33837     },
33838
33839     renderRows : function(startRow, endRow){
33840         // pull in all the crap needed to render rows
33841         var g = this.grid, cm = g.colModel, ds = g.dataSource, stripe = g.stripeRows;
33842         var colCount = cm.getColumnCount();
33843
33844         if(ds.getCount() < 1){
33845             return ["", ""];
33846         }
33847
33848         // build a map for all the columns
33849         var cs = [];
33850         for(var i = 0; i < colCount; i++){
33851             var name = cm.getDataIndex(i);
33852             cs[i] = {
33853                 name : typeof name == 'undefined' ? ds.fields.get(i).name : name,
33854                 renderer : cm.getRenderer(i),
33855                 id : cm.getColumnId(i),
33856                 locked : cm.isLocked(i)
33857             };
33858         }
33859
33860         startRow = startRow || 0;
33861         endRow = typeof endRow == "undefined"? ds.getCount()-1 : endRow;
33862
33863         // records to render
33864         var rs = ds.getRange(startRow, endRow);
33865
33866         return this.doRender(cs, rs, ds, startRow, colCount, stripe);
33867     },
33868
33869     // As much as I hate to duplicate code, this was branched because FireFox really hates
33870     // [].join("") on strings. The performance difference was substantial enough to
33871     // branch this function
33872     doRender : Roo.isGecko ?
33873             function(cs, rs, ds, startRow, colCount, stripe){
33874                 var ts = this.templates, ct = ts.cell, rt = ts.row;
33875                 // buffers
33876                 var buf = "", lbuf = "", cb, lcb, c, p = {}, rp = {}, r, rowIndex;
33877                 
33878                 var hasListener = this.grid.hasListener('rowclass');
33879                 var rowcfg = {};
33880                 for(var j = 0, len = rs.length; j < len; j++){
33881                     r = rs[j]; cb = ""; lcb = ""; rowIndex = (j+startRow);
33882                     for(var i = 0; i < colCount; i++){
33883                         c = cs[i];
33884                         p.cellId = "x-grid-cell-" + rowIndex + "-" + i;
33885                         p.id = c.id;
33886                         p.css = p.attr = "";
33887                         p.value = c.renderer(r.data[c.name], p, r, rowIndex, i, ds);
33888                         if(p.value == undefined || p.value === "") p.value = "&#160;";
33889                         if(r.dirty && typeof r.modified[c.name] !== 'undefined'){
33890                             p.css += p.css ? ' x-grid-dirty-cell' : 'x-grid-dirty-cell';
33891                         }
33892                         var markup = ct.apply(p);
33893                         if(!c.locked){
33894                             cb+= markup;
33895                         }else{
33896                             lcb+= markup;
33897                         }
33898                     }
33899                     var alt = [];
33900                     if(stripe && ((rowIndex+1) % 2 == 0)){
33901                         alt.push("x-grid-row-alt")
33902                     }
33903                     if(r.dirty){
33904                         alt.push(  " x-grid-dirty-row");
33905                     }
33906                     rp.cells = lcb;
33907                     if(this.getRowClass){
33908                         alt.push(this.getRowClass(r, rowIndex));
33909                     }
33910                     if (hasListener) {
33911                         rowcfg = {
33912                              
33913                             record: r,
33914                             rowIndex : rowIndex,
33915                             rowClass : ''
33916                         }
33917                         this.grid.fireEvent('rowclass', this, rowcfg);
33918                         alt.push(rowcfg.rowClass);
33919                     }
33920                     rp.alt = alt.join(" ");
33921                     lbuf+= rt.apply(rp);
33922                     rp.cells = cb;
33923                     buf+=  rt.apply(rp);
33924                 }
33925                 return [lbuf, buf];
33926             } :
33927             function(cs, rs, ds, startRow, colCount, stripe){
33928                 var ts = this.templates, ct = ts.cell, rt = ts.row;
33929                 // buffers
33930                 var buf = [], lbuf = [], cb, lcb, c, p = {}, rp = {}, r, rowIndex;
33931                 var hasListener = this.grid.hasListener('rowclass');
33932                 var rowcfg = {};
33933                 for(var j = 0, len = rs.length; j < len; j++){
33934                     r = rs[j]; cb = []; lcb = []; rowIndex = (j+startRow);
33935                     for(var i = 0; i < colCount; i++){
33936                         c = cs[i];
33937                         p.cellId = "x-grid-cell-" + rowIndex + "-" + i;
33938                         p.id = c.id;
33939                         p.css = p.attr = "";
33940                         p.value = c.renderer(r.data[c.name], p, r, rowIndex, i, ds);
33941                         if(p.value == undefined || p.value === "") p.value = "&#160;";
33942                         if(r.dirty && typeof r.modified[c.name] !== 'undefined'){
33943                             p.css += p.css ? ' x-grid-dirty-cell' : 'x-grid-dirty-cell';
33944                         }
33945                         var markup = ct.apply(p);
33946                         if(!c.locked){
33947                             cb[cb.length] = markup;
33948                         }else{
33949                             lcb[lcb.length] = markup;
33950                         }
33951                     }
33952                     var alt = [];
33953                     if(stripe && ((rowIndex+1) % 2 == 0)){
33954                         alt.push( "x-grid-row-alt");
33955                     }
33956                     if(r.dirty){
33957                         alt.push(" x-grid-dirty-row");
33958                     }
33959                     rp.cells = lcb;
33960                     if(this.getRowClass){
33961                         alt.push( this.getRowClass(r, rowIndex));
33962                     }
33963                     if (hasListener) {
33964                         rowcfg = {
33965                              
33966                             record: r,
33967                             rowIndex : rowIndex,
33968                             rowClass : ''
33969                         }
33970                         this.grid.fireEvent('rowclass', this, rowcfg);
33971                         alt.push(rowcfg.rowClass);
33972                     }
33973                     rp.alt = alt.join(" ");
33974                     rp.cells = lcb.join("");
33975                     lbuf[lbuf.length] = rt.apply(rp);
33976                     rp.cells = cb.join("");
33977                     buf[buf.length] =  rt.apply(rp);
33978                 }
33979                 return [lbuf.join(""), buf.join("")];
33980             },
33981
33982     renderBody : function(){
33983         var markup = this.renderRows();
33984         var bt = this.templates.body;
33985         return [bt.apply({rows: markup[0]}), bt.apply({rows: markup[1]})];
33986     },
33987
33988     /**
33989      * Refreshes the grid
33990      * @param {Boolean} headersToo
33991      */
33992     refresh : function(headersToo){
33993         this.fireEvent("beforerefresh", this);
33994         this.grid.stopEditing();
33995         var result = this.renderBody();
33996         this.lockedBody.update(result[0]);
33997         this.mainBody.update(result[1]);
33998         if(headersToo === true){
33999             this.updateHeaders();
34000             this.updateColumns();
34001             this.updateSplitters();
34002             this.updateHeaderSortState();
34003         }
34004         this.syncRowHeights();
34005         this.layout();
34006         this.fireEvent("refresh", this);
34007     },
34008
34009     handleColumnMove : function(cm, oldIndex, newIndex){
34010         this.indexMap = null;
34011         var s = this.getScrollState();
34012         this.refresh(true);
34013         this.restoreScroll(s);
34014         this.afterMove(newIndex);
34015     },
34016
34017     afterMove : function(colIndex){
34018         if(this.enableMoveAnim && Roo.enableFx){
34019             this.fly(this.getHeaderCell(colIndex).firstChild).highlight(this.hlColor);
34020         }
34021         // if multisort - fix sortOrder, and reload..
34022         if (this.grid.dataSource.multiSort) {
34023             // the we can call sort again..
34024             var dm = this.grid.dataSource;
34025             var cm = this.grid.colModel;
34026             var so = [];
34027             for(var i = 0; i < cm.config.length; i++ ) {
34028                 
34029                 if ((typeof(dm.sortToggle[cm.config[i].dataIndex]) == 'undefined')) {
34030                     continue; // dont' bother, it's not in sort list or being set.
34031                 }
34032                 
34033                 so.push(cm.config[i].dataIndex);
34034             };
34035             dm.sortOrder = so;
34036             dm.load(dm.lastOptions);
34037             
34038             
34039         }
34040         
34041     },
34042
34043     updateCell : function(dm, rowIndex, dataIndex){
34044         var colIndex = this.getColumnIndexByDataIndex(dataIndex);
34045         if(typeof colIndex == "undefined"){ // not present in grid
34046             return;
34047         }
34048         var cm = this.grid.colModel;
34049         var cell = this.getCell(rowIndex, colIndex);
34050         var cellText = this.getCellText(rowIndex, colIndex);
34051
34052         var p = {
34053             cellId : "x-grid-cell-" + rowIndex + "-" + colIndex,
34054             id : cm.getColumnId(colIndex),
34055             css: colIndex == cm.getColumnCount()-1 ? "x-grid-col-last" : ""
34056         };
34057         var renderer = cm.getRenderer(colIndex);
34058         var val = renderer(dm.getValueAt(rowIndex, dataIndex), p, rowIndex, colIndex, dm);
34059         if(typeof val == "undefined" || val === "") val = "&#160;";
34060         cellText.innerHTML = val;
34061         cell.className = this.cellClass + " " + this.idToCssName(p.cellId) + " " + p.css;
34062         this.syncRowHeights(rowIndex, rowIndex);
34063     },
34064
34065     calcColumnWidth : function(colIndex, maxRowsToMeasure){
34066         var maxWidth = 0;
34067         if(this.grid.autoSizeHeaders){
34068             var h = this.getHeaderCellMeasure(colIndex);
34069             maxWidth = Math.max(maxWidth, h.scrollWidth);
34070         }
34071         var tb, index;
34072         if(this.cm.isLocked(colIndex)){
34073             tb = this.getLockedTable();
34074             index = colIndex;
34075         }else{
34076             tb = this.getBodyTable();
34077             index = colIndex - this.cm.getLockedCount();
34078         }
34079         if(tb && tb.rows){
34080             var rows = tb.rows;
34081             var stopIndex = Math.min(maxRowsToMeasure || rows.length, rows.length);
34082             for(var i = 0; i < stopIndex; i++){
34083                 var cell = rows[i].childNodes[index].firstChild;
34084                 maxWidth = Math.max(maxWidth, cell.scrollWidth);
34085             }
34086         }
34087         return maxWidth + /*margin for error in IE*/ 5;
34088     },
34089     /**
34090      * Autofit a column to its content.
34091      * @param {Number} colIndex
34092      * @param {Boolean} forceMinSize true to force the column to go smaller if possible
34093      */
34094      autoSizeColumn : function(colIndex, forceMinSize, suppressEvent){
34095          if(this.cm.isHidden(colIndex)){
34096              return; // can't calc a hidden column
34097          }
34098         if(forceMinSize){
34099             var cid = this.cm.getColumnId(colIndex);
34100             this.css.updateRule(this.colSelector +this.idToCssName( cid), "width", this.grid.minColumnWidth + "px");
34101            if(this.grid.autoSizeHeaders){
34102                this.css.updateRule(this.hdSelector + this.idToCssName(cid), "width", this.grid.minColumnWidth + "px");
34103            }
34104         }
34105         var newWidth = this.calcColumnWidth(colIndex);
34106         this.cm.setColumnWidth(colIndex,
34107             Math.max(this.grid.minColumnWidth, newWidth), suppressEvent);
34108         if(!suppressEvent){
34109             this.grid.fireEvent("columnresize", colIndex, newWidth);
34110         }
34111     },
34112
34113     /**
34114      * Autofits all columns to their content and then expands to fit any extra space in the grid
34115      */
34116      autoSizeColumns : function(){
34117         var cm = this.grid.colModel;
34118         var colCount = cm.getColumnCount();
34119         for(var i = 0; i < colCount; i++){
34120             this.autoSizeColumn(i, true, true);
34121         }
34122         if(cm.getTotalWidth() < this.scroller.dom.clientWidth){
34123             this.fitColumns();
34124         }else{
34125             this.updateColumns();
34126             this.layout();
34127         }
34128     },
34129
34130     /**
34131      * Autofits all columns to the grid's width proportionate with their current size
34132      * @param {Boolean} reserveScrollSpace Reserve space for a scrollbar
34133      */
34134     fitColumns : function(reserveScrollSpace){
34135         var cm = this.grid.colModel;
34136         var colCount = cm.getColumnCount();
34137         var cols = [];
34138         var width = 0;
34139         var i, w;
34140         for (i = 0; i < colCount; i++){
34141             if(!cm.isHidden(i) && !cm.isFixed(i)){
34142                 w = cm.getColumnWidth(i);
34143                 cols.push(i);
34144                 cols.push(w);
34145                 width += w;
34146             }
34147         }
34148         var avail = Math.min(this.scroller.dom.clientWidth, this.el.getWidth());
34149         if(reserveScrollSpace){
34150             avail -= 17;
34151         }
34152         var frac = (avail - cm.getTotalWidth())/width;
34153         while (cols.length){
34154             w = cols.pop();
34155             i = cols.pop();
34156             cm.setColumnWidth(i, Math.floor(w + w*frac), true);
34157         }
34158         this.updateColumns();
34159         this.layout();
34160     },
34161
34162     onRowSelect : function(rowIndex){
34163         var row = this.getRowComposite(rowIndex);
34164         row.addClass("x-grid-row-selected");
34165     },
34166
34167     onRowDeselect : function(rowIndex){
34168         var row = this.getRowComposite(rowIndex);
34169         row.removeClass("x-grid-row-selected");
34170     },
34171
34172     onCellSelect : function(row, col){
34173         var cell = this.getCell(row, col);
34174         if(cell){
34175             Roo.fly(cell).addClass("x-grid-cell-selected");
34176         }
34177     },
34178
34179     onCellDeselect : function(row, col){
34180         var cell = this.getCell(row, col);
34181         if(cell){
34182             Roo.fly(cell).removeClass("x-grid-cell-selected");
34183         }
34184     },
34185
34186     updateHeaderSortState : function(){
34187         
34188         // sort state can be single { field: xxx, direction : yyy}
34189         // or   { xxx=>ASC , yyy : DESC ..... }
34190         
34191         var mstate = {};
34192         if (!this.ds.multiSort) { 
34193             var state = this.ds.getSortState();
34194             if(!state){
34195                 return;
34196             }
34197             mstate[state.field] = state.direction;
34198             // FIXME... - this is not used here.. but might be elsewhere..
34199             this.sortState = state;
34200             
34201         } else {
34202             mstate = this.ds.sortToggle;
34203         }
34204         //remove existing sort classes..
34205         
34206         var sc = this.sortClasses;
34207         var hds = this.el.select(this.headerSelector).removeClass(sc);
34208         
34209         for(var f in mstate) {
34210         
34211             var sortColumn = this.cm.findColumnIndex(f);
34212             
34213             if(sortColumn != -1){
34214                 var sortDir = mstate[f];        
34215                 hds.item(sortColumn).addClass(sc[sortDir == "DESC" ? 1 : 0]);
34216             }
34217         }
34218         
34219          
34220         
34221     },
34222
34223
34224     handleHeaderClick : function(g, index){
34225         if(this.headersDisabled){
34226             return;
34227         }
34228         var dm = g.dataSource, cm = g.colModel;
34229         if(!cm.isSortable(index)){
34230             return;
34231         }
34232         g.stopEditing();
34233         
34234         if (dm.multiSort) {
34235             // update the sortOrder
34236             var so = [];
34237             for(var i = 0; i < cm.config.length; i++ ) {
34238                 
34239                 if ((typeof(dm.sortToggle[cm.config[i].dataIndex]) == 'undefined') && (index != i)) {
34240                     continue; // dont' bother, it's not in sort list or being set.
34241                 }
34242                 
34243                 so.push(cm.config[i].dataIndex);
34244             };
34245             dm.sortOrder = so;
34246         }
34247         
34248         
34249         dm.sort(cm.getDataIndex(index));
34250     },
34251
34252
34253     destroy : function(){
34254         if(this.colMenu){
34255             this.colMenu.removeAll();
34256             Roo.menu.MenuMgr.unregister(this.colMenu);
34257             this.colMenu.getEl().remove();
34258             delete this.colMenu;
34259         }
34260         if(this.hmenu){
34261             this.hmenu.removeAll();
34262             Roo.menu.MenuMgr.unregister(this.hmenu);
34263             this.hmenu.getEl().remove();
34264             delete this.hmenu;
34265         }
34266         if(this.grid.enableColumnMove){
34267             var dds = Roo.dd.DDM.ids['gridHeader' + this.grid.getGridEl().id];
34268             if(dds){
34269                 for(var dd in dds){
34270                     if(!dds[dd].config.isTarget && dds[dd].dragElId){
34271                         var elid = dds[dd].dragElId;
34272                         dds[dd].unreg();
34273                         Roo.get(elid).remove();
34274                     } else if(dds[dd].config.isTarget){
34275                         dds[dd].proxyTop.remove();
34276                         dds[dd].proxyBottom.remove();
34277                         dds[dd].unreg();
34278                     }
34279                     if(Roo.dd.DDM.locationCache[dd]){
34280                         delete Roo.dd.DDM.locationCache[dd];
34281                     }
34282                 }
34283                 delete Roo.dd.DDM.ids['gridHeader' + this.grid.getGridEl().id];
34284             }
34285         }
34286         Roo.util.CSS.removeStyleSheet(this.idToCssName(this.grid.id) + '-cssrules');
34287         this.bind(null, null);
34288         Roo.EventManager.removeResizeListener(this.onWindowResize, this);
34289     },
34290
34291     handleLockChange : function(){
34292         this.refresh(true);
34293     },
34294
34295     onDenyColumnLock : function(){
34296
34297     },
34298
34299     onDenyColumnHide : function(){
34300
34301     },
34302
34303     handleHdMenuClick : function(item){
34304         var index = this.hdCtxIndex;
34305         var cm = this.cm, ds = this.ds;
34306         switch(item.id){
34307             case "asc":
34308                 ds.sort(cm.getDataIndex(index), "ASC");
34309                 break;
34310             case "desc":
34311                 ds.sort(cm.getDataIndex(index), "DESC");
34312                 break;
34313             case "lock":
34314                 var lc = cm.getLockedCount();
34315                 if(cm.getColumnCount(true) <= lc+1){
34316                     this.onDenyColumnLock();
34317                     return;
34318                 }
34319                 if(lc != index){
34320                     cm.setLocked(index, true, true);
34321                     cm.moveColumn(index, lc);
34322                     this.grid.fireEvent("columnmove", index, lc);
34323                 }else{
34324                     cm.setLocked(index, true);
34325                 }
34326             break;
34327             case "unlock":
34328                 var lc = cm.getLockedCount();
34329                 if((lc-1) != index){
34330                     cm.setLocked(index, false, true);
34331                     cm.moveColumn(index, lc-1);
34332                     this.grid.fireEvent("columnmove", index, lc-1);
34333                 }else{
34334                     cm.setLocked(index, false);
34335                 }
34336             break;
34337             default:
34338                 index = cm.getIndexById(item.id.substr(4));
34339                 if(index != -1){
34340                     if(item.checked && cm.getColumnCount(true) <= 1){
34341                         this.onDenyColumnHide();
34342                         return false;
34343                     }
34344                     cm.setHidden(index, item.checked);
34345                 }
34346         }
34347         return true;
34348     },
34349
34350     beforeColMenuShow : function(){
34351         var cm = this.cm,  colCount = cm.getColumnCount();
34352         this.colMenu.removeAll();
34353         for(var i = 0; i < colCount; i++){
34354             this.colMenu.add(new Roo.menu.CheckItem({
34355                 id: "col-"+cm.getColumnId(i),
34356                 text: cm.getColumnHeader(i),
34357                 checked: !cm.isHidden(i),
34358                 hideOnClick:false
34359             }));
34360         }
34361     },
34362
34363     handleHdCtx : function(g, index, e){
34364         e.stopEvent();
34365         var hd = this.getHeaderCell(index);
34366         this.hdCtxIndex = index;
34367         var ms = this.hmenu.items, cm = this.cm;
34368         ms.get("asc").setDisabled(!cm.isSortable(index));
34369         ms.get("desc").setDisabled(!cm.isSortable(index));
34370         if(this.grid.enableColLock !== false){
34371             ms.get("lock").setDisabled(cm.isLocked(index));
34372             ms.get("unlock").setDisabled(!cm.isLocked(index));
34373         }
34374         this.hmenu.show(hd, "tl-bl");
34375     },
34376
34377     handleHdOver : function(e){
34378         var hd = this.findHeaderCell(e.getTarget());
34379         if(hd && !this.headersDisabled){
34380             if(this.grid.colModel.isSortable(this.getCellIndex(hd))){
34381                this.fly(hd).addClass("x-grid-hd-over");
34382             }
34383         }
34384     },
34385
34386     handleHdOut : function(e){
34387         var hd = this.findHeaderCell(e.getTarget());
34388         if(hd){
34389             this.fly(hd).removeClass("x-grid-hd-over");
34390         }
34391     },
34392
34393     handleSplitDblClick : function(e, t){
34394         var i = this.getCellIndex(t);
34395         if(this.grid.enableColumnResize !== false && this.cm.isResizable(i) && !this.cm.isFixed(i)){
34396             this.autoSizeColumn(i, true);
34397             this.layout();
34398         }
34399     },
34400
34401     render : function(){
34402
34403         var cm = this.cm;
34404         var colCount = cm.getColumnCount();
34405
34406         if(this.grid.monitorWindowResize === true){
34407             Roo.EventManager.onWindowResize(this.onWindowResize, this, true);
34408         }
34409         var header = this.renderHeaders();
34410         var body = this.templates.body.apply({rows:""});
34411         var html = this.templates.master.apply({
34412             lockedBody: body,
34413             body: body,
34414             lockedHeader: header[0],
34415             header: header[1]
34416         });
34417
34418         //this.updateColumns();
34419
34420         this.grid.getGridEl().dom.innerHTML = html;
34421
34422         this.initElements();
34423         
34424         // a kludge to fix the random scolling effect in webkit
34425         this.el.on("scroll", function() {
34426             this.el.dom.scrollTop=0; // hopefully not recursive..
34427         },this);
34428
34429         this.scroller.on("scroll", this.handleScroll, this);
34430         this.lockedBody.on("mousewheel", this.handleWheel, this);
34431         this.mainBody.on("mousewheel", this.handleWheel, this);
34432
34433         this.mainHd.on("mouseover", this.handleHdOver, this);
34434         this.mainHd.on("mouseout", this.handleHdOut, this);
34435         this.mainHd.on("dblclick", this.handleSplitDblClick, this,
34436                 {delegate: "."+this.splitClass});
34437
34438         this.lockedHd.on("mouseover", this.handleHdOver, this);
34439         this.lockedHd.on("mouseout", this.handleHdOut, this);
34440         this.lockedHd.on("dblclick", this.handleSplitDblClick, this,
34441                 {delegate: "."+this.splitClass});
34442
34443         if(this.grid.enableColumnResize !== false && Roo.grid.SplitDragZone){
34444             new Roo.grid.SplitDragZone(this.grid, this.lockedHd.dom, this.mainHd.dom);
34445         }
34446
34447         this.updateSplitters();
34448
34449         if(this.grid.enableColumnMove && Roo.grid.HeaderDragZone){
34450             new Roo.grid.HeaderDragZone(this.grid, this.lockedHd.dom, this.mainHd.dom);
34451             new Roo.grid.HeaderDropZone(this.grid, this.lockedHd.dom, this.mainHd.dom);
34452         }
34453
34454         if(this.grid.enableCtxMenu !== false && Roo.menu.Menu){
34455             this.hmenu = new Roo.menu.Menu({id: this.grid.id + "-hctx"});
34456             this.hmenu.add(
34457                 {id:"asc", text: this.sortAscText, cls: "xg-hmenu-sort-asc"},
34458                 {id:"desc", text: this.sortDescText, cls: "xg-hmenu-sort-desc"}
34459             );
34460             if(this.grid.enableColLock !== false){
34461                 this.hmenu.add('-',
34462                     {id:"lock", text: this.lockText, cls: "xg-hmenu-lock"},
34463                     {id:"unlock", text: this.unlockText, cls: "xg-hmenu-unlock"}
34464                 );
34465             }
34466             if(this.grid.enableColumnHide !== false){
34467
34468                 this.colMenu = new Roo.menu.Menu({id:this.grid.id + "-hcols-menu"});
34469                 this.colMenu.on("beforeshow", this.beforeColMenuShow, this);
34470                 this.colMenu.on("itemclick", this.handleHdMenuClick, this);
34471
34472                 this.hmenu.add('-',
34473                     {id:"columns", text: this.columnsText, menu: this.colMenu}
34474                 );
34475             }
34476             this.hmenu.on("itemclick", this.handleHdMenuClick, this);
34477
34478             this.grid.on("headercontextmenu", this.handleHdCtx, this);
34479         }
34480
34481         if((this.grid.enableDragDrop || this.grid.enableDrag) && Roo.grid.GridDragZone){
34482             this.dd = new Roo.grid.GridDragZone(this.grid, {
34483                 ddGroup : this.grid.ddGroup || 'GridDD'
34484             });
34485         }
34486
34487         /*
34488         for(var i = 0; i < colCount; i++){
34489             if(cm.isHidden(i)){
34490                 this.hideColumn(i);
34491             }
34492             if(cm.config[i].align){
34493                 this.css.updateRule(this.colSelector + i, "textAlign", cm.config[i].align);
34494                 this.css.updateRule(this.hdSelector + i, "textAlign", cm.config[i].align);
34495             }
34496         }*/
34497         
34498         this.updateHeaderSortState();
34499
34500         this.beforeInitialResize();
34501         this.layout(true);
34502
34503         // two part rendering gives faster view to the user
34504         this.renderPhase2.defer(1, this);
34505     },
34506
34507     renderPhase2 : function(){
34508         // render the rows now
34509         this.refresh();
34510         if(this.grid.autoSizeColumns){
34511             this.autoSizeColumns();
34512         }
34513     },
34514
34515     beforeInitialResize : function(){
34516
34517     },
34518
34519     onColumnSplitterMoved : function(i, w){
34520         this.userResized = true;
34521         var cm = this.grid.colModel;
34522         cm.setColumnWidth(i, w, true);
34523         var cid = cm.getColumnId(i);
34524         this.css.updateRule(this.colSelector + this.idToCssName(cid), "width", (w-this.borderWidth) + "px");
34525         this.css.updateRule(this.hdSelector + this.idToCssName(cid), "width", (w-this.borderWidth) + "px");
34526         this.updateSplitters();
34527         this.layout();
34528         this.grid.fireEvent("columnresize", i, w);
34529     },
34530
34531     syncRowHeights : function(startIndex, endIndex){
34532         if(this.grid.enableRowHeightSync === true && this.cm.getLockedCount() > 0){
34533             startIndex = startIndex || 0;
34534             var mrows = this.getBodyTable().rows;
34535             var lrows = this.getLockedTable().rows;
34536             var len = mrows.length-1;
34537             endIndex = Math.min(endIndex || len, len);
34538             for(var i = startIndex; i <= endIndex; i++){
34539                 var m = mrows[i], l = lrows[i];
34540                 var h = Math.max(m.offsetHeight, l.offsetHeight);
34541                 m.style.height = l.style.height = h + "px";
34542             }
34543         }
34544     },
34545
34546     layout : function(initialRender, is2ndPass){
34547         var g = this.grid;
34548         var auto = g.autoHeight;
34549         var scrollOffset = 16;
34550         var c = g.getGridEl(), cm = this.cm,
34551                 expandCol = g.autoExpandColumn,
34552                 gv = this;
34553         //c.beginMeasure();
34554
34555         if(!c.dom.offsetWidth){ // display:none?
34556             if(initialRender){
34557                 this.lockedWrap.show();
34558                 this.mainWrap.show();
34559             }
34560             return;
34561         }
34562
34563         var hasLock = this.cm.isLocked(0);
34564
34565         var tbh = this.headerPanel.getHeight();
34566         var bbh = this.footerPanel.getHeight();
34567
34568         if(auto){
34569             var ch = this.getBodyTable().offsetHeight + tbh + bbh + this.mainHd.getHeight();
34570             var newHeight = ch + c.getBorderWidth("tb");
34571             if(g.maxHeight){
34572                 newHeight = Math.min(g.maxHeight, newHeight);
34573             }
34574             c.setHeight(newHeight);
34575         }
34576
34577         if(g.autoWidth){
34578             c.setWidth(cm.getTotalWidth()+c.getBorderWidth('lr'));
34579         }
34580
34581         var s = this.scroller;
34582
34583         var csize = c.getSize(true);
34584
34585         this.el.setSize(csize.width, csize.height);
34586
34587         this.headerPanel.setWidth(csize.width);
34588         this.footerPanel.setWidth(csize.width);
34589
34590         var hdHeight = this.mainHd.getHeight();
34591         var vw = csize.width;
34592         var vh = csize.height - (tbh + bbh);
34593
34594         s.setSize(vw, vh);
34595
34596         var bt = this.getBodyTable();
34597         var ltWidth = hasLock ?
34598                       Math.max(this.getLockedTable().offsetWidth, this.lockedHd.dom.firstChild.offsetWidth) : 0;
34599
34600         var scrollHeight = bt.offsetHeight;
34601         var scrollWidth = ltWidth + bt.offsetWidth;
34602         var vscroll = false, hscroll = false;
34603
34604         this.scrollSizer.setSize(scrollWidth, scrollHeight+hdHeight);
34605
34606         var lw = this.lockedWrap, mw = this.mainWrap;
34607         var lb = this.lockedBody, mb = this.mainBody;
34608
34609         setTimeout(function(){
34610             var t = s.dom.offsetTop;
34611             var w = s.dom.clientWidth,
34612                 h = s.dom.clientHeight;
34613
34614             lw.setTop(t);
34615             lw.setSize(ltWidth, h);
34616
34617             mw.setLeftTop(ltWidth, t);
34618             mw.setSize(w-ltWidth, h);
34619
34620             lb.setHeight(h-hdHeight);
34621             mb.setHeight(h-hdHeight);
34622
34623             if(is2ndPass !== true && !gv.userResized && expandCol){
34624                 // high speed resize without full column calculation
34625                 
34626                 var ci = cm.getIndexById(expandCol);
34627                 if (ci < 0) {
34628                     ci = cm.findColumnIndex(expandCol);
34629                 }
34630                 ci = Math.max(0, ci); // make sure it's got at least the first col.
34631                 var expandId = cm.getColumnId(ci);
34632                 var  tw = cm.getTotalWidth(false);
34633                 var currentWidth = cm.getColumnWidth(ci);
34634                 var cw = Math.min(Math.max(((w-tw)+currentWidth-2)-/*scrollbar*/(w <= s.dom.offsetWidth ? 0 : 18), g.autoExpandMin), g.autoExpandMax);
34635                 if(currentWidth != cw){
34636                     cm.setColumnWidth(ci, cw, true);
34637                     gv.css.updateRule(gv.colSelector+gv.idToCssName(expandId), "width", (cw - gv.borderWidth) + "px");
34638                     gv.css.updateRule(gv.hdSelector+gv.idToCssName(expandId), "width", (cw - gv.borderWidth) + "px");
34639                     gv.updateSplitters();
34640                     gv.layout(false, true);
34641                 }
34642             }
34643
34644             if(initialRender){
34645                 lw.show();
34646                 mw.show();
34647             }
34648             //c.endMeasure();
34649         }, 10);
34650     },
34651
34652     onWindowResize : function(){
34653         if(!this.grid.monitorWindowResize || this.grid.autoHeight){
34654             return;
34655         }
34656         this.layout();
34657     },
34658
34659     appendFooter : function(parentEl){
34660         return null;
34661     },
34662
34663     sortAscText : "Sort Ascending",
34664     sortDescText : "Sort Descending",
34665     lockText : "Lock Column",
34666     unlockText : "Unlock Column",
34667     columnsText : "Columns"
34668 });
34669
34670
34671 Roo.grid.GridView.ColumnDragZone = function(grid, hd){
34672     Roo.grid.GridView.ColumnDragZone.superclass.constructor.call(this, grid, hd, null);
34673     this.proxy.el.addClass('x-grid3-col-dd');
34674 };
34675
34676 Roo.extend(Roo.grid.GridView.ColumnDragZone, Roo.grid.HeaderDragZone, {
34677     handleMouseDown : function(e){
34678
34679     },
34680
34681     callHandleMouseDown : function(e){
34682         Roo.grid.GridView.ColumnDragZone.superclass.handleMouseDown.call(this, e);
34683     }
34684 });
34685 /*
34686  * Based on:
34687  * Ext JS Library 1.1.1
34688  * Copyright(c) 2006-2007, Ext JS, LLC.
34689  *
34690  * Originally Released Under LGPL - original licence link has changed is not relivant.
34691  *
34692  * Fork - LGPL
34693  * <script type="text/javascript">
34694  */
34695  
34696 // private
34697 // This is a support class used internally by the Grid components
34698 Roo.grid.SplitDragZone = function(grid, hd, hd2){
34699     this.grid = grid;
34700     this.view = grid.getView();
34701     this.proxy = this.view.resizeProxy;
34702     Roo.grid.SplitDragZone.superclass.constructor.call(this, hd,
34703         "gridSplitters" + this.grid.getGridEl().id, {
34704         dragElId : Roo.id(this.proxy.dom), resizeFrame:false
34705     });
34706     this.setHandleElId(Roo.id(hd));
34707     this.setOuterHandleElId(Roo.id(hd2));
34708     this.scroll = false;
34709 };
34710 Roo.extend(Roo.grid.SplitDragZone, Roo.dd.DDProxy, {
34711     fly: Roo.Element.fly,
34712
34713     b4StartDrag : function(x, y){
34714         this.view.headersDisabled = true;
34715         this.proxy.setHeight(this.view.mainWrap.getHeight());
34716         var w = this.cm.getColumnWidth(this.cellIndex);
34717         var minw = Math.max(w-this.grid.minColumnWidth, 0);
34718         this.resetConstraints();
34719         this.setXConstraint(minw, 1000);
34720         this.setYConstraint(0, 0);
34721         this.minX = x - minw;
34722         this.maxX = x + 1000;
34723         this.startPos = x;
34724         Roo.dd.DDProxy.prototype.b4StartDrag.call(this, x, y);
34725     },
34726
34727
34728     handleMouseDown : function(e){
34729         ev = Roo.EventObject.setEvent(e);
34730         var t = this.fly(ev.getTarget());
34731         if(t.hasClass("x-grid-split")){
34732             this.cellIndex = this.view.getCellIndex(t.dom);
34733             this.split = t.dom;
34734             this.cm = this.grid.colModel;
34735             if(this.cm.isResizable(this.cellIndex) && !this.cm.isFixed(this.cellIndex)){
34736                 Roo.grid.SplitDragZone.superclass.handleMouseDown.apply(this, arguments);
34737             }
34738         }
34739     },
34740
34741     endDrag : function(e){
34742         this.view.headersDisabled = false;
34743         var endX = Math.max(this.minX, Roo.lib.Event.getPageX(e));
34744         var diff = endX - this.startPos;
34745         this.view.onColumnSplitterMoved(this.cellIndex, this.cm.getColumnWidth(this.cellIndex)+diff);
34746     },
34747
34748     autoOffset : function(){
34749         this.setDelta(0,0);
34750     }
34751 });/*
34752  * Based on:
34753  * Ext JS Library 1.1.1
34754  * Copyright(c) 2006-2007, Ext JS, LLC.
34755  *
34756  * Originally Released Under LGPL - original licence link has changed is not relivant.
34757  *
34758  * Fork - LGPL
34759  * <script type="text/javascript">
34760  */
34761  
34762 // private
34763 // This is a support class used internally by the Grid components
34764 Roo.grid.GridDragZone = function(grid, config){
34765     this.view = grid.getView();
34766     Roo.grid.GridDragZone.superclass.constructor.call(this, this.view.mainBody.dom, config);
34767     if(this.view.lockedBody){
34768         this.setHandleElId(Roo.id(this.view.mainBody.dom));
34769         this.setOuterHandleElId(Roo.id(this.view.lockedBody.dom));
34770     }
34771     this.scroll = false;
34772     this.grid = grid;
34773     this.ddel = document.createElement('div');
34774     this.ddel.className = 'x-grid-dd-wrap';
34775 };
34776
34777 Roo.extend(Roo.grid.GridDragZone, Roo.dd.DragZone, {
34778     ddGroup : "GridDD",
34779
34780     getDragData : function(e){
34781         var t = Roo.lib.Event.getTarget(e);
34782         var rowIndex = this.view.findRowIndex(t);
34783         if(rowIndex !== false){
34784             var sm = this.grid.selModel;
34785             //if(!sm.isSelected(rowIndex) || e.hasModifier()){
34786               //  sm.mouseDown(e, t);
34787             //}
34788             if (e.hasModifier()){
34789                 sm.handleMouseDown(e, t); // non modifier buttons are handled by row select.
34790             }
34791             return {grid: this.grid, ddel: this.ddel, rowIndex: rowIndex, selections:sm.getSelections()};
34792         }
34793         return false;
34794     },
34795
34796     onInitDrag : function(e){
34797         var data = this.dragData;
34798         this.ddel.innerHTML = this.grid.getDragDropText();
34799         this.proxy.update(this.ddel);
34800         // fire start drag?
34801     },
34802
34803     afterRepair : function(){
34804         this.dragging = false;
34805     },
34806
34807     getRepairXY : function(e, data){
34808         return false;
34809     },
34810
34811     onEndDrag : function(data, e){
34812         // fire end drag?
34813     },
34814
34815     onValidDrop : function(dd, e, id){
34816         // fire drag drop?
34817         this.hideProxy();
34818     },
34819
34820     beforeInvalidDrop : function(e, id){
34821
34822     }
34823 });/*
34824  * Based on:
34825  * Ext JS Library 1.1.1
34826  * Copyright(c) 2006-2007, Ext JS, LLC.
34827  *
34828  * Originally Released Under LGPL - original licence link has changed is not relivant.
34829  *
34830  * Fork - LGPL
34831  * <script type="text/javascript">
34832  */
34833  
34834
34835 /**
34836  * @class Roo.grid.ColumnModel
34837  * @extends Roo.util.Observable
34838  * This is the default implementation of a ColumnModel used by the Grid. It defines
34839  * the columns in the grid.
34840  * <br>Usage:<br>
34841  <pre><code>
34842  var colModel = new Roo.grid.ColumnModel([
34843         {header: "Ticker", width: 60, sortable: true, locked: true},
34844         {header: "Company Name", width: 150, sortable: true},
34845         {header: "Market Cap.", width: 100, sortable: true},
34846         {header: "$ Sales", width: 100, sortable: true, renderer: money},
34847         {header: "Employees", width: 100, sortable: true, resizable: false}
34848  ]);
34849  </code></pre>
34850  * <p>
34851  
34852  * The config options listed for this class are options which may appear in each
34853  * individual column definition.
34854  * <br/>RooJS Fix - column id's are not sequential but use Roo.id() - fixes bugs with layouts.
34855  * @constructor
34856  * @param {Object} config An Array of column config objects. See this class's
34857  * config objects for details.
34858 */
34859 Roo.grid.ColumnModel = function(config){
34860         /**
34861      * The config passed into the constructor
34862      */
34863     this.config = config;
34864     this.lookup = {};
34865
34866     // if no id, create one
34867     // if the column does not have a dataIndex mapping,
34868     // map it to the order it is in the config
34869     for(var i = 0, len = config.length; i < len; i++){
34870         var c = config[i];
34871         if(typeof c.dataIndex == "undefined"){
34872             c.dataIndex = i;
34873         }
34874         if(typeof c.renderer == "string"){
34875             c.renderer = Roo.util.Format[c.renderer];
34876         }
34877         if(typeof c.id == "undefined"){
34878             c.id = Roo.id();
34879         }
34880         if(c.editor && c.editor.xtype){
34881             c.editor  = Roo.factory(c.editor, Roo.grid);
34882         }
34883         if(c.editor && c.editor.isFormField){
34884             c.editor = new Roo.grid.GridEditor(c.editor);
34885         }
34886         this.lookup[c.id] = c;
34887     }
34888
34889     /**
34890      * The width of columns which have no width specified (defaults to 100)
34891      * @type Number
34892      */
34893     this.defaultWidth = 100;
34894
34895     /**
34896      * Default sortable of columns which have no sortable specified (defaults to false)
34897      * @type Boolean
34898      */
34899     this.defaultSortable = false;
34900
34901     this.addEvents({
34902         /**
34903              * @event widthchange
34904              * Fires when the width of a column changes.
34905              * @param {ColumnModel} this
34906              * @param {Number} columnIndex The column index
34907              * @param {Number} newWidth The new width
34908              */
34909             "widthchange": true,
34910         /**
34911              * @event headerchange
34912              * Fires when the text of a header changes.
34913              * @param {ColumnModel} this
34914              * @param {Number} columnIndex The column index
34915              * @param {Number} newText The new header text
34916              */
34917             "headerchange": true,
34918         /**
34919              * @event hiddenchange
34920              * Fires when a column is hidden or "unhidden".
34921              * @param {ColumnModel} this
34922              * @param {Number} columnIndex The column index
34923              * @param {Boolean} hidden true if hidden, false otherwise
34924              */
34925             "hiddenchange": true,
34926             /**
34927          * @event columnmoved
34928          * Fires when a column is moved.
34929          * @param {ColumnModel} this
34930          * @param {Number} oldIndex
34931          * @param {Number} newIndex
34932          */
34933         "columnmoved" : true,
34934         /**
34935          * @event columlockchange
34936          * Fires when a column's locked state is changed
34937          * @param {ColumnModel} this
34938          * @param {Number} colIndex
34939          * @param {Boolean} locked true if locked
34940          */
34941         "columnlockchange" : true
34942     });
34943     Roo.grid.ColumnModel.superclass.constructor.call(this);
34944 };
34945 Roo.extend(Roo.grid.ColumnModel, Roo.util.Observable, {
34946     /**
34947      * @cfg {String} header The header text to display in the Grid view.
34948      */
34949     /**
34950      * @cfg {String} dataIndex (Optional) The name of the field in the grid's {@link Roo.data.Store}'s
34951      * {@link Roo.data.Record} definition from which to draw the column's value. If not
34952      * specified, the column's index is used as an index into the Record's data Array.
34953      */
34954     /**
34955      * @cfg {Number} width (Optional) The initial width in pixels of the column. Using this
34956      * instead of {@link Roo.grid.Grid#autoSizeColumns} is more efficient.
34957      */
34958     /**
34959      * @cfg {Boolean} sortable (Optional) True if sorting is to be allowed on this column.
34960      * Defaults to the value of the {@link #defaultSortable} property.
34961      * Whether local/remote sorting is used is specified in {@link Roo.data.Store#remoteSort}.
34962      */
34963     /**
34964      * @cfg {Boolean} locked (Optional) True to lock the column in place while scrolling the Grid.  Defaults to false.
34965      */
34966     /**
34967      * @cfg {Boolean} fixed (Optional) True if the column width cannot be changed.  Defaults to false.
34968      */
34969     /**
34970      * @cfg {Boolean} resizable (Optional) False to disable column resizing. Defaults to true.
34971      */
34972     /**
34973      * @cfg {Boolean} hidden (Optional) True to hide the column. Defaults to false.
34974      */
34975     /**
34976      * @cfg {Function} renderer (Optional) A function used to generate HTML markup for a cell
34977      * given the cell's data value. See {@link #setRenderer}. If not specified, the
34978      * default renderer uses the raw data value.
34979      */
34980        /**
34981      * @cfg {Roo.grid.GridEditor} editor (Optional) For grid editors - returns the grid editor 
34982      */
34983     /**
34984      * @cfg {String} align (Optional) Set the CSS text-align property of the column.  Defaults to undefined.
34985      */
34986
34987     /**
34988      * Returns the id of the column at the specified index.
34989      * @param {Number} index The column index
34990      * @return {String} the id
34991      */
34992     getColumnId : function(index){
34993         return this.config[index].id;
34994     },
34995
34996     /**
34997      * Returns the column for a specified id.
34998      * @param {String} id The column id
34999      * @return {Object} the column
35000      */
35001     getColumnById : function(id){
35002         return this.lookup[id];
35003     },
35004
35005     
35006     /**
35007      * Returns the column for a specified dataIndex.
35008      * @param {String} dataIndex The column dataIndex
35009      * @return {Object|Boolean} the column or false if not found
35010      */
35011     getColumnByDataIndex: function(dataIndex){
35012         var index = this.findColumnIndex(dataIndex);
35013         return index > -1 ? this.config[index] : false;
35014     },
35015     
35016     /**
35017      * Returns the index for a specified column id.
35018      * @param {String} id The column id
35019      * @return {Number} the index, or -1 if not found
35020      */
35021     getIndexById : function(id){
35022         for(var i = 0, len = this.config.length; i < len; i++){
35023             if(this.config[i].id == id){
35024                 return i;
35025             }
35026         }
35027         return -1;
35028     },
35029     
35030     /**
35031      * Returns the index for a specified column dataIndex.
35032      * @param {String} dataIndex The column dataIndex
35033      * @return {Number} the index, or -1 if not found
35034      */
35035     
35036     findColumnIndex : function(dataIndex){
35037         for(var i = 0, len = this.config.length; i < len; i++){
35038             if(this.config[i].dataIndex == dataIndex){
35039                 return i;
35040             }
35041         }
35042         return -1;
35043     },
35044     
35045     
35046     moveColumn : function(oldIndex, newIndex){
35047         var c = this.config[oldIndex];
35048         this.config.splice(oldIndex, 1);
35049         this.config.splice(newIndex, 0, c);
35050         this.dataMap = null;
35051         this.fireEvent("columnmoved", this, oldIndex, newIndex);
35052     },
35053
35054     isLocked : function(colIndex){
35055         return this.config[colIndex].locked === true;
35056     },
35057
35058     setLocked : function(colIndex, value, suppressEvent){
35059         if(this.isLocked(colIndex) == value){
35060             return;
35061         }
35062         this.config[colIndex].locked = value;
35063         if(!suppressEvent){
35064             this.fireEvent("columnlockchange", this, colIndex, value);
35065         }
35066     },
35067
35068     getTotalLockedWidth : function(){
35069         var totalWidth = 0;
35070         for(var i = 0; i < this.config.length; i++){
35071             if(this.isLocked(i) && !this.isHidden(i)){
35072                 this.totalWidth += this.getColumnWidth(i);
35073             }
35074         }
35075         return totalWidth;
35076     },
35077
35078     getLockedCount : function(){
35079         for(var i = 0, len = this.config.length; i < len; i++){
35080             if(!this.isLocked(i)){
35081                 return i;
35082             }
35083         }
35084     },
35085
35086     /**
35087      * Returns the number of columns.
35088      * @return {Number}
35089      */
35090     getColumnCount : function(visibleOnly){
35091         if(visibleOnly === true){
35092             var c = 0;
35093             for(var i = 0, len = this.config.length; i < len; i++){
35094                 if(!this.isHidden(i)){
35095                     c++;
35096                 }
35097             }
35098             return c;
35099         }
35100         return this.config.length;
35101     },
35102
35103     /**
35104      * Returns the column configs that return true by the passed function that is called with (columnConfig, index)
35105      * @param {Function} fn
35106      * @param {Object} scope (optional)
35107      * @return {Array} result
35108      */
35109     getColumnsBy : function(fn, scope){
35110         var r = [];
35111         for(var i = 0, len = this.config.length; i < len; i++){
35112             var c = this.config[i];
35113             if(fn.call(scope||this, c, i) === true){
35114                 r[r.length] = c;
35115             }
35116         }
35117         return r;
35118     },
35119
35120     /**
35121      * Returns true if the specified column is sortable.
35122      * @param {Number} col The column index
35123      * @return {Boolean}
35124      */
35125     isSortable : function(col){
35126         if(typeof this.config[col].sortable == "undefined"){
35127             return this.defaultSortable;
35128         }
35129         return this.config[col].sortable;
35130     },
35131
35132     /**
35133      * Returns the rendering (formatting) function defined for the column.
35134      * @param {Number} col The column index.
35135      * @return {Function} The function used to render the cell. See {@link #setRenderer}.
35136      */
35137     getRenderer : function(col){
35138         if(!this.config[col].renderer){
35139             return Roo.grid.ColumnModel.defaultRenderer;
35140         }
35141         return this.config[col].renderer;
35142     },
35143
35144     /**
35145      * Sets the rendering (formatting) function for a column.
35146      * @param {Number} col The column index
35147      * @param {Function} fn The function to use to process the cell's raw data
35148      * to return HTML markup for the grid view. The render function is called with
35149      * the following parameters:<ul>
35150      * <li>Data value.</li>
35151      * <li>Cell metadata. An object in which you may set the following attributes:<ul>
35152      * <li>css A CSS style string to apply to the table cell.</li>
35153      * <li>attr An HTML attribute definition string to apply to the data container element <i>within</i> the table cell.</li></ul>
35154      * <li>The {@link Roo.data.Record} from which the data was extracted.</li>
35155      * <li>Row index</li>
35156      * <li>Column index</li>
35157      * <li>The {@link Roo.data.Store} object from which the Record was extracted</li></ul>
35158      */
35159     setRenderer : function(col, fn){
35160         this.config[col].renderer = fn;
35161     },
35162
35163     /**
35164      * Returns the width for the specified column.
35165      * @param {Number} col The column index
35166      * @return {Number}
35167      */
35168     getColumnWidth : function(col){
35169         return this.config[col].width * 1 || this.defaultWidth;
35170     },
35171
35172     /**
35173      * Sets the width for a column.
35174      * @param {Number} col The column index
35175      * @param {Number} width The new width
35176      */
35177     setColumnWidth : function(col, width, suppressEvent){
35178         this.config[col].width = width;
35179         this.totalWidth = null;
35180         if(!suppressEvent){
35181              this.fireEvent("widthchange", this, col, width);
35182         }
35183     },
35184
35185     /**
35186      * Returns the total width of all columns.
35187      * @param {Boolean} includeHidden True to include hidden column widths
35188      * @return {Number}
35189      */
35190     getTotalWidth : function(includeHidden){
35191         if(!this.totalWidth){
35192             this.totalWidth = 0;
35193             for(var i = 0, len = this.config.length; i < len; i++){
35194                 if(includeHidden || !this.isHidden(i)){
35195                     this.totalWidth += this.getColumnWidth(i);
35196                 }
35197             }
35198         }
35199         return this.totalWidth;
35200     },
35201
35202     /**
35203      * Returns the header for the specified column.
35204      * @param {Number} col The column index
35205      * @return {String}
35206      */
35207     getColumnHeader : function(col){
35208         return this.config[col].header;
35209     },
35210
35211     /**
35212      * Sets the header for a column.
35213      * @param {Number} col The column index
35214      * @param {String} header The new header
35215      */
35216     setColumnHeader : function(col, header){
35217         this.config[col].header = header;
35218         this.fireEvent("headerchange", this, col, header);
35219     },
35220
35221     /**
35222      * Returns the tooltip for the specified column.
35223      * @param {Number} col The column index
35224      * @return {String}
35225      */
35226     getColumnTooltip : function(col){
35227             return this.config[col].tooltip;
35228     },
35229     /**
35230      * Sets the tooltip for a column.
35231      * @param {Number} col The column index
35232      * @param {String} tooltip The new tooltip
35233      */
35234     setColumnTooltip : function(col, tooltip){
35235             this.config[col].tooltip = tooltip;
35236     },
35237
35238     /**
35239      * Returns the dataIndex for the specified column.
35240      * @param {Number} col The column index
35241      * @return {Number}
35242      */
35243     getDataIndex : function(col){
35244         return this.config[col].dataIndex;
35245     },
35246
35247     /**
35248      * Sets the dataIndex for a column.
35249      * @param {Number} col The column index
35250      * @param {Number} dataIndex The new dataIndex
35251      */
35252     setDataIndex : function(col, dataIndex){
35253         this.config[col].dataIndex = dataIndex;
35254     },
35255
35256     
35257     
35258     /**
35259      * Returns true if the cell is editable.
35260      * @param {Number} colIndex The column index
35261      * @param {Number} rowIndex The row index
35262      * @return {Boolean}
35263      */
35264     isCellEditable : function(colIndex, rowIndex){
35265         return (this.config[colIndex].editable || (typeof this.config[colIndex].editable == "undefined" && this.config[colIndex].editor)) ? true : false;
35266     },
35267
35268     /**
35269      * Returns the editor defined for the cell/column.
35270      * return false or null to disable editing.
35271      * @param {Number} colIndex The column index
35272      * @param {Number} rowIndex The row index
35273      * @return {Object}
35274      */
35275     getCellEditor : function(colIndex, rowIndex){
35276         return this.config[colIndex].editor;
35277     },
35278
35279     /**
35280      * Sets if a column is editable.
35281      * @param {Number} col The column index
35282      * @param {Boolean} editable True if the column is editable
35283      */
35284     setEditable : function(col, editable){
35285         this.config[col].editable = editable;
35286     },
35287
35288
35289     /**
35290      * Returns true if the column is hidden.
35291      * @param {Number} colIndex The column index
35292      * @return {Boolean}
35293      */
35294     isHidden : function(colIndex){
35295         return this.config[colIndex].hidden;
35296     },
35297
35298
35299     /**
35300      * Returns true if the column width cannot be changed
35301      */
35302     isFixed : function(colIndex){
35303         return this.config[colIndex].fixed;
35304     },
35305
35306     /**
35307      * Returns true if the column can be resized
35308      * @return {Boolean}
35309      */
35310     isResizable : function(colIndex){
35311         return colIndex >= 0 && this.config[colIndex].resizable !== false && this.config[colIndex].fixed !== true;
35312     },
35313     /**
35314      * Sets if a column is hidden.
35315      * @param {Number} colIndex The column index
35316      * @param {Boolean} hidden True if the column is hidden
35317      */
35318     setHidden : function(colIndex, hidden){
35319         this.config[colIndex].hidden = hidden;
35320         this.totalWidth = null;
35321         this.fireEvent("hiddenchange", this, colIndex, hidden);
35322     },
35323
35324     /**
35325      * Sets the editor for a column.
35326      * @param {Number} col The column index
35327      * @param {Object} editor The editor object
35328      */
35329     setEditor : function(col, editor){
35330         this.config[col].editor = editor;
35331     }
35332 });
35333
35334 Roo.grid.ColumnModel.defaultRenderer = function(value){
35335         if(typeof value == "string" && value.length < 1){
35336             return "&#160;";
35337         }
35338         return value;
35339 };
35340
35341 // Alias for backwards compatibility
35342 Roo.grid.DefaultColumnModel = Roo.grid.ColumnModel;
35343 /*
35344  * Based on:
35345  * Ext JS Library 1.1.1
35346  * Copyright(c) 2006-2007, Ext JS, LLC.
35347  *
35348  * Originally Released Under LGPL - original licence link has changed is not relivant.
35349  *
35350  * Fork - LGPL
35351  * <script type="text/javascript">
35352  */
35353
35354 /**
35355  * @class Roo.grid.AbstractSelectionModel
35356  * @extends Roo.util.Observable
35357  * Abstract base class for grid SelectionModels.  It provides the interface that should be
35358  * implemented by descendant classes.  This class should not be directly instantiated.
35359  * @constructor
35360  */
35361 Roo.grid.AbstractSelectionModel = function(){
35362     this.locked = false;
35363     Roo.grid.AbstractSelectionModel.superclass.constructor.call(this);
35364 };
35365
35366 Roo.extend(Roo.grid.AbstractSelectionModel, Roo.util.Observable,  {
35367     /** @ignore Called by the grid automatically. Do not call directly. */
35368     init : function(grid){
35369         this.grid = grid;
35370         this.initEvents();
35371     },
35372
35373     /**
35374      * Locks the selections.
35375      */
35376     lock : function(){
35377         this.locked = true;
35378     },
35379
35380     /**
35381      * Unlocks the selections.
35382      */
35383     unlock : function(){
35384         this.locked = false;
35385     },
35386
35387     /**
35388      * Returns true if the selections are locked.
35389      * @return {Boolean}
35390      */
35391     isLocked : function(){
35392         return this.locked;
35393     }
35394 });/*
35395  * Based on:
35396  * Ext JS Library 1.1.1
35397  * Copyright(c) 2006-2007, Ext JS, LLC.
35398  *
35399  * Originally Released Under LGPL - original licence link has changed is not relivant.
35400  *
35401  * Fork - LGPL
35402  * <script type="text/javascript">
35403  */
35404 /**
35405  * @extends Roo.grid.AbstractSelectionModel
35406  * @class Roo.grid.RowSelectionModel
35407  * The default SelectionModel used by {@link Roo.grid.Grid}.
35408  * It supports multiple selections and keyboard selection/navigation. 
35409  * @constructor
35410  * @param {Object} config
35411  */
35412 Roo.grid.RowSelectionModel = function(config){
35413     Roo.apply(this, config);
35414     this.selections = new Roo.util.MixedCollection(false, function(o){
35415         return o.id;
35416     });
35417
35418     this.last = false;
35419     this.lastActive = false;
35420
35421     this.addEvents({
35422         /**
35423              * @event selectionchange
35424              * Fires when the selection changes
35425              * @param {SelectionModel} this
35426              */
35427             "selectionchange" : true,
35428         /**
35429              * @event afterselectionchange
35430              * Fires after the selection changes (eg. by key press or clicking)
35431              * @param {SelectionModel} this
35432              */
35433             "afterselectionchange" : true,
35434         /**
35435              * @event beforerowselect
35436              * Fires when a row is selected being selected, return false to cancel.
35437              * @param {SelectionModel} this
35438              * @param {Number} rowIndex The selected index
35439              * @param {Boolean} keepExisting False if other selections will be cleared
35440              */
35441             "beforerowselect" : true,
35442         /**
35443              * @event rowselect
35444              * Fires when a row is selected.
35445              * @param {SelectionModel} this
35446              * @param {Number} rowIndex The selected index
35447              * @param {Roo.data.Record} r The record
35448              */
35449             "rowselect" : true,
35450         /**
35451              * @event rowdeselect
35452              * Fires when a row is deselected.
35453              * @param {SelectionModel} this
35454              * @param {Number} rowIndex The selected index
35455              */
35456         "rowdeselect" : true
35457     });
35458     Roo.grid.RowSelectionModel.superclass.constructor.call(this);
35459     this.locked = false;
35460 };
35461
35462 Roo.extend(Roo.grid.RowSelectionModel, Roo.grid.AbstractSelectionModel,  {
35463     /**
35464      * @cfg {Boolean} singleSelect
35465      * True to allow selection of only one row at a time (defaults to false)
35466      */
35467     singleSelect : false,
35468
35469     // private
35470     initEvents : function(){
35471
35472         if(!this.grid.enableDragDrop && !this.grid.enableDrag){
35473             this.grid.on("mousedown", this.handleMouseDown, this);
35474         }else{ // allow click to work like normal
35475             this.grid.on("rowclick", this.handleDragableRowClick, this);
35476         }
35477
35478         this.rowNav = new Roo.KeyNav(this.grid.getGridEl(), {
35479             "up" : function(e){
35480                 if(!e.shiftKey){
35481                     this.selectPrevious(e.shiftKey);
35482                 }else if(this.last !== false && this.lastActive !== false){
35483                     var last = this.last;
35484                     this.selectRange(this.last,  this.lastActive-1);
35485                     this.grid.getView().focusRow(this.lastActive);
35486                     if(last !== false){
35487                         this.last = last;
35488                     }
35489                 }else{
35490                     this.selectFirstRow();
35491                 }
35492                 this.fireEvent("afterselectionchange", this);
35493             },
35494             "down" : function(e){
35495                 if(!e.shiftKey){
35496                     this.selectNext(e.shiftKey);
35497                 }else if(this.last !== false && this.lastActive !== false){
35498                     var last = this.last;
35499                     this.selectRange(this.last,  this.lastActive+1);
35500                     this.grid.getView().focusRow(this.lastActive);
35501                     if(last !== false){
35502                         this.last = last;
35503                     }
35504                 }else{
35505                     this.selectFirstRow();
35506                 }
35507                 this.fireEvent("afterselectionchange", this);
35508             },
35509             scope: this
35510         });
35511
35512         var view = this.grid.view;
35513         view.on("refresh", this.onRefresh, this);
35514         view.on("rowupdated", this.onRowUpdated, this);
35515         view.on("rowremoved", this.onRemove, this);
35516     },
35517
35518     // private
35519     onRefresh : function(){
35520         var ds = this.grid.dataSource, i, v = this.grid.view;
35521         var s = this.selections;
35522         s.each(function(r){
35523             if((i = ds.indexOfId(r.id)) != -1){
35524                 v.onRowSelect(i);
35525             }else{
35526                 s.remove(r);
35527             }
35528         });
35529     },
35530
35531     // private
35532     onRemove : function(v, index, r){
35533         this.selections.remove(r);
35534     },
35535
35536     // private
35537     onRowUpdated : function(v, index, r){
35538         if(this.isSelected(r)){
35539             v.onRowSelect(index);
35540         }
35541     },
35542
35543     /**
35544      * Select records.
35545      * @param {Array} records The records to select
35546      * @param {Boolean} keepExisting (optional) True to keep existing selections
35547      */
35548     selectRecords : function(records, keepExisting){
35549         if(!keepExisting){
35550             this.clearSelections();
35551         }
35552         var ds = this.grid.dataSource;
35553         for(var i = 0, len = records.length; i < len; i++){
35554             this.selectRow(ds.indexOf(records[i]), true);
35555         }
35556     },
35557
35558     /**
35559      * Gets the number of selected rows.
35560      * @return {Number}
35561      */
35562     getCount : function(){
35563         return this.selections.length;
35564     },
35565
35566     /**
35567      * Selects the first row in the grid.
35568      */
35569     selectFirstRow : function(){
35570         this.selectRow(0);
35571     },
35572
35573     /**
35574      * Select the last row.
35575      * @param {Boolean} keepExisting (optional) True to keep existing selections
35576      */
35577     selectLastRow : function(keepExisting){
35578         this.selectRow(this.grid.dataSource.getCount() - 1, keepExisting);
35579     },
35580
35581     /**
35582      * Selects the row immediately following the last selected row.
35583      * @param {Boolean} keepExisting (optional) True to keep existing selections
35584      */
35585     selectNext : function(keepExisting){
35586         if(this.last !== false && (this.last+1) < this.grid.dataSource.getCount()){
35587             this.selectRow(this.last+1, keepExisting);
35588             this.grid.getView().focusRow(this.last);
35589         }
35590     },
35591
35592     /**
35593      * Selects the row that precedes the last selected row.
35594      * @param {Boolean} keepExisting (optional) True to keep existing selections
35595      */
35596     selectPrevious : function(keepExisting){
35597         if(this.last){
35598             this.selectRow(this.last-1, keepExisting);
35599             this.grid.getView().focusRow(this.last);
35600         }
35601     },
35602
35603     /**
35604      * Returns the selected records
35605      * @return {Array} Array of selected records
35606      */
35607     getSelections : function(){
35608         return [].concat(this.selections.items);
35609     },
35610
35611     /**
35612      * Returns the first selected record.
35613      * @return {Record}
35614      */
35615     getSelected : function(){
35616         return this.selections.itemAt(0);
35617     },
35618
35619
35620     /**
35621      * Clears all selections.
35622      */
35623     clearSelections : function(fast){
35624         if(this.locked) return;
35625         if(fast !== true){
35626             var ds = this.grid.dataSource;
35627             var s = this.selections;
35628             s.each(function(r){
35629                 this.deselectRow(ds.indexOfId(r.id));
35630             }, this);
35631             s.clear();
35632         }else{
35633             this.selections.clear();
35634         }
35635         this.last = false;
35636     },
35637
35638
35639     /**
35640      * Selects all rows.
35641      */
35642     selectAll : function(){
35643         if(this.locked) return;
35644         this.selections.clear();
35645         for(var i = 0, len = this.grid.dataSource.getCount(); i < len; i++){
35646             this.selectRow(i, true);
35647         }
35648     },
35649
35650     /**
35651      * Returns True if there is a selection.
35652      * @return {Boolean}
35653      */
35654     hasSelection : function(){
35655         return this.selections.length > 0;
35656     },
35657
35658     /**
35659      * Returns True if the specified row is selected.
35660      * @param {Number/Record} record The record or index of the record to check
35661      * @return {Boolean}
35662      */
35663     isSelected : function(index){
35664         var r = typeof index == "number" ? this.grid.dataSource.getAt(index) : index;
35665         return (r && this.selections.key(r.id) ? true : false);
35666     },
35667
35668     /**
35669      * Returns True if the specified record id is selected.
35670      * @param {String} id The id of record to check
35671      * @return {Boolean}
35672      */
35673     isIdSelected : function(id){
35674         return (this.selections.key(id) ? true : false);
35675     },
35676
35677     // private
35678     handleMouseDown : function(e, t){
35679         var view = this.grid.getView(), rowIndex;
35680         if(this.isLocked() || (rowIndex = view.findRowIndex(t)) === false){
35681             return;
35682         };
35683         if(e.shiftKey && this.last !== false){
35684             var last = this.last;
35685             this.selectRange(last, rowIndex, e.ctrlKey);
35686             this.last = last; // reset the last
35687             view.focusRow(rowIndex);
35688         }else{
35689             var isSelected = this.isSelected(rowIndex);
35690             if(e.button !== 0 && isSelected){
35691                 view.focusRow(rowIndex);
35692             }else if(e.ctrlKey && isSelected){
35693                 this.deselectRow(rowIndex);
35694             }else if(!isSelected){
35695                 this.selectRow(rowIndex, e.button === 0 && (e.ctrlKey || e.shiftKey));
35696                 view.focusRow(rowIndex);
35697             }
35698         }
35699         this.fireEvent("afterselectionchange", this);
35700     },
35701     // private
35702     handleDragableRowClick :  function(grid, rowIndex, e) 
35703     {
35704         if(e.button === 0 && !e.shiftKey && !e.ctrlKey) {
35705             this.selectRow(rowIndex, false);
35706             grid.view.focusRow(rowIndex);
35707              this.fireEvent("afterselectionchange", this);
35708         }
35709     },
35710     
35711     /**
35712      * Selects multiple rows.
35713      * @param {Array} rows Array of the indexes of the row to select
35714      * @param {Boolean} keepExisting (optional) True to keep existing selections
35715      */
35716     selectRows : function(rows, keepExisting){
35717         if(!keepExisting){
35718             this.clearSelections();
35719         }
35720         for(var i = 0, len = rows.length; i < len; i++){
35721             this.selectRow(rows[i], true);
35722         }
35723     },
35724
35725     /**
35726      * Selects a range of rows. All rows in between startRow and endRow are also selected.
35727      * @param {Number} startRow The index of the first row in the range
35728      * @param {Number} endRow The index of the last row in the range
35729      * @param {Boolean} keepExisting (optional) True to retain existing selections
35730      */
35731     selectRange : function(startRow, endRow, keepExisting){
35732         if(this.locked) return;
35733         if(!keepExisting){
35734             this.clearSelections();
35735         }
35736         if(startRow <= endRow){
35737             for(var i = startRow; i <= endRow; i++){
35738                 this.selectRow(i, true);
35739             }
35740         }else{
35741             for(var i = startRow; i >= endRow; i--){
35742                 this.selectRow(i, true);
35743             }
35744         }
35745     },
35746
35747     /**
35748      * Deselects a range of rows. All rows in between startRow and endRow are also deselected.
35749      * @param {Number} startRow The index of the first row in the range
35750      * @param {Number} endRow The index of the last row in the range
35751      */
35752     deselectRange : function(startRow, endRow, preventViewNotify){
35753         if(this.locked) return;
35754         for(var i = startRow; i <= endRow; i++){
35755             this.deselectRow(i, preventViewNotify);
35756         }
35757     },
35758
35759     /**
35760      * Selects a row.
35761      * @param {Number} row The index of the row to select
35762      * @param {Boolean} keepExisting (optional) True to keep existing selections
35763      */
35764     selectRow : function(index, keepExisting, preventViewNotify){
35765         if(this.locked || (index < 0 || index >= this.grid.dataSource.getCount())) return;
35766         if(this.fireEvent("beforerowselect", this, index, keepExisting) !== false){
35767             if(!keepExisting || this.singleSelect){
35768                 this.clearSelections();
35769             }
35770             var r = this.grid.dataSource.getAt(index);
35771             this.selections.add(r);
35772             this.last = this.lastActive = index;
35773             if(!preventViewNotify){
35774                 this.grid.getView().onRowSelect(index);
35775             }
35776             this.fireEvent("rowselect", this, index, r);
35777             this.fireEvent("selectionchange", this);
35778         }
35779     },
35780
35781     /**
35782      * Deselects a row.
35783      * @param {Number} row The index of the row to deselect
35784      */
35785     deselectRow : function(index, preventViewNotify){
35786         if(this.locked) return;
35787         if(this.last == index){
35788             this.last = false;
35789         }
35790         if(this.lastActive == index){
35791             this.lastActive = false;
35792         }
35793         var r = this.grid.dataSource.getAt(index);
35794         this.selections.remove(r);
35795         if(!preventViewNotify){
35796             this.grid.getView().onRowDeselect(index);
35797         }
35798         this.fireEvent("rowdeselect", this, index);
35799         this.fireEvent("selectionchange", this);
35800     },
35801
35802     // private
35803     restoreLast : function(){
35804         if(this._last){
35805             this.last = this._last;
35806         }
35807     },
35808
35809     // private
35810     acceptsNav : function(row, col, cm){
35811         return !cm.isHidden(col) && cm.isCellEditable(col, row);
35812     },
35813
35814     // private
35815     onEditorKey : function(field, e){
35816         var k = e.getKey(), newCell, g = this.grid, ed = g.activeEditor;
35817         if(k == e.TAB){
35818             e.stopEvent();
35819             ed.completeEdit();
35820             if(e.shiftKey){
35821                 newCell = g.walkCells(ed.row, ed.col-1, -1, this.acceptsNav, this);
35822             }else{
35823                 newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
35824             }
35825         }else if(k == e.ENTER && !e.ctrlKey){
35826             e.stopEvent();
35827             ed.completeEdit();
35828             if(e.shiftKey){
35829                 newCell = g.walkCells(ed.row-1, ed.col, -1, this.acceptsNav, this);
35830             }else{
35831                 newCell = g.walkCells(ed.row+1, ed.col, 1, this.acceptsNav, this);
35832             }
35833         }else if(k == e.ESC){
35834             ed.cancelEdit();
35835         }
35836         if(newCell){
35837             g.startEditing(newCell[0], newCell[1]);
35838         }
35839     }
35840 });/*
35841  * Based on:
35842  * Ext JS Library 1.1.1
35843  * Copyright(c) 2006-2007, Ext JS, LLC.
35844  *
35845  * Originally Released Under LGPL - original licence link has changed is not relivant.
35846  *
35847  * Fork - LGPL
35848  * <script type="text/javascript">
35849  */
35850 /**
35851  * @class Roo.grid.CellSelectionModel
35852  * @extends Roo.grid.AbstractSelectionModel
35853  * This class provides the basic implementation for cell selection in a grid.
35854  * @constructor
35855  * @param {Object} config The object containing the configuration of this model.
35856  */
35857 Roo.grid.CellSelectionModel = function(config){
35858     Roo.apply(this, config);
35859
35860     this.selection = null;
35861
35862     this.addEvents({
35863         /**
35864              * @event beforerowselect
35865              * Fires before a cell is selected.
35866              * @param {SelectionModel} this
35867              * @param {Number} rowIndex The selected row index
35868              * @param {Number} colIndex The selected cell index
35869              */
35870             "beforecellselect" : true,
35871         /**
35872              * @event cellselect
35873              * Fires when a cell is selected.
35874              * @param {SelectionModel} this
35875              * @param {Number} rowIndex The selected row index
35876              * @param {Number} colIndex The selected cell index
35877              */
35878             "cellselect" : true,
35879         /**
35880              * @event selectionchange
35881              * Fires when the active selection changes.
35882              * @param {SelectionModel} this
35883              * @param {Object} selection null for no selection or an object (o) with two properties
35884                 <ul>
35885                 <li>o.record: the record object for the row the selection is in</li>
35886                 <li>o.cell: An array of [rowIndex, columnIndex]</li>
35887                 </ul>
35888              */
35889             "selectionchange" : true
35890     });
35891     Roo.grid.CellSelectionModel.superclass.constructor.call(this);
35892 };
35893
35894 Roo.extend(Roo.grid.CellSelectionModel, Roo.grid.AbstractSelectionModel,  {
35895
35896     /** @ignore */
35897     initEvents : function(){
35898         this.grid.on("mousedown", this.handleMouseDown, this);
35899         this.grid.getGridEl().on(Roo.isIE ? "keydown" : "keypress", this.handleKeyDown, this);
35900         var view = this.grid.view;
35901         view.on("refresh", this.onViewChange, this);
35902         view.on("rowupdated", this.onRowUpdated, this);
35903         view.on("beforerowremoved", this.clearSelections, this);
35904         view.on("beforerowsinserted", this.clearSelections, this);
35905         if(this.grid.isEditor){
35906             this.grid.on("beforeedit", this.beforeEdit,  this);
35907         }
35908     },
35909
35910         //private
35911     beforeEdit : function(e){
35912         this.select(e.row, e.column, false, true, e.record);
35913     },
35914
35915         //private
35916     onRowUpdated : function(v, index, r){
35917         if(this.selection && this.selection.record == r){
35918             v.onCellSelect(index, this.selection.cell[1]);
35919         }
35920     },
35921
35922         //private
35923     onViewChange : function(){
35924         this.clearSelections(true);
35925     },
35926
35927         /**
35928          * Returns the currently selected cell,.
35929          * @return {Array} The selected cell (row, column) or null if none selected.
35930          */
35931     getSelectedCell : function(){
35932         return this.selection ? this.selection.cell : null;
35933     },
35934
35935     /**
35936      * Clears all selections.
35937      * @param {Boolean} true to prevent the gridview from being notified about the change.
35938      */
35939     clearSelections : function(preventNotify){
35940         var s = this.selection;
35941         if(s){
35942             if(preventNotify !== true){
35943                 this.grid.view.onCellDeselect(s.cell[0], s.cell[1]);
35944             }
35945             this.selection = null;
35946             this.fireEvent("selectionchange", this, null);
35947         }
35948     },
35949
35950     /**
35951      * Returns true if there is a selection.
35952      * @return {Boolean}
35953      */
35954     hasSelection : function(){
35955         return this.selection ? true : false;
35956     },
35957
35958     /** @ignore */
35959     handleMouseDown : function(e, t){
35960         var v = this.grid.getView();
35961         if(this.isLocked()){
35962             return;
35963         };
35964         var row = v.findRowIndex(t);
35965         var cell = v.findCellIndex(t);
35966         if(row !== false && cell !== false){
35967             this.select(row, cell);
35968         }
35969     },
35970
35971     /**
35972      * Selects a cell.
35973      * @param {Number} rowIndex
35974      * @param {Number} collIndex
35975      */
35976     select : function(rowIndex, colIndex, preventViewNotify, preventFocus, /*internal*/ r){
35977         if(this.fireEvent("beforecellselect", this, rowIndex, colIndex) !== false){
35978             this.clearSelections();
35979             r = r || this.grid.dataSource.getAt(rowIndex);
35980             this.selection = {
35981                 record : r,
35982                 cell : [rowIndex, colIndex]
35983             };
35984             if(!preventViewNotify){
35985                 var v = this.grid.getView();
35986                 v.onCellSelect(rowIndex, colIndex);
35987                 if(preventFocus !== true){
35988                     v.focusCell(rowIndex, colIndex);
35989                 }
35990             }
35991             this.fireEvent("cellselect", this, rowIndex, colIndex);
35992             this.fireEvent("selectionchange", this, this.selection);
35993         }
35994     },
35995
35996         //private
35997     isSelectable : function(rowIndex, colIndex, cm){
35998         return !cm.isHidden(colIndex);
35999     },
36000
36001     /** @ignore */
36002     handleKeyDown : function(e){
36003         Roo.log('Cell Sel Model handleKeyDown');
36004         if(!e.isNavKeyPress()){
36005             return;
36006         }
36007         var g = this.grid, s = this.selection;
36008         if(!s){
36009             e.stopEvent();
36010             var cell = g.walkCells(0, 0, 1, this.isSelectable,  this);
36011             if(cell){
36012                 this.select(cell[0], cell[1]);
36013             }
36014             return;
36015         }
36016         var sm = this;
36017         var walk = function(row, col, step){
36018             return g.walkCells(row, col, step, sm.isSelectable,  sm);
36019         };
36020         var k = e.getKey(), r = s.cell[0], c = s.cell[1];
36021         var newCell;
36022
36023         switch(k){
36024             case e.TAB:
36025                 // handled by onEditorKey
36026                 if (g.isEditor && g.editing) {
36027                     return;
36028                 }
36029                 if(e.shiftKey){
36030                      newCell = walk(r, c-1, -1);
36031                 }else{
36032                      newCell = walk(r, c+1, 1);
36033                 }
36034              break;
36035              case e.DOWN:
36036                  newCell = walk(r+1, c, 1);
36037              break;
36038              case e.UP:
36039                  newCell = walk(r-1, c, -1);
36040              break;
36041              case e.RIGHT:
36042                  newCell = walk(r, c+1, 1);
36043              break;
36044              case e.LEFT:
36045                  newCell = walk(r, c-1, -1);
36046              break;
36047              case e.ENTER:
36048                  if(g.isEditor && !g.editing){
36049                     g.startEditing(r, c);
36050                     e.stopEvent();
36051                     return;
36052                 }
36053              break;
36054         };
36055         if(newCell){
36056             this.select(newCell[0], newCell[1]);
36057             e.stopEvent();
36058         }
36059     },
36060
36061     acceptsNav : function(row, col, cm){
36062         return !cm.isHidden(col) && cm.isCellEditable(col, row);
36063     },
36064
36065     onEditorKey : function(field, e){
36066         
36067         var k = e.getKey(), newCell, g = this.grid, ed = g.activeEditor;
36068         ///Roo.log('onEditorKey' + k);
36069         
36070         if(k == e.TAB){
36071             if(e.shiftKey){
36072                 newCell = g.walkCells(ed.row, ed.col-1, -1, this.acceptsNav, this);
36073             }else{
36074                 newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
36075             }
36076             e.stopEvent();
36077         }else if(k == e.ENTER && !e.ctrlKey){
36078             ed.completeEdit();
36079             e.stopEvent();
36080             newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
36081         }else if(k == e.ESC){
36082             ed.cancelEdit();
36083         }
36084         
36085         
36086         if(newCell){
36087             //Roo.log('next cell after edit');
36088             g.startEditing.defer(100, g, [newCell[0], newCell[1]]);
36089         }
36090     }
36091 });/*
36092  * Based on:
36093  * Ext JS Library 1.1.1
36094  * Copyright(c) 2006-2007, Ext JS, LLC.
36095  *
36096  * Originally Released Under LGPL - original licence link has changed is not relivant.
36097  *
36098  * Fork - LGPL
36099  * <script type="text/javascript">
36100  */
36101  
36102 /**
36103  * @class Roo.grid.EditorGrid
36104  * @extends Roo.grid.Grid
36105  * Class for creating and editable grid.
36106  * @param {String/HTMLElement/Roo.Element} container The element into which this grid will be rendered - 
36107  * The container MUST have some type of size defined for the grid to fill. The container will be 
36108  * automatically set to position relative if it isn't already.
36109  * @param {Object} dataSource The data model to bind to
36110  * @param {Object} colModel The column model with info about this grid's columns
36111  */
36112 Roo.grid.EditorGrid = function(container, config){
36113     Roo.grid.EditorGrid.superclass.constructor.call(this, container, config);
36114     this.getGridEl().addClass("xedit-grid");
36115
36116     if(!this.selModel){
36117         this.selModel = new Roo.grid.CellSelectionModel();
36118     }
36119
36120     this.activeEditor = null;
36121
36122         this.addEvents({
36123             /**
36124              * @event beforeedit
36125              * Fires before cell editing is triggered. The edit event object has the following properties <br />
36126              * <ul style="padding:5px;padding-left:16px;">
36127              * <li>grid - This grid</li>
36128              * <li>record - The record being edited</li>
36129              * <li>field - The field name being edited</li>
36130              * <li>value - The value for the field being edited.</li>
36131              * <li>row - The grid row index</li>
36132              * <li>column - The grid column index</li>
36133              * <li>cancel - Set this to true to cancel the edit or return false from your handler.</li>
36134              * </ul>
36135              * @param {Object} e An edit event (see above for description)
36136              */
36137             "beforeedit" : true,
36138             /**
36139              * @event afteredit
36140              * Fires after a cell is edited. <br />
36141              * <ul style="padding:5px;padding-left:16px;">
36142              * <li>grid - This grid</li>
36143              * <li>record - The record being edited</li>
36144              * <li>field - The field name being edited</li>
36145              * <li>value - The value being set</li>
36146              * <li>originalValue - The original value for the field, before the edit.</li>
36147              * <li>row - The grid row index</li>
36148              * <li>column - The grid column index</li>
36149              * </ul>
36150              * @param {Object} e An edit event (see above for description)
36151              */
36152             "afteredit" : true,
36153             /**
36154              * @event validateedit
36155              * Fires after a cell is edited, but before the value is set in the record. 
36156          * You can use this to modify the value being set in the field, Return false
36157              * to cancel the change. The edit event object has the following properties <br />
36158              * <ul style="padding:5px;padding-left:16px;">
36159          * <li>editor - This editor</li>
36160              * <li>grid - This grid</li>
36161              * <li>record - The record being edited</li>
36162              * <li>field - The field name being edited</li>
36163              * <li>value - The value being set</li>
36164              * <li>originalValue - The original value for the field, before the edit.</li>
36165              * <li>row - The grid row index</li>
36166              * <li>column - The grid column index</li>
36167              * <li>cancel - Set this to true to cancel the edit or return false from your handler.</li>
36168              * </ul>
36169              * @param {Object} e An edit event (see above for description)
36170              */
36171             "validateedit" : true
36172         });
36173     this.on("bodyscroll", this.stopEditing,  this);
36174     this.on(this.clicksToEdit == 1 ? "cellclick" : "celldblclick", this.onCellDblClick,  this);
36175 };
36176
36177 Roo.extend(Roo.grid.EditorGrid, Roo.grid.Grid, {
36178     /**
36179      * @cfg {Number} clicksToEdit
36180      * The number of clicks on a cell required to display the cell's editor (defaults to 2)
36181      */
36182     clicksToEdit: 2,
36183
36184     // private
36185     isEditor : true,
36186     // private
36187     trackMouseOver: false, // causes very odd FF errors
36188
36189     onCellDblClick : function(g, row, col){
36190         this.startEditing(row, col);
36191     },
36192
36193     onEditComplete : function(ed, value, startValue){
36194         this.editing = false;
36195         this.activeEditor = null;
36196         ed.un("specialkey", this.selModel.onEditorKey, this.selModel);
36197         var r = ed.record;
36198         var field = this.colModel.getDataIndex(ed.col);
36199         var e = {
36200             grid: this,
36201             record: r,
36202             field: field,
36203             originalValue: startValue,
36204             value: value,
36205             row: ed.row,
36206             column: ed.col,
36207             cancel:false,
36208             editor: ed
36209         };
36210         if(String(value) !== String(startValue)){
36211             
36212             if(this.fireEvent("validateedit", e) !== false && !e.cancel){
36213                 r.set(field, e.value);
36214                 // if we are dealing with a combo box..
36215                 // then we also set the 'name' colum to be the displayField
36216                 if (ed.field.displayField && ed.field.name) {
36217                     r.set(ed.field.name, ed.field.el.dom.value);
36218                 }
36219                 
36220                 delete e.cancel; //?? why!!!
36221                 this.fireEvent("afteredit", e);
36222             }
36223         } else {
36224             this.fireEvent("afteredit", e); // always fire it!
36225         }
36226         this.view.focusCell(ed.row, ed.col);
36227     },
36228
36229     /**
36230      * Starts editing the specified for the specified row/column
36231      * @param {Number} rowIndex
36232      * @param {Number} colIndex
36233      */
36234     startEditing : function(row, col){
36235         this.stopEditing();
36236         if(this.colModel.isCellEditable(col, row)){
36237             this.view.ensureVisible(row, col, true);
36238             var r = this.dataSource.getAt(row);
36239             var field = this.colModel.getDataIndex(col);
36240             var e = {
36241                 grid: this,
36242                 record: r,
36243                 field: field,
36244                 value: r.data[field],
36245                 row: row,
36246                 column: col,
36247                 cancel:false
36248             };
36249             if(this.fireEvent("beforeedit", e) !== false && !e.cancel){
36250                 this.editing = true;
36251                 var ed = this.colModel.getCellEditor(col, row);
36252                 
36253                 if (!ed) {
36254                     return;
36255                 }
36256                 if(!ed.rendered){
36257                     ed.render(ed.parentEl || document.body);
36258                 }
36259                 ed.field.reset();
36260                 (function(){ // complex but required for focus issues in safari, ie and opera
36261                     ed.row = row;
36262                     ed.col = col;
36263                     ed.record = r;
36264                     ed.on("complete", this.onEditComplete, this, {single: true});
36265                     ed.on("specialkey", this.selModel.onEditorKey, this.selModel);
36266                     this.activeEditor = ed;
36267                     var v = r.data[field];
36268                     ed.startEdit(this.view.getCell(row, col), v);
36269                     // combo's with 'displayField and name set
36270                     if (ed.field.displayField && ed.field.name) {
36271                         ed.field.el.dom.value = r.data[ed.field.name];
36272                     }
36273                     
36274                     
36275                 }).defer(50, this);
36276             }
36277         }
36278     },
36279         
36280     /**
36281      * Stops any active editing
36282      */
36283     stopEditing : function(){
36284         if(this.activeEditor){
36285             this.activeEditor.completeEdit();
36286         }
36287         this.activeEditor = null;
36288     }
36289 });/*
36290  * Based on:
36291  * Ext JS Library 1.1.1
36292  * Copyright(c) 2006-2007, Ext JS, LLC.
36293  *
36294  * Originally Released Under LGPL - original licence link has changed is not relivant.
36295  *
36296  * Fork - LGPL
36297  * <script type="text/javascript">
36298  */
36299
36300 // private - not really -- you end up using it !
36301 // This is a support class used internally by the Grid components
36302
36303 /**
36304  * @class Roo.grid.GridEditor
36305  * @extends Roo.Editor
36306  * Class for creating and editable grid elements.
36307  * @param {Object} config any settings (must include field)
36308  */
36309 Roo.grid.GridEditor = function(field, config){
36310     if (!config && field.field) {
36311         config = field;
36312         field = Roo.factory(config.field, Roo.form);
36313     }
36314     Roo.grid.GridEditor.superclass.constructor.call(this, field, config);
36315     field.monitorTab = false;
36316 };
36317
36318 Roo.extend(Roo.grid.GridEditor, Roo.Editor, {
36319     
36320     /**
36321      * @cfg {Roo.form.Field} field Field to wrap (or xtyped)
36322      */
36323     
36324     alignment: "tl-tl",
36325     autoSize: "width",
36326     hideEl : false,
36327     cls: "x-small-editor x-grid-editor",
36328     shim:false,
36329     shadow:"frame"
36330 });/*
36331  * Based on:
36332  * Ext JS Library 1.1.1
36333  * Copyright(c) 2006-2007, Ext JS, LLC.
36334  *
36335  * Originally Released Under LGPL - original licence link has changed is not relivant.
36336  *
36337  * Fork - LGPL
36338  * <script type="text/javascript">
36339  */
36340   
36341
36342   
36343 Roo.grid.PropertyRecord = Roo.data.Record.create([
36344     {name:'name',type:'string'},  'value'
36345 ]);
36346
36347
36348 Roo.grid.PropertyStore = function(grid, source){
36349     this.grid = grid;
36350     this.store = new Roo.data.Store({
36351         recordType : Roo.grid.PropertyRecord
36352     });
36353     this.store.on('update', this.onUpdate,  this);
36354     if(source){
36355         this.setSource(source);
36356     }
36357     Roo.grid.PropertyStore.superclass.constructor.call(this);
36358 };
36359
36360
36361
36362 Roo.extend(Roo.grid.PropertyStore, Roo.util.Observable, {
36363     setSource : function(o){
36364         this.source = o;
36365         this.store.removeAll();
36366         var data = [];
36367         for(var k in o){
36368             if(this.isEditableValue(o[k])){
36369                 data.push(new Roo.grid.PropertyRecord({name: k, value: o[k]}, k));
36370             }
36371         }
36372         this.store.loadRecords({records: data}, {}, true);
36373     },
36374
36375     onUpdate : function(ds, record, type){
36376         if(type == Roo.data.Record.EDIT){
36377             var v = record.data['value'];
36378             var oldValue = record.modified['value'];
36379             if(this.grid.fireEvent('beforepropertychange', this.source, record.id, v, oldValue) !== false){
36380                 this.source[record.id] = v;
36381                 record.commit();
36382                 this.grid.fireEvent('propertychange', this.source, record.id, v, oldValue);
36383             }else{
36384                 record.reject();
36385             }
36386         }
36387     },
36388
36389     getProperty : function(row){
36390        return this.store.getAt(row);
36391     },
36392
36393     isEditableValue: function(val){
36394         if(val && val instanceof Date){
36395             return true;
36396         }else if(typeof val == 'object' || typeof val == 'function'){
36397             return false;
36398         }
36399         return true;
36400     },
36401
36402     setValue : function(prop, value){
36403         this.source[prop] = value;
36404         this.store.getById(prop).set('value', value);
36405     },
36406
36407     getSource : function(){
36408         return this.source;
36409     }
36410 });
36411
36412 Roo.grid.PropertyColumnModel = function(grid, store){
36413     this.grid = grid;
36414     var g = Roo.grid;
36415     g.PropertyColumnModel.superclass.constructor.call(this, [
36416         {header: this.nameText, sortable: true, dataIndex:'name', id: 'name'},
36417         {header: this.valueText, resizable:false, dataIndex: 'value', id: 'value'}
36418     ]);
36419     this.store = store;
36420     this.bselect = Roo.DomHelper.append(document.body, {
36421         tag: 'select', style:'display:none', cls: 'x-grid-editor', children: [
36422             {tag: 'option', value: 'true', html: 'true'},
36423             {tag: 'option', value: 'false', html: 'false'}
36424         ]
36425     });
36426     Roo.id(this.bselect);
36427     var f = Roo.form;
36428     this.editors = {
36429         'date' : new g.GridEditor(new f.DateField({selectOnFocus:true})),
36430         'string' : new g.GridEditor(new f.TextField({selectOnFocus:true})),
36431         'number' : new g.GridEditor(new f.NumberField({selectOnFocus:true, style:'text-align:left;'})),
36432         'int' : new g.GridEditor(new f.NumberField({selectOnFocus:true, allowDecimals:false, style:'text-align:left;'})),
36433         'boolean' : new g.GridEditor(new f.Field({el:this.bselect,selectOnFocus:true}))
36434     };
36435     this.renderCellDelegate = this.renderCell.createDelegate(this);
36436     this.renderPropDelegate = this.renderProp.createDelegate(this);
36437 };
36438
36439 Roo.extend(Roo.grid.PropertyColumnModel, Roo.grid.ColumnModel, {
36440     
36441     
36442     nameText : 'Name',
36443     valueText : 'Value',
36444     
36445     dateFormat : 'm/j/Y',
36446     
36447     
36448     renderDate : function(dateVal){
36449         return dateVal.dateFormat(this.dateFormat);
36450     },
36451
36452     renderBool : function(bVal){
36453         return bVal ? 'true' : 'false';
36454     },
36455
36456     isCellEditable : function(colIndex, rowIndex){
36457         return colIndex == 1;
36458     },
36459
36460     getRenderer : function(col){
36461         return col == 1 ?
36462             this.renderCellDelegate : this.renderPropDelegate;
36463     },
36464
36465     renderProp : function(v){
36466         return this.getPropertyName(v);
36467     },
36468
36469     renderCell : function(val){
36470         var rv = val;
36471         if(val instanceof Date){
36472             rv = this.renderDate(val);
36473         }else if(typeof val == 'boolean'){
36474             rv = this.renderBool(val);
36475         }
36476         return Roo.util.Format.htmlEncode(rv);
36477     },
36478
36479     getPropertyName : function(name){
36480         var pn = this.grid.propertyNames;
36481         return pn && pn[name] ? pn[name] : name;
36482     },
36483
36484     getCellEditor : function(colIndex, rowIndex){
36485         var p = this.store.getProperty(rowIndex);
36486         var n = p.data['name'], val = p.data['value'];
36487         
36488         if(typeof(this.grid.customEditors[n]) == 'string'){
36489             return this.editors[this.grid.customEditors[n]];
36490         }
36491         if(typeof(this.grid.customEditors[n]) != 'undefined'){
36492             return this.grid.customEditors[n];
36493         }
36494         if(val instanceof Date){
36495             return this.editors['date'];
36496         }else if(typeof val == 'number'){
36497             return this.editors['number'];
36498         }else if(typeof val == 'boolean'){
36499             return this.editors['boolean'];
36500         }else{
36501             return this.editors['string'];
36502         }
36503     }
36504 });
36505
36506 /**
36507  * @class Roo.grid.PropertyGrid
36508  * @extends Roo.grid.EditorGrid
36509  * This class represents the  interface of a component based property grid control.
36510  * <br><br>Usage:<pre><code>
36511  var grid = new Roo.grid.PropertyGrid("my-container-id", {
36512       
36513  });
36514  // set any options
36515  grid.render();
36516  * </code></pre>
36517   
36518  * @constructor
36519  * @param {String/HTMLElement/Roo.Element} container The element into which this grid will be rendered -
36520  * The container MUST have some type of size defined for the grid to fill. The container will be
36521  * automatically set to position relative if it isn't already.
36522  * @param {Object} config A config object that sets properties on this grid.
36523  */
36524 Roo.grid.PropertyGrid = function(container, config){
36525     config = config || {};
36526     var store = new Roo.grid.PropertyStore(this);
36527     this.store = store;
36528     var cm = new Roo.grid.PropertyColumnModel(this, store);
36529     store.store.sort('name', 'ASC');
36530     Roo.grid.PropertyGrid.superclass.constructor.call(this, container, Roo.apply({
36531         ds: store.store,
36532         cm: cm,
36533         enableColLock:false,
36534         enableColumnMove:false,
36535         stripeRows:false,
36536         trackMouseOver: false,
36537         clicksToEdit:1
36538     }, config));
36539     this.getGridEl().addClass('x-props-grid');
36540     this.lastEditRow = null;
36541     this.on('columnresize', this.onColumnResize, this);
36542     this.addEvents({
36543          /**
36544              * @event beforepropertychange
36545              * Fires before a property changes (return false to stop?)
36546              * @param {Roo.grid.PropertyGrid} grid property grid? (check could be store)
36547              * @param {String} id Record Id
36548              * @param {String} newval New Value
36549          * @param {String} oldval Old Value
36550              */
36551         "beforepropertychange": true,
36552         /**
36553              * @event propertychange
36554              * Fires after a property changes
36555              * @param {Roo.grid.PropertyGrid} grid property grid? (check could be store)
36556              * @param {String} id Record Id
36557              * @param {String} newval New Value
36558          * @param {String} oldval Old Value
36559              */
36560         "propertychange": true
36561     });
36562     this.customEditors = this.customEditors || {};
36563 };
36564 Roo.extend(Roo.grid.PropertyGrid, Roo.grid.EditorGrid, {
36565     
36566      /**
36567      * @cfg {Object} customEditors map of colnames=> custom editors.
36568      * the custom editor can be one of the standard ones (date|string|number|int|boolean), or a
36569      * grid editor eg. Roo.grid.GridEditor(new Roo.form.TextArea({selectOnFocus:true})),
36570      * false disables editing of the field.
36571          */
36572     
36573       /**
36574      * @cfg {Object} propertyNames map of property Names to their displayed value
36575          */
36576     
36577     render : function(){
36578         Roo.grid.PropertyGrid.superclass.render.call(this);
36579         this.autoSize.defer(100, this);
36580     },
36581
36582     autoSize : function(){
36583         Roo.grid.PropertyGrid.superclass.autoSize.call(this);
36584         if(this.view){
36585             this.view.fitColumns();
36586         }
36587     },
36588
36589     onColumnResize : function(){
36590         this.colModel.setColumnWidth(1, this.container.getWidth(true)-this.colModel.getColumnWidth(0));
36591         this.autoSize();
36592     },
36593     /**
36594      * Sets the data for the Grid
36595      * accepts a Key => Value object of all the elements avaiable.
36596      * @param {Object} data  to appear in grid.
36597      */
36598     setSource : function(source){
36599         this.store.setSource(source);
36600         //this.autoSize();
36601     },
36602     /**
36603      * Gets all the data from the grid.
36604      * @return {Object} data  data stored in grid
36605      */
36606     getSource : function(){
36607         return this.store.getSource();
36608     }
36609 });/*
36610  * Based on:
36611  * Ext JS Library 1.1.1
36612  * Copyright(c) 2006-2007, Ext JS, LLC.
36613  *
36614  * Originally Released Under LGPL - original licence link has changed is not relivant.
36615  *
36616  * Fork - LGPL
36617  * <script type="text/javascript">
36618  */
36619  
36620 /**
36621  * @class Roo.LoadMask
36622  * A simple utility class for generically masking elements while loading data.  If the element being masked has
36623  * an underlying {@link Roo.data.Store}, the masking will be automatically synchronized with the store's loading
36624  * process and the mask element will be cached for reuse.  For all other elements, this mask will replace the
36625  * element's UpdateManager load indicator and will be destroyed after the initial load.
36626  * @constructor
36627  * Create a new LoadMask
36628  * @param {String/HTMLElement/Roo.Element} el The element or DOM node, or its id
36629  * @param {Object} config The config object
36630  */
36631 Roo.LoadMask = function(el, config){
36632     this.el = Roo.get(el);
36633     Roo.apply(this, config);
36634     if(this.store){
36635         this.store.on('beforeload', this.onBeforeLoad, this);
36636         this.store.on('load', this.onLoad, this);
36637         this.store.on('loadexception', this.onLoad, this);
36638         this.removeMask = false;
36639     }else{
36640         var um = this.el.getUpdateManager();
36641         um.showLoadIndicator = false; // disable the default indicator
36642         um.on('beforeupdate', this.onBeforeLoad, this);
36643         um.on('update', this.onLoad, this);
36644         um.on('failure', this.onLoad, this);
36645         this.removeMask = true;
36646     }
36647 };
36648
36649 Roo.LoadMask.prototype = {
36650     /**
36651      * @cfg {Boolean} removeMask
36652      * True to create a single-use mask that is automatically destroyed after loading (useful for page loads),
36653      * False to persist the mask element reference for multiple uses (e.g., for paged data widgets).  Defaults to false.
36654      */
36655     /**
36656      * @cfg {String} msg
36657      * The text to display in a centered loading message box (defaults to 'Loading...')
36658      */
36659     msg : 'Loading...',
36660     /**
36661      * @cfg {String} msgCls
36662      * The CSS class to apply to the loading message element (defaults to "x-mask-loading")
36663      */
36664     msgCls : 'x-mask-loading',
36665
36666     /**
36667      * Read-only. True if the mask is currently disabled so that it will not be displayed (defaults to false)
36668      * @type Boolean
36669      */
36670     disabled: false,
36671
36672     /**
36673      * Disables the mask to prevent it from being displayed
36674      */
36675     disable : function(){
36676        this.disabled = true;
36677     },
36678
36679     /**
36680      * Enables the mask so that it can be displayed
36681      */
36682     enable : function(){
36683         this.disabled = false;
36684     },
36685
36686     // private
36687     onLoad : function(){
36688         this.el.unmask(this.removeMask);
36689     },
36690
36691     // private
36692     onBeforeLoad : function(){
36693         if(!this.disabled){
36694             this.el.mask(this.msg, this.msgCls);
36695         }
36696     },
36697
36698     // private
36699     destroy : function(){
36700         if(this.store){
36701             this.store.un('beforeload', this.onBeforeLoad, this);
36702             this.store.un('load', this.onLoad, this);
36703             this.store.un('loadexception', this.onLoad, this);
36704         }else{
36705             var um = this.el.getUpdateManager();
36706             um.un('beforeupdate', this.onBeforeLoad, this);
36707             um.un('update', this.onLoad, this);
36708             um.un('failure', this.onLoad, this);
36709         }
36710     }
36711 };/*
36712  * Based on:
36713  * Ext JS Library 1.1.1
36714  * Copyright(c) 2006-2007, Ext JS, LLC.
36715  *
36716  * Originally Released Under LGPL - original licence link has changed is not relivant.
36717  *
36718  * Fork - LGPL
36719  * <script type="text/javascript">
36720  */
36721 Roo.XTemplate = function(){
36722     Roo.XTemplate.superclass.constructor.apply(this, arguments);
36723     var s = this.html;
36724
36725     s = ['<tpl>', s, '</tpl>'].join('');
36726
36727     var re = /<tpl\b[^>]*>((?:(?=([^<]+))\2|<(?!tpl\b[^>]*>))*?)<\/tpl>/;
36728
36729     var nameRe = /^<tpl\b[^>]*?for="(.*?)"/;
36730     var ifRe = /^<tpl\b[^>]*?if="(.*?)"/;
36731     var execRe = /^<tpl\b[^>]*?exec="(.*?)"/;
36732     var m, id = 0;
36733     var tpls = [];
36734
36735     while(m = s.match(re)){
36736        var m2 = m[0].match(nameRe);
36737        var m3 = m[0].match(ifRe);
36738        var m4 = m[0].match(execRe);
36739        var exp = null, fn = null, exec = null;
36740        var name = m2 && m2[1] ? m2[1] : '';
36741        if(m3){
36742            exp = m3 && m3[1] ? m3[1] : null;
36743            if(exp){
36744                fn = new Function('values', 'parent', 'with(values){ return '+(Roo.util.Format.htmlDecode(exp))+'; }');
36745            }
36746        }
36747        if(m4){
36748            exp = m4 && m4[1] ? m4[1] : null;
36749            if(exp){
36750                exec = new Function('values', 'parent', 'with(values){ '+(Roo.util.Format.htmlDecode(exp))+'; }');
36751            }
36752        }
36753        if(name){
36754            switch(name){
36755                case '.': name = new Function('values', 'parent', 'with(values){ return values; }'); break;
36756                case '..': name = new Function('values', 'parent', 'with(values){ return parent; }'); break;
36757                default: name = new Function('values', 'parent', 'with(values){ return '+name+'; }');
36758            }
36759        }
36760        tpls.push({
36761             id: id,
36762             target: name,
36763             exec: exec,
36764             test: fn,
36765             body: m[1]||''
36766         });
36767        s = s.replace(m[0], '{xtpl'+ id + '}');
36768        ++id;
36769     }
36770     for(var i = tpls.length-1; i >= 0; --i){
36771         this.compileTpl(tpls[i]);
36772     }
36773     this.master = tpls[tpls.length-1];
36774     this.tpls = tpls;
36775 };
36776 Roo.extend(Roo.XTemplate, Roo.Template, {
36777
36778     re : /\{([\w-\.]+)(?:\:([\w\.]*)(?:\((.*?)?\))?)?\}/g,
36779
36780     applySubTemplate : function(id, values, parent){
36781         var t = this.tpls[id];
36782         if(t.test && !t.test.call(this, values, parent)){
36783             return '';
36784         }
36785         if(t.exec && t.exec.call(this, values, parent)){
36786             return '';
36787         }
36788         var vs = t.target ? t.target.call(this, values, parent) : values;
36789         parent = t.target ? values : parent;
36790         if(t.target && vs instanceof Array){
36791             var buf = [];
36792             for(var i = 0, len = vs.length; i < len; i++){
36793                 buf[buf.length] = t.compiled.call(this, vs[i], parent);
36794             }
36795             return buf.join('');
36796         }
36797         return t.compiled.call(this, vs, parent);
36798     },
36799
36800     compileTpl : function(tpl){
36801         var fm = Roo.util.Format;
36802         var useF = this.disableFormats !== true;
36803         var sep = Roo.isGecko ? "+" : ",";
36804         var fn = function(m, name, format, args){
36805             if(name.substr(0, 4) == 'xtpl'){
36806                 return "'"+ sep +'this.applySubTemplate('+name.substr(4)+', values, parent)'+sep+"'";
36807             }
36808             var v;
36809             if(name.indexOf('.') != -1){
36810                 v = name;
36811             }else{
36812                 v = "values['" + name + "']";
36813             }
36814             if(format && useF){
36815                 args = args ? ',' + args : "";
36816                 if(format.substr(0, 5) != "this."){
36817                     format = "fm." + format + '(';
36818                 }else{
36819                     format = 'this.call("'+ format.substr(5) + '", ';
36820                     args = ", values";
36821                 }
36822             }else{
36823                 args= ''; format = "("+v+" === undefined ? '' : ";
36824             }
36825             return "'"+ sep + format + v + args + ")"+sep+"'";
36826         };
36827         var body;
36828         // branched to use + in gecko and [].join() in others
36829         if(Roo.isGecko){
36830             body = "tpl.compiled = function(values, parent){ return '" +
36831                    tpl.body.replace(/(\r\n|\n)/g, '\\n').replace(/'/g, "\\'").replace(this.re, fn) +
36832                     "';};";
36833         }else{
36834             body = ["tpl.compiled = function(values, parent){ return ['"];
36835             body.push(tpl.body.replace(/(\r\n|\n)/g, '\\n').replace(/'/g, "\\'").replace(this.re, fn));
36836             body.push("'].join('');};");
36837             body = body.join('');
36838         }
36839         /** eval:var:zzzzzzz */
36840         eval(body);
36841         return this;
36842     },
36843
36844     applyTemplate : function(values){
36845         return this.master.compiled.call(this, values, {});
36846         var s = this.subs;
36847     },
36848
36849     apply : function(){
36850         return this.applyTemplate.apply(this, arguments);
36851     },
36852
36853     compile : function(){return this;}
36854 });
36855
36856 Roo.XTemplate.from = function(el){
36857     el = Roo.getDom(el);
36858     return new Roo.XTemplate(el.value || el.innerHTML);
36859 };/*
36860  * Original code for Roojs - LGPL
36861  * <script type="text/javascript">
36862  */
36863  
36864 /**
36865  * @class Roo.XComponent
36866  * A delayed Element creator...
36867  * Or a way to group chunks of interface together.
36868  * 
36869  * Mypart.xyx = new Roo.XComponent({
36870
36871     parent : 'Mypart.xyz', // empty == document.element.!!
36872     order : '001',
36873     name : 'xxxx'
36874     region : 'xxxx'
36875     disabled : function() {} 
36876      
36877     tree : function() { // return an tree of xtype declared components
36878         var MODULE = this;
36879         return 
36880         {
36881             xtype : 'NestedLayoutPanel',
36882             // technicall
36883         }
36884      ]
36885  *})
36886  *
36887  *
36888  * It can be used to build a big heiracy, with parent etc.
36889  * or you can just use this to render a single compoent to a dom element
36890  * MYPART.render(Roo.Element | String(id) | dom_element )
36891  * 
36892  * @extends Roo.util.Observable
36893  * @constructor
36894  * @param cfg {Object} configuration of component
36895  * 
36896  */
36897 Roo.XComponent = function(cfg) {
36898     Roo.apply(this, cfg);
36899     this.addEvents({ 
36900         /**
36901              * @event built
36902              * Fires when this the componnt is built
36903              * @param {Roo.XComponent} c the component
36904              */
36905         'built' : true,
36906         /**
36907              * @event buildcomplete
36908              * Fires on the top level element when all elements have been built
36909              * @param {Roo.XComponent} c the top level component.
36910          */
36911         'buildcomplete' : true
36912         
36913     });
36914     this.region = this.region || 'center'; // default..
36915     Roo.XComponent.register(this);
36916     this.modules = false;
36917     this.el = false; // where the layout goes..
36918     
36919     
36920 }
36921 Roo.extend(Roo.XComponent, Roo.util.Observable, {
36922     /**
36923      * @property el
36924      * The created element (with Roo.factory())
36925      * @type {Roo.Layout}
36926      */
36927     el  : false,
36928     
36929     /**
36930      * @property el
36931      * for BC  - use el in new code
36932      * @type {Roo.Layout}
36933      */
36934     panel : false,
36935     
36936     /**
36937      * @property layout
36938      * for BC  - use el in new code
36939      * @type {Roo.Layout}
36940      */
36941     layout : false,
36942     
36943      /**
36944      * @cfg {Function|boolean} disabled
36945      * If this module is disabled by some rule, return true from the funtion
36946      */
36947     disabled : false,
36948     
36949     /**
36950      * @cfg {String} parent 
36951      * Name of parent element which it get xtype added to..
36952      */
36953     parent: false,
36954     
36955     /**
36956      * @cfg {String} order
36957      * Used to set the order in which elements are created (usefull for multiple tabs)
36958      */
36959     
36960     order : false,
36961     /**
36962      * @cfg {String} name
36963      * String to display while loading.
36964      */
36965     name : false,
36966     /**
36967      * @cfg {String} region
36968      * Region to render component to (defaults to center)
36969      */
36970     region : 'center',
36971     
36972     /**
36973      * @cfg {Array} items
36974      * A single item array - the first element is the root of the tree..
36975      * It's done this way to stay compatible with the Xtype system...
36976      */
36977     items : false,
36978     
36979     
36980      /**
36981      * render
36982      * render element to dom or tree
36983      * @param {Roo.Element|String|DomElement} optional render to if parent is not set.
36984      */
36985     
36986     render : function(el)
36987     {
36988         
36989         el = el || false;
36990         var hp = this.parent ? 1 : 0;
36991         
36992         if (!el && typeof(this.parent) == 'string' && this.parent.substring(0,1) == '#') {
36993             // if parent is a '#.....' string, then let's use that..
36994             var ename = this.parent.substr(1)
36995             this.parent = false;
36996             el = Roo.get(ename);
36997             if (!el) {
36998                 Roo.log("Warning - element can not be found :#" + ename );
36999                 return;
37000             }
37001         }
37002         
37003         
37004         if (!this.parent) {
37005             
37006             el = el ? Roo.get(el) : false;
37007             
37008             // it's a top level one..
37009             this.parent =  {
37010                 el : new Roo.BorderLayout(el || document.body, {
37011                 
37012                      center: {
37013                          titlebar: false,
37014                          autoScroll:false,
37015                          closeOnTab: true,
37016                          tabPosition: 'top',
37017                           //resizeTabs: true,
37018                          alwaysShowTabs: el && hp? false :  true,
37019                          hideTabs: el || !hp ? true :  false,
37020                          minTabWidth: 140
37021                      }
37022                  })
37023             }
37024         }
37025         
37026         
37027             
37028         var tree = this.tree();
37029         tree.region = tree.region || this.region;
37030         this.el = this.parent.el.addxtype(tree);
37031         this.fireEvent('built', this);
37032         
37033         this.panel = this.el;
37034         this.layout = this.panel.layout;    
37035          
37036     }
37037     
37038 });
37039
37040 Roo.apply(Roo.XComponent, {
37041     
37042     /**
37043      * @property  buildCompleted
37044      * True when the builder has completed building the interface.
37045      * @type Boolean
37046      */
37047     buildCompleted : false,
37048      
37049     /**
37050      * @property  topModule
37051      * the upper most module - uses document.element as it's constructor.
37052      * @type Object
37053      */
37054      
37055     topModule  : false,
37056       
37057     /**
37058      * @property  modules
37059      * array of modules to be created by registration system.
37060      * @type {Array} of Roo.XComponent
37061      */
37062     
37063     modules : [],
37064     /**
37065      * @property  elmodules
37066      * array of modules to be created by which use #ID 
37067      * @type {Array} of Roo.XComponent
37068      */
37069      
37070     elmodules : [],
37071
37072     
37073     /**
37074      * Register components to be built later.
37075      *
37076      * This solves the following issues
37077      * - Building is not done on page load, but after an authentication process has occured.
37078      * - Interface elements are registered on page load
37079      * - Parent Interface elements may not be loaded before child, so this handles that..
37080      * 
37081      *
37082      * example:
37083      * 
37084      * MyApp.register({
37085           order : '000001',
37086           module : 'Pman.Tab.projectMgr',
37087           region : 'center',
37088           parent : 'Pman.layout',
37089           disabled : false,  // or use a function..
37090         })
37091      
37092      * * @param {Object} details about module
37093      */
37094     register : function(obj) {
37095         this.modules.push(obj);
37096          
37097     },
37098     /**
37099      * convert a string to an object..
37100      * eg. 'AAA.BBB' -> finds AAA.BBB
37101
37102      */
37103     
37104     toObject : function(str)
37105     {
37106         if (!str || typeof(str) == 'object') {
37107             return str;
37108         }
37109         if (str.substring(0,1) == '#') {
37110             return str;
37111         }
37112
37113         var ar = str.split('.');
37114         var rt, o;
37115         rt = ar.shift();
37116             /** eval:var:o */
37117         try {
37118             eval('if (typeof ' + rt + ' == "undefined"){ o = false;} o = ' + rt + ';');
37119         } catch (e) {
37120             throw "Module not found : " + str;
37121         }
37122         
37123         if (o === false) {
37124             throw "Module not found : " + str;
37125         }
37126         Roo.each(ar, function(e) {
37127             if (typeof(o[e]) == 'undefined') {
37128                 throw "Module not found : " + str;
37129             }
37130             o = o[e];
37131         });
37132         
37133         return o;
37134         
37135     },
37136     
37137     
37138     /**
37139      * move modules into their correct place in the tree..
37140      * 
37141      */
37142     preBuild : function ()
37143     {
37144         var _t = this;
37145         Roo.each(this.modules , function (obj)
37146         {
37147             var opar = obj.parent;
37148             try { 
37149                 obj.parent = this.toObject(opar);
37150             } catch(e) {
37151                 Roo.log(e.toString());
37152                 return;
37153             }
37154             
37155             if (!obj.parent) {
37156                 this.topModule = obj;
37157                 return;
37158             }
37159             if (typeof(obj.parent) == 'string') {
37160                 this.elmodules.push(obj);
37161                 return;
37162             }
37163             if (obj.parent.constructor != Roo.XComponent) {
37164                 Roo.log("Object Parent is not instance of XComponent:" + obj.name)
37165             }
37166             if (!obj.parent.modules) {
37167                 obj.parent.modules = new Roo.util.MixedCollection(false, 
37168                     function(o) { return o.order + '' }
37169                 );
37170             }
37171             
37172             obj.parent.modules.add(obj);
37173         }, this);
37174     },
37175     
37176      /**
37177      * make a list of modules to build.
37178      * @return {Array} list of modules. 
37179      */ 
37180     
37181     buildOrder : function()
37182     {
37183         var _this = this;
37184         var cmp = function(a,b) {   
37185             return String(a).toUpperCase() > String(b).toUpperCase() ? 1 : -1;
37186         };
37187         if ((!this.topModule || !this.topModule.modules) && !this.elmodules.length) {
37188             throw "No top level modules to build";
37189         }
37190         
37191         // make a flat list in order of modules to build.
37192         var mods = this.topModule ? [ this.topModule ] : [];
37193         Roo.each(this.elmodules,function(e) { mods.push(e) });
37194
37195         
37196         // add modules to their parents..
37197         var addMod = function(m) {
37198            // Roo.debug && Roo.log(m.modKey);
37199             
37200             mods.push(m);
37201             if (m.modules) {
37202                 m.modules.keySort('ASC',  cmp );
37203                 m.modules.each(addMod);
37204             }
37205             // not sure if this is used any more..
37206             if (m.finalize) {
37207                 m.finalize.name = m.name + " (clean up) ";
37208                 mods.push(m.finalize);
37209             }
37210             
37211         }
37212         if (this.topModule) { 
37213             this.topModule.modules.keySort('ASC',  cmp );
37214             this.topModule.modules.each(addMod);
37215         }
37216         return mods;
37217     },
37218     
37219      /**
37220      * Build the registered modules.
37221      * @param {Object} parent element.
37222      * @param {Function} optional method to call after module has been added.
37223      * 
37224      */ 
37225    
37226     build : function() 
37227     {
37228         
37229         this.preBuild();
37230         var mods = this.buildOrder();
37231       
37232         //this.allmods = mods;
37233         //Roo.debug && Roo.log(mods);
37234         //return;
37235         if (!mods.length) { // should not happen
37236             throw "NO modules!!!";
37237         }
37238         
37239         
37240         
37241         // flash it up as modal - so we store the mask!?
37242         Roo.MessageBox.show({ title: 'loading' });
37243         Roo.MessageBox.show({
37244            title: "Please wait...",
37245            msg: "Building Interface...",
37246            width:450,
37247            progress:true,
37248            closable:false,
37249            modal: false
37250           
37251         });
37252         var total = mods.length;
37253         
37254         var _this = this;
37255         var progressRun = function() {
37256             if (!mods.length) {
37257                 Roo.debug && Roo.log('hide?');
37258                 Roo.MessageBox.hide();
37259                 if (_this.topModule) { 
37260                     _this.topModule.fireEvent('buildcomplete', _this.topModule);
37261                 }
37262                 // THE END...
37263                 return false;   
37264             }
37265             
37266             var m = mods.shift();
37267             
37268             
37269             Roo.debug && Roo.log(m);
37270             // not sure if this is supported any more.. - modules that are are just function
37271             if (typeof(m) == 'function') { 
37272                 m.call(this);
37273                 return progressRun.defer(10, _this);
37274             } 
37275             
37276             
37277             
37278             Roo.MessageBox.updateProgress(
37279                 (total  - mods.length)/total,  "Building Interface " + (total  - mods.length) + 
37280                     " of " + total + 
37281                     (m.name ? (' - ' + m.name) : '')
37282                     );
37283             
37284          
37285             // is the module disabled?
37286             var disabled = (typeof(m.disabled) == 'function') ?
37287                 m.disabled.call(m.module.disabled) : m.disabled;    
37288             
37289             
37290             if (disabled) {
37291                 return progressRun(); // we do not update the display!
37292             }
37293             
37294             // now build 
37295             
37296             m.render();
37297             // it's 10 on top level, and 1 on others??? why...
37298             return progressRun.defer(10, _this);
37299              
37300         }
37301         progressRun.defer(1, _this);
37302      
37303         
37304         
37305     }
37306     
37307      
37308    
37309     
37310     
37311 });
37312  //<script type="text/javascript">
37313
37314
37315 /**
37316  * @class Roo.Login
37317  * @extends Roo.LayoutDialog
37318  * A generic Login Dialog..... - only one needed in theory!?!?
37319  *
37320  * Fires XComponent builder on success...
37321  * 
37322  * Sends 
37323  *    username,password, lang = for login actions.
37324  *    check = 1 for periodic checking that sesion is valid.
37325  *    passwordRequest = email request password
37326  *    logout = 1 = to logout
37327  * 
37328  * Affects: (this id="????" elements)
37329  *   loading  (removed) (used to indicate application is loading)
37330  *   loading-mask (hides) (used to hide application when it's building loading)
37331  *   
37332  * 
37333  * Usage: 
37334  *    
37335  * 
37336  * Myapp.login = Roo.Login({
37337      url: xxxx,
37338    
37339      realm : 'Myapp', 
37340      
37341      
37342      method : 'POST',
37343      
37344      
37345      * 
37346  })
37347  * 
37348  * 
37349  * 
37350  **/
37351  
37352 Roo.Login = function(cfg)
37353 {
37354     this.addEvents({
37355         'refreshed' : true
37356     });
37357     
37358     Roo.apply(this,cfg);
37359     
37360     Roo.onReady(function() {
37361         this.onLoad();
37362     }, this);
37363     // call parent..
37364     
37365    
37366     Roo.Login.superclass.constructor.call(this, this);
37367     //this.addxtype(this.items[0]);
37368     
37369     
37370 }
37371
37372
37373 Roo.extend(Roo.Login, Roo.LayoutDialog, {
37374     
37375     /**
37376      * @cfg {String} method
37377      * Method used to query for login details.
37378      */
37379     
37380     method : 'POST',
37381     /**
37382      * @cfg {String} url
37383      * URL to query login data. - eg. baseURL + '/Login.php'
37384      */
37385     url : '',
37386     
37387     /**
37388      * @property user
37389      * The user data - if user.id < 0 then login will be bypassed. (used for inital setup situation.
37390      * @type {Object} 
37391      */
37392     user : false,
37393     /**
37394      * @property checkFails
37395      * Number of times we have attempted to get authentication check, and failed.
37396      * @type {Number} 
37397      */
37398     checkFails : 0,
37399       /**
37400      * @property intervalID
37401      * The window interval that does the constant login checking.
37402      * @type {Number} 
37403      */
37404     intervalID : 0,
37405     
37406     
37407     onLoad : function() // called on page load...
37408     {
37409         // load 
37410          
37411         if (Roo.get('loading')) { // clear any loading indicator..
37412             Roo.get('loading').remove();
37413         }
37414         
37415         //this.switchLang('en'); // set the language to english..
37416        
37417         this.check({
37418             success:  function(response, opts)  {  // check successfull...
37419             
37420                 var res = this.processResponse(response);
37421                 this.checkFails =0;
37422                 if (!res.success) { // error!
37423                     this.checkFails = 5;
37424                     //console.log('call failure');
37425                     return this.failure(response,opts);
37426                 }
37427                 
37428                 if (!res.data.id) { // id=0 == login failure.
37429                     return this.show();
37430                 }
37431                 
37432                               
37433                         //console.log(success);
37434                 this.fillAuth(res.data);   
37435                 this.checkFails =0;
37436                 Roo.XComponent.build();
37437             },
37438             failure : this.show
37439         });
37440         
37441     }, 
37442     
37443     
37444     check: function(cfg) // called every so often to refresh cookie etc..
37445     {
37446         if (cfg.again) { // could be undefined..
37447             this.checkFails++;
37448         } else {
37449             this.checkFails = 0;
37450         }
37451         var _this = this;
37452         if (this.sending) {
37453             if ( this.checkFails > 4) {
37454                 Roo.MessageBox.alert("Error",  
37455                     "Error getting authentication status. - try reloading, or wait a while", function() {
37456                         _this.sending = false;
37457                     }); 
37458                 return;
37459             }
37460             cfg.again = true;
37461             _this.check.defer(10000, _this, [ cfg ]); // check in 10 secs.
37462             return;
37463         }
37464         this.sending = true;
37465         
37466         Roo.Ajax.request({  
37467             url: this.url,
37468             params: {
37469                 getAuthUser: true
37470             },  
37471             method: this.method,
37472             success:  cfg.success || this.success,
37473             failure : cfg.failure || this.failure,
37474             scope : this,
37475             callCfg : cfg
37476               
37477         });  
37478     }, 
37479     
37480     
37481     logout: function()
37482     {
37483         window.onbeforeunload = function() { }; // false does not work for IE..
37484         this.user = false;
37485         var _this = this;
37486         
37487         Roo.Ajax.request({  
37488             url: this.url,
37489             params: {
37490                 logout: 1
37491             },  
37492             method: 'GET',
37493             failure : function() {
37494                 Roo.MessageBox.alert("Error", "Error logging out. - continuing anyway.", function() {
37495                     document.location = document.location.toString() + '?ts=' + Math.random();
37496                 });
37497                 
37498             },
37499             success : function() {
37500                 _this.user = false;
37501                 this.checkFails =0;
37502                 // fixme..
37503                 document.location = document.location.toString() + '?ts=' + Math.random();
37504             }
37505               
37506               
37507         }); 
37508     },
37509     
37510     processResponse : function (response)
37511     {
37512         var res = '';
37513         try {
37514             res = Roo.decode(response.responseText);
37515             // oops...
37516             if (typeof(res) != 'object') {
37517                 res = { success : false, errorMsg : res, errors : true };
37518             }
37519             if (typeof(res.success) == 'undefined') {
37520                 res.success = false;
37521             }
37522             
37523         } catch(e) {
37524             res = { success : false,  errorMsg : response.responseText, errors : true };
37525         }
37526         return res;
37527     },
37528     
37529     success : function(response, opts)  // check successfull...
37530     {  
37531         this.sending = false;
37532         var res = this.processResponse(response);
37533         if (!res.success) {
37534             return this.failure(response, opts);
37535         }
37536         if (!res.data || !res.data.id) {
37537             return this.failure(response,opts);
37538         }
37539         //console.log(res);
37540         this.fillAuth(res.data);
37541         
37542         this.checkFails =0;
37543         
37544     },
37545     
37546     
37547     failure : function (response, opts) // called if login 'check' fails.. (causes re-check)
37548     {
37549         this.authUser = -1;
37550         this.sending = false;
37551         var res = this.processResponse(response);
37552         //console.log(res);
37553         if ( this.checkFails > 2) {
37554         
37555             Roo.MessageBox.alert("Error", res.errorMsg ? res.errorMsg : 
37556                 "Error getting authentication status. - try reloading"); 
37557             return;
37558         }
37559         opts.callCfg.again = true;
37560         this.check.defer(1000, this, [ opts.callCfg ]);
37561         return;  
37562     },
37563     
37564     
37565     
37566     fillAuth: function(au) {
37567         this.startAuthCheck();
37568         this.authUserId = au.id;
37569         this.authUser = au;
37570         this.lastChecked = new Date();
37571         this.fireEvent('refreshed', au);
37572         //Pman.Tab.FaxQueue.newMaxId(au.faxMax);
37573         //Pman.Tab.FaxTab.setTitle(au.faxNumPending);
37574         au.lang = au.lang || 'en';
37575         //this.switchLang(Roo.state.Manager.get('Pman.Login.lang', 'en'));
37576         Roo.state.Manager.set( this.realm + 'lang' , au.lang);
37577         this.switchLang(au.lang );
37578         
37579      
37580         // open system... - -on setyp..
37581         if (this.authUserId  < 0) {
37582             Roo.MessageBox.alert("Warning", 
37583                 "This is an open system - please set up a admin user with a password.");  
37584         }
37585          
37586         //Pman.onload(); // which should do nothing if it's a re-auth result...
37587         
37588              
37589     },
37590     
37591     startAuthCheck : function() // starter for timeout checking..
37592     {
37593         if (this.intervalID) { // timer already in place...
37594             return false;
37595         }
37596         var _this = this;
37597         this.intervalID =  window.setInterval(function() {
37598               _this.check(false);
37599             }, 120000); // every 120 secs = 2mins..
37600         
37601         
37602     },
37603          
37604     
37605     switchLang : function (lang) 
37606     {
37607         _T = typeof(_T) == 'undefined' ? false : _T;
37608           if (!_T || !lang.length) {
37609             return;
37610         }
37611         
37612         if (!_T && lang != 'en') {
37613             Roo.MessageBox.alert("Sorry", "Language not available yet (" + lang +')');
37614             return;
37615         }
37616         
37617         if (typeof(_T.en) == 'undefined') {
37618             _T.en = {};
37619             Roo.apply(_T.en, _T);
37620         }
37621         
37622         if (typeof(_T[lang]) == 'undefined') {
37623             Roo.MessageBox.alert("Sorry", "Language not available yet (" + lang +')');
37624             return;
37625         }
37626         
37627         
37628         Roo.apply(_T, _T[lang]);
37629         // just need to set the text values for everything...
37630         var _this = this;
37631         /* this will not work ...
37632         if (this.form) { 
37633             
37634                
37635             function formLabel(name, val) {
37636                 _this.form.findField(name).fieldEl.child('label').dom.innerHTML  = val;
37637             }
37638             
37639             formLabel('password', "Password"+':');
37640             formLabel('username', "Email Address"+':');
37641             formLabel('lang', "Language"+':');
37642             this.dialog.setTitle("Login");
37643             this.dialog.buttons[0].setText("Forgot Password");
37644             this.dialog.buttons[1].setText("Login");
37645         }
37646         */
37647         
37648         
37649     },
37650     
37651     
37652     title: "Login",
37653     modal: true,
37654     width:  350,
37655     //height: 230,
37656     height: 180,
37657     shadow: true,
37658     minWidth:200,
37659     minHeight:180,
37660     //proxyDrag: true,
37661     closable: false,
37662     draggable: false,
37663     collapsible: false,
37664     resizable: false,
37665     center: {  // needed??
37666         autoScroll:false,
37667         titlebar: false,
37668        // tabPosition: 'top',
37669         hideTabs: true,
37670         closeOnTab: true,
37671         alwaysShowTabs: false
37672     } ,
37673     listeners : {
37674         
37675         show  : function(dlg)
37676         {
37677             //console.log(this);
37678             this.form = this.layout.getRegion('center').activePanel.form;
37679             this.form.dialog = dlg;
37680             this.buttons[0].form = this.form;
37681             this.buttons[0].dialog = dlg;
37682             this.buttons[1].form = this.form;
37683             this.buttons[1].dialog = dlg;
37684            
37685            //this.resizeToLogo.defer(1000,this);
37686             // this is all related to resizing for logos..
37687             //var sz = Roo.get(Pman.Login.form.el.query('img')[0]).getSize();
37688            //// if (!sz) {
37689              //   this.resizeToLogo.defer(1000,this);
37690              //   return;
37691            // }
37692             //var w = Ext.lib.Dom.getViewWidth() - 100;
37693             //var h = Ext.lib.Dom.getViewHeight() - 100;
37694             //this.resizeTo(Math.max(350, Math.min(sz.width + 30, w)),Math.min(sz.height+200, h));
37695             //this.center();
37696             if (this.disabled) {
37697                 this.hide();
37698                 return;
37699             }
37700             
37701             if (this.user.id < 0) { // used for inital setup situations.
37702                 return;
37703             }
37704             
37705             if (this.intervalID) {
37706                 // remove the timer
37707                 window.clearInterval(this.intervalID);
37708                 this.intervalID = false;
37709             }
37710             
37711             
37712             if (Roo.get('loading')) {
37713                 Roo.get('loading').remove();
37714             }
37715             if (Roo.get('loading-mask')) {
37716                 Roo.get('loading-mask').hide();
37717             }
37718             
37719             //incomming._node = tnode;
37720             this.form.reset();
37721             //this.dialog.modal = !modal;
37722             //this.dialog.show();
37723             this.el.unmask(); 
37724             
37725             
37726             this.form.setValues({
37727                 'username' : Roo.state.Manager.get(this.realm + '.username', ''),
37728                 'lang' : Roo.state.Manager.get(this.realm + '.lang', 'en')
37729             });
37730             
37731             this.switchLang(Roo.state.Manager.get(this.realm + '.lang', 'en'));
37732             if (this.form.findField('username').getValue().length > 0 ){
37733                 this.form.findField('password').focus();
37734             } else {
37735                this.form.findField('username').focus();
37736             }
37737     
37738         }
37739     },
37740     items : [
37741          {
37742        
37743             xtype : 'ContentPanel',
37744             xns : Roo,
37745             region: 'center',
37746             fitToFrame : true,
37747             
37748             items : [
37749     
37750                 {
37751                
37752                     xtype : 'Form',
37753                     xns : Roo.form,
37754                     labelWidth: 100,
37755                     style : 'margin: 10px;',
37756                     
37757                     listeners : {
37758                         actionfailed : function(f, act) {
37759                             // form can return { errors: .... }
37760                                 
37761                             //act.result.errors // invalid form element list...
37762                             //act.result.errorMsg// invalid form element list...
37763                             
37764                             this.dialog.el.unmask();
37765                             Roo.MessageBox.alert("Error", act.result.errorMsg ? act.result.errorMsg : 
37766                                         "Login failed - communication error - try again.");
37767                                       
37768                         },
37769                         actioncomplete: function(re, act) {
37770                              
37771                             Roo.state.Manager.set(
37772                                 this.dialog.realm + '.username',  
37773                                     this.findField('username').getValue()
37774                             );
37775                             Roo.state.Manager.set(
37776                                 this.dialog.realm + '.lang',  
37777                                 this.findField('lang').getValue() 
37778                             );
37779                             
37780                             this.dialog.fillAuth(act.result.data);
37781                               
37782                             this.dialog.hide();
37783                             
37784                             if (Roo.get('loading-mask')) {
37785                                 Roo.get('loading-mask').show();
37786                             }
37787                             Roo.XComponent.build();
37788                             
37789                              
37790                             
37791                         }
37792                     },
37793                     items : [
37794                         {
37795                             xtype : 'TextField',
37796                             xns : Roo.form,
37797                             fieldLabel: "Email Address",
37798                             name: 'username',
37799                             width:200,
37800                             autoCreate : {tag: "input", type: "text", size: "20"}
37801                         },
37802                         {
37803                             xtype : 'TextField',
37804                             xns : Roo.form,
37805                             fieldLabel: "Password",
37806                             inputType: 'password',
37807                             name: 'password',
37808                             width:200,
37809                             autoCreate : {tag: "input", type: "text", size: "20"},
37810                             listeners : {
37811                                 specialkey : function(e,ev) {
37812                                     if (ev.keyCode == 13) {
37813                                         this.form.dialog.el.mask("Logging in");
37814                                         this.form.doAction('submit', {
37815                                             url: this.form.dialog.url,
37816                                             method: this.form.dialog.method
37817                                         });
37818                                     }
37819                                 }
37820                             }  
37821                         },
37822                         {
37823                             xtype : 'ComboBox',
37824                             xns : Roo.form,
37825                             fieldLabel: "Language",
37826                             name : 'langdisp',
37827                             store: {
37828                                 xtype : 'SimpleStore',
37829                                 fields: ['lang', 'ldisp'],
37830                                 data : [
37831                                     [ 'en', 'English' ],
37832                                     [ 'zh_HK' , '\u7E41\u4E2D' ],
37833                                     [ 'zh_CN', '\u7C21\u4E2D' ]
37834                                 ]
37835                             },
37836                             
37837                             valueField : 'lang',
37838                             hiddenName:  'lang',
37839                             width: 200,
37840                             displayField:'ldisp',
37841                             typeAhead: false,
37842                             editable: false,
37843                             mode: 'local',
37844                             triggerAction: 'all',
37845                             emptyText:'Select a Language...',
37846                             selectOnFocus:true,
37847                             listeners : {
37848                                 select :  function(cb, rec, ix) {
37849                                     this.form.switchLang(rec.data.lang);
37850                                 }
37851                             }
37852                         
37853                         }
37854                     ]
37855                 }
37856                   
37857                 
37858             ]
37859         }
37860     ],
37861     buttons : [
37862         {
37863             xtype : 'Button',
37864             xns : 'Roo',
37865             text : "Forgot Password",
37866             listeners : {
37867                 click : function() {
37868                     //console.log(this);
37869                     var n = this.form.findField('username').getValue();
37870                     if (!n.length) {
37871                         Roo.MessageBox.alert("Error", "Fill in your email address");
37872                         return;
37873                     }
37874                     Roo.Ajax.request({
37875                         url: this.dialog.url,
37876                         params: {
37877                             passwordRequest: n
37878                         },
37879                         method: this.dialog.method,
37880                         success:  function(response, opts)  {  // check successfull...
37881                         
37882                             var res = this.dialog.processResponse(response);
37883                             if (!res.success) { // error!
37884                                Roo.MessageBox.alert("Error" ,
37885                                     res.errorMsg ? res.errorMsg  : "Problem Requesting Password Reset");
37886                                return;
37887                             }
37888                             Roo.MessageBox.alert("Notice" ,
37889                                 "Please check you email for the Password Reset message");
37890                         },
37891                         failure : function() {
37892                             Roo.MessageBox.alert("Error" , "Problem Requesting Password Reset");
37893                         }
37894                         
37895                     });
37896                 }
37897             }
37898         },
37899         {
37900             xtype : 'Button',
37901             xns : 'Roo',
37902             text : "Login",
37903             listeners : {
37904                 
37905                 click : function () {
37906                         
37907                     this.dialog.el.mask("Logging in");
37908                     this.form.doAction('submit', {
37909                             url: this.dialog.url,
37910                             method: this.dialog.method
37911                     });
37912                 }
37913             }
37914         }
37915     ]
37916   
37917   
37918 })
37919  
37920
37921
37922