roojs-ui.js
[roojs1] / roojs-ui-debug.js
1 /*
2  * Based on:
3  * Ext JS Library 1.1.1
4  * Copyright(c) 2006-2007, Ext JS, LLC.
5  *
6  * Originally Released Under LGPL - original licence link has changed is not relivant.
7  *
8  * Fork - LGPL
9  * <script type="text/javascript">
10  */
11
12
13
14 /*
15  * These classes are derivatives of the similarly named classes in the YUI Library.
16  * The original license:
17  * Copyright (c) 2006, Yahoo! Inc. All rights reserved.
18  * Code licensed under the BSD License:
19  * http://developer.yahoo.net/yui/license.txt
20  */
21
22 (function() {
23
24 var Event=Roo.EventManager;
25 var Dom=Roo.lib.Dom;
26
27 /**
28  * @class Roo.dd.DragDrop
29  * @extends Roo.util.Observable
30  * Defines the interface and base operation of items that that can be
31  * dragged or can be drop targets.  It was designed to be extended, overriding
32  * the event handlers for startDrag, onDrag, onDragOver and onDragOut.
33  * Up to three html elements can be associated with a DragDrop instance:
34  * <ul>
35  * <li>linked element: the element that is passed into the constructor.
36  * This is the element which defines the boundaries for interaction with
37  * other DragDrop objects.</li>
38  * <li>handle element(s): The drag operation only occurs if the element that
39  * was clicked matches a handle element.  By default this is the linked
40  * element, but there are times that you will want only a portion of the
41  * linked element to initiate the drag operation, and the setHandleElId()
42  * method provides a way to define this.</li>
43  * <li>drag element: this represents the element that would be moved along
44  * with the cursor during a drag operation.  By default, this is the linked
45  * element itself as in {@link Roo.dd.DD}.  setDragElId() lets you define
46  * a separate element that would be moved, as in {@link Roo.dd.DDProxy}.
47  * </li>
48  * </ul>
49  * This class should not be instantiated until the onload event to ensure that
50  * the associated elements are available.
51  * The following would define a DragDrop obj that would interact with any
52  * other DragDrop obj in the "group1" group:
53  * <pre>
54  *  dd = new Roo.dd.DragDrop("div1", "group1");
55  * </pre>
56  * Since none of the event handlers have been implemented, nothing would
57  * actually happen if you were to run the code above.  Normally you would
58  * override this class or one of the default implementations, but you can
59  * also override the methods you want on an instance of the class...
60  * <pre>
61  *  dd.onDragDrop = function(e, id) {
62  *  &nbsp;&nbsp;alert("dd was dropped on " + id);
63  *  }
64  * </pre>
65  * @constructor
66  * @param {String} id of the element that is linked to this instance
67  * @param {String} sGroup the group of related DragDrop objects
68  * @param {object} config an object containing configurable attributes
69  *                Valid properties for DragDrop:
70  *                    padding, isTarget, maintainOffset, primaryButtonOnly
71  */
72 Roo.dd.DragDrop = function(id, sGroup, config) {
73     if (id) {
74         this.init(id, sGroup, config);
75     }
76     
77 };
78
79 Roo.extend(Roo.dd.DragDrop, Roo.util.Observable , {
80
81     /**
82      * The id of the element associated with this object.  This is what we
83      * refer to as the "linked element" because the size and position of
84      * this element is used to determine when the drag and drop objects have
85      * interacted.
86      * @property id
87      * @type String
88      */
89     id: null,
90
91     /**
92      * Configuration attributes passed into the constructor
93      * @property config
94      * @type object
95      */
96     config: null,
97
98     /**
99      * The id of the element that will be dragged.  By default this is same
100      * as the linked element , but could be changed to another element. Ex:
101      * Roo.dd.DDProxy
102      * @property dragElId
103      * @type String
104      * @private
105      */
106     dragElId: null,
107
108     /**
109      * the id of the element that initiates the drag operation.  By default
110      * this is the linked element, but could be changed to be a child of this
111      * element.  This lets us do things like only starting the drag when the
112      * header element within the linked html element is clicked.
113      * @property handleElId
114      * @type String
115      * @private
116      */
117     handleElId: null,
118
119     /**
120      * An associative array of HTML tags that will be ignored if clicked.
121      * @property invalidHandleTypes
122      * @type {string: string}
123      */
124     invalidHandleTypes: null,
125
126     /**
127      * An associative array of ids for elements that will be ignored if clicked
128      * @property invalidHandleIds
129      * @type {string: string}
130      */
131     invalidHandleIds: null,
132
133     /**
134      * An indexted array of css class names for elements that will be ignored
135      * if clicked.
136      * @property invalidHandleClasses
137      * @type string[]
138      */
139     invalidHandleClasses: null,
140
141     /**
142      * The linked element's absolute X position at the time the drag was
143      * started
144      * @property startPageX
145      * @type int
146      * @private
147      */
148     startPageX: 0,
149
150     /**
151      * The linked element's absolute X position at the time the drag was
152      * started
153      * @property startPageY
154      * @type int
155      * @private
156      */
157     startPageY: 0,
158
159     /**
160      * The group defines a logical collection of DragDrop objects that are
161      * related.  Instances only get events when interacting with other
162      * DragDrop object in the same group.  This lets us define multiple
163      * groups using a single DragDrop subclass if we want.
164      * @property groups
165      * @type {string: string}
166      */
167     groups: null,
168
169     /**
170      * Individual drag/drop instances can be locked.  This will prevent
171      * onmousedown start drag.
172      * @property locked
173      * @type boolean
174      * @private
175      */
176     locked: false,
177
178     /**
179      * Lock this instance
180      * @method lock
181      */
182     lock: function() { this.locked = true; },
183
184     /**
185      * Unlock this instace
186      * @method unlock
187      */
188     unlock: function() { this.locked = false; },
189
190     /**
191      * By default, all insances can be a drop target.  This can be disabled by
192      * setting isTarget to false.
193      * @method isTarget
194      * @type boolean
195      */
196     isTarget: true,
197
198     /**
199      * The padding configured for this drag and drop object for calculating
200      * the drop zone intersection with this object.
201      * @method padding
202      * @type int[]
203      */
204     padding: null,
205
206     /**
207      * Cached reference to the linked element
208      * @property _domRef
209      * @private
210      */
211     _domRef: null,
212
213     /**
214      * Internal typeof flag
215      * @property __ygDragDrop
216      * @private
217      */
218     __ygDragDrop: true,
219
220     /**
221      * Set to true when horizontal contraints are applied
222      * @property constrainX
223      * @type boolean
224      * @private
225      */
226     constrainX: false,
227
228     /**
229      * Set to true when vertical contraints are applied
230      * @property constrainY
231      * @type boolean
232      * @private
233      */
234     constrainY: false,
235
236     /**
237      * The left constraint
238      * @property minX
239      * @type int
240      * @private
241      */
242     minX: 0,
243
244     /**
245      * The right constraint
246      * @property maxX
247      * @type int
248      * @private
249      */
250     maxX: 0,
251
252     /**
253      * The up constraint
254      * @property minY
255      * @type int
256      * @type int
257      * @private
258      */
259     minY: 0,
260
261     /**
262      * The down constraint
263      * @property maxY
264      * @type int
265      * @private
266      */
267     maxY: 0,
268
269     /**
270      * Maintain offsets when we resetconstraints.  Set to true when you want
271      * the position of the element relative to its parent to stay the same
272      * when the page changes
273      *
274      * @property maintainOffset
275      * @type boolean
276      */
277     maintainOffset: false,
278
279     /**
280      * Array of pixel locations the element will snap to if we specified a
281      * horizontal graduation/interval.  This array is generated automatically
282      * when you define a tick interval.
283      * @property xTicks
284      * @type int[]
285      */
286     xTicks: null,
287
288     /**
289      * Array of pixel locations the element will snap to if we specified a
290      * vertical graduation/interval.  This array is generated automatically
291      * when you define a tick interval.
292      * @property yTicks
293      * @type int[]
294      */
295     yTicks: null,
296
297     /**
298      * By default the drag and drop instance will only respond to the primary
299      * button click (left button for a right-handed mouse).  Set to true to
300      * allow drag and drop to start with any mouse click that is propogated
301      * by the browser
302      * @property primaryButtonOnly
303      * @type boolean
304      */
305     primaryButtonOnly: true,
306
307     /**
308      * The availabe property is false until the linked dom element is accessible.
309      * @property available
310      * @type boolean
311      */
312     available: false,
313
314     /**
315      * By default, drags can only be initiated if the mousedown occurs in the
316      * region the linked element is.  This is done in part to work around a
317      * bug in some browsers that mis-report the mousedown if the previous
318      * mouseup happened outside of the window.  This property is set to true
319      * if outer handles are defined.
320      *
321      * @property hasOuterHandles
322      * @type boolean
323      * @default false
324      */
325     hasOuterHandles: false,
326
327     /**
328      * Code that executes immediately before the startDrag event
329      * @method b4StartDrag
330      * @private
331      */
332     b4StartDrag: function(x, y) { },
333
334     /**
335      * Abstract method called after a drag/drop object is clicked
336      * and the drag or mousedown time thresholds have beeen met.
337      * @method startDrag
338      * @param {int} X click location
339      * @param {int} Y click location
340      */
341     startDrag: function(x, y) { /* override this */ },
342
343     /**
344      * Code that executes immediately before the onDrag event
345      * @method b4Drag
346      * @private
347      */
348     b4Drag: function(e) { },
349
350     /**
351      * Abstract method called during the onMouseMove event while dragging an
352      * object.
353      * @method onDrag
354      * @param {Event} e the mousemove event
355      */
356     onDrag: function(e) { /* override this */ },
357
358     /**
359      * Abstract method called when this element fist begins hovering over
360      * another DragDrop obj
361      * @method onDragEnter
362      * @param {Event} e the mousemove event
363      * @param {String|DragDrop[]} id In POINT mode, the element
364      * id this is hovering over.  In INTERSECT mode, an array of one or more
365      * dragdrop items being hovered over.
366      */
367     onDragEnter: function(e, id) { /* override this */ },
368
369     /**
370      * Code that executes immediately before the onDragOver event
371      * @method b4DragOver
372      * @private
373      */
374     b4DragOver: function(e) { },
375
376     /**
377      * Abstract method called when this element is hovering over another
378      * DragDrop obj
379      * @method onDragOver
380      * @param {Event} e the mousemove event
381      * @param {String|DragDrop[]} id In POINT mode, the element
382      * id this is hovering over.  In INTERSECT mode, an array of dd items
383      * being hovered over.
384      */
385     onDragOver: function(e, id) { /* override this */ },
386
387     /**
388      * Code that executes immediately before the onDragOut event
389      * @method b4DragOut
390      * @private
391      */
392     b4DragOut: function(e) { },
393
394     /**
395      * Abstract method called when we are no longer hovering over an element
396      * @method onDragOut
397      * @param {Event} e the mousemove event
398      * @param {String|DragDrop[]} id In POINT mode, the element
399      * id this was hovering over.  In INTERSECT mode, an array of dd items
400      * that the mouse is no longer over.
401      */
402     onDragOut: function(e, id) { /* override this */ },
403
404     /**
405      * Code that executes immediately before the onDragDrop event
406      * @method b4DragDrop
407      * @private
408      */
409     b4DragDrop: function(e) { },
410
411     /**
412      * Abstract method called when this item is dropped on another DragDrop
413      * obj
414      * @method onDragDrop
415      * @param {Event} e the mouseup event
416      * @param {String|DragDrop[]} id In POINT mode, the element
417      * id this was dropped on.  In INTERSECT mode, an array of dd items this
418      * was dropped on.
419      */
420     onDragDrop: function(e, id) { /* override this */ },
421
422     /**
423      * Abstract method called when this item is dropped on an area with no
424      * drop target
425      * @method onInvalidDrop
426      * @param {Event} e the mouseup event
427      */
428     onInvalidDrop: function(e) { /* override this */ },
429
430     /**
431      * Code that executes immediately before the endDrag event
432      * @method b4EndDrag
433      * @private
434      */
435     b4EndDrag: function(e) { },
436
437     /**
438      * Fired when we are done dragging the object
439      * @method endDrag
440      * @param {Event} e the mouseup event
441      */
442     endDrag: function(e) { /* override this */ },
443
444     /**
445      * Code executed immediately before the onMouseDown event
446      * @method b4MouseDown
447      * @param {Event} e the mousedown event
448      * @private
449      */
450     b4MouseDown: function(e) {  },
451
452     /**
453      * Event handler that fires when a drag/drop obj gets a mousedown
454      * @method onMouseDown
455      * @param {Event} e the mousedown event
456      */
457     onMouseDown: function(e) { /* override this */ },
458
459     /**
460      * Event handler that fires when a drag/drop obj gets a mouseup
461      * @method onMouseUp
462      * @param {Event} e the mouseup event
463      */
464     onMouseUp: function(e) { /* override this */ },
465
466     /**
467      * Override the onAvailable method to do what is needed after the initial
468      * position was determined.
469      * @method onAvailable
470      */
471     onAvailable: function () {
472     },
473
474     /*
475      * Provides default constraint padding to "constrainTo" elements (defaults to {left: 0, right:0, top:0, bottom:0}).
476      * @type Object
477      */
478     defaultPadding : {left:0, right:0, top:0, bottom:0},
479
480     /*
481      * Initializes the drag drop object's constraints to restrict movement to a certain element.
482  *
483  * Usage:
484  <pre><code>
485  var dd = new Roo.dd.DDProxy("dragDiv1", "proxytest",
486                 { dragElId: "existingProxyDiv" });
487  dd.startDrag = function(){
488      this.constrainTo("parent-id");
489  };
490  </code></pre>
491  * Or you can initalize it using the {@link Roo.Element} object:
492  <pre><code>
493  Roo.get("dragDiv1").initDDProxy("proxytest", {dragElId: "existingProxyDiv"}, {
494      startDrag : function(){
495          this.constrainTo("parent-id");
496      }
497  });
498  </code></pre>
499      * @param {String/HTMLElement/Element} constrainTo The element to constrain to.
500      * @param {Object/Number} pad (optional) Pad provides a way to specify "padding" of the constraints,
501      * and can be either a number for symmetrical padding (4 would be equal to {left:4, right:4, top:4, bottom:4}) or
502      * an object containing the sides to pad. For example: {right:10, bottom:10}
503      * @param {Boolean} inContent (optional) Constrain the draggable in the content box of the element (inside padding and borders)
504      */
505     constrainTo : function(constrainTo, pad, inContent){
506         if(typeof pad == "number"){
507             pad = {left: pad, right:pad, top:pad, bottom:pad};
508         }
509         pad = pad || this.defaultPadding;
510         var b = Roo.get(this.getEl()).getBox();
511         var ce = Roo.get(constrainTo);
512         var s = ce.getScroll();
513         var c, cd = ce.dom;
514         if(cd == document.body){
515             c = { x: s.left, y: s.top, width: Roo.lib.Dom.getViewWidth(), height: Roo.lib.Dom.getViewHeight()};
516         }else{
517             xy = ce.getXY();
518             c = {x : xy[0]+s.left, y: xy[1]+s.top, width: cd.clientWidth, height: cd.clientHeight};
519         }
520
521
522         var topSpace = b.y - c.y;
523         var leftSpace = b.x - c.x;
524
525         this.resetConstraints();
526         this.setXConstraint(leftSpace - (pad.left||0), // left
527                 c.width - leftSpace - b.width - (pad.right||0) //right
528         );
529         this.setYConstraint(topSpace - (pad.top||0), //top
530                 c.height - topSpace - b.height - (pad.bottom||0) //bottom
531         );
532     },
533
534     /**
535      * Returns a reference to the linked element
536      * @method getEl
537      * @return {HTMLElement} the html element
538      */
539     getEl: function() {
540         if (!this._domRef) {
541             this._domRef = Roo.getDom(this.id);
542         }
543
544         return this._domRef;
545     },
546
547     /**
548      * Returns a reference to the actual element to drag.  By default this is
549      * the same as the html element, but it can be assigned to another
550      * element. An example of this can be found in Roo.dd.DDProxy
551      * @method getDragEl
552      * @return {HTMLElement} the html element
553      */
554     getDragEl: function() {
555         return Roo.getDom(this.dragElId);
556     },
557
558     /**
559      * Sets up the DragDrop object.  Must be called in the constructor of any
560      * Roo.dd.DragDrop subclass
561      * @method init
562      * @param id the id of the linked element
563      * @param {String} sGroup the group of related items
564      * @param {object} config configuration attributes
565      */
566     init: function(id, sGroup, config) {
567         this.initTarget(id, sGroup, config);
568         Event.on(this.id, "mousedown", this.handleMouseDown, this);
569         // Event.on(this.id, "selectstart", Event.preventDefault);
570     },
571
572     /**
573      * Initializes Targeting functionality only... the object does not
574      * get a mousedown handler.
575      * @method initTarget
576      * @param id the id of the linked element
577      * @param {String} sGroup the group of related items
578      * @param {object} config configuration attributes
579      */
580     initTarget: function(id, sGroup, config) {
581
582         // configuration attributes
583         this.config = config || {};
584
585         // create a local reference to the drag and drop manager
586         this.DDM = Roo.dd.DDM;
587         // initialize the groups array
588         this.groups = {};
589
590         // assume that we have an element reference instead of an id if the
591         // parameter is not a string
592         if (typeof id !== "string") {
593             id = Roo.id(id);
594         }
595
596         // set the id
597         this.id = id;
598
599         // add to an interaction group
600         this.addToGroup((sGroup) ? sGroup : "default");
601
602         // We don't want to register this as the handle with the manager
603         // so we just set the id rather than calling the setter.
604         this.handleElId = id;
605
606         // the linked element is the element that gets dragged by default
607         this.setDragElId(id);
608
609         // by default, clicked anchors will not start drag operations.
610         this.invalidHandleTypes = { A: "A" };
611         this.invalidHandleIds = {};
612         this.invalidHandleClasses = [];
613
614         this.applyConfig();
615
616         this.handleOnAvailable();
617     },
618
619     /**
620      * Applies the configuration parameters that were passed into the constructor.
621      * This is supposed to happen at each level through the inheritance chain.  So
622      * a DDProxy implentation will execute apply config on DDProxy, DD, and
623      * DragDrop in order to get all of the parameters that are available in
624      * each object.
625      * @method applyConfig
626      */
627     applyConfig: function() {
628
629         // configurable properties:
630         //    padding, isTarget, maintainOffset, primaryButtonOnly
631         this.padding           = this.config.padding || [0, 0, 0, 0];
632         this.isTarget          = (this.config.isTarget !== false);
633         this.maintainOffset    = (this.config.maintainOffset);
634         this.primaryButtonOnly = (this.config.primaryButtonOnly !== false);
635
636     },
637
638     /**
639      * Executed when the linked element is available
640      * @method handleOnAvailable
641      * @private
642      */
643     handleOnAvailable: function() {
644         this.available = true;
645         this.resetConstraints();
646         this.onAvailable();
647     },
648
649      /**
650      * Configures the padding for the target zone in px.  Effectively expands
651      * (or reduces) the virtual object size for targeting calculations.
652      * Supports css-style shorthand; if only one parameter is passed, all sides
653      * will have that padding, and if only two are passed, the top and bottom
654      * will have the first param, the left and right the second.
655      * @method setPadding
656      * @param {int} iTop    Top pad
657      * @param {int} iRight  Right pad
658      * @param {int} iBot    Bot pad
659      * @param {int} iLeft   Left pad
660      */
661     setPadding: function(iTop, iRight, iBot, iLeft) {
662         // this.padding = [iLeft, iRight, iTop, iBot];
663         if (!iRight && 0 !== iRight) {
664             this.padding = [iTop, iTop, iTop, iTop];
665         } else if (!iBot && 0 !== iBot) {
666             this.padding = [iTop, iRight, iTop, iRight];
667         } else {
668             this.padding = [iTop, iRight, iBot, iLeft];
669         }
670     },
671
672     /**
673      * Stores the initial placement of the linked element.
674      * @method setInitialPosition
675      * @param {int} diffX   the X offset, default 0
676      * @param {int} diffY   the Y offset, default 0
677      */
678     setInitPosition: function(diffX, diffY) {
679         var el = this.getEl();
680
681         if (!this.DDM.verifyEl(el)) {
682             return;
683         }
684
685         var dx = diffX || 0;
686         var dy = diffY || 0;
687
688         var p = Dom.getXY( el );
689
690         this.initPageX = p[0] - dx;
691         this.initPageY = p[1] - dy;
692
693         this.lastPageX = p[0];
694         this.lastPageY = p[1];
695
696
697         this.setStartPosition(p);
698     },
699
700     /**
701      * Sets the start position of the element.  This is set when the obj
702      * is initialized, the reset when a drag is started.
703      * @method setStartPosition
704      * @param pos current position (from previous lookup)
705      * @private
706      */
707     setStartPosition: function(pos) {
708         var p = pos || Dom.getXY( this.getEl() );
709         this.deltaSetXY = null;
710
711         this.startPageX = p[0];
712         this.startPageY = p[1];
713     },
714
715     /**
716      * Add this instance to a group of related drag/drop objects.  All
717      * instances belong to at least one group, and can belong to as many
718      * groups as needed.
719      * @method addToGroup
720      * @param sGroup {string} the name of the group
721      */
722     addToGroup: function(sGroup) {
723         this.groups[sGroup] = true;
724         this.DDM.regDragDrop(this, sGroup);
725     },
726
727     /**
728      * Remove's this instance from the supplied interaction group
729      * @method removeFromGroup
730      * @param {string}  sGroup  The group to drop
731      */
732     removeFromGroup: function(sGroup) {
733         if (this.groups[sGroup]) {
734             delete this.groups[sGroup];
735         }
736
737         this.DDM.removeDDFromGroup(this, sGroup);
738     },
739
740     /**
741      * Allows you to specify that an element other than the linked element
742      * will be moved with the cursor during a drag
743      * @method setDragElId
744      * @param id {string} the id of the element that will be used to initiate the drag
745      */
746     setDragElId: function(id) {
747         this.dragElId = id;
748     },
749
750     /**
751      * Allows you to specify a child of the linked element that should be
752      * used to initiate the drag operation.  An example of this would be if
753      * you have a content div with text and links.  Clicking anywhere in the
754      * content area would normally start the drag operation.  Use this method
755      * to specify that an element inside of the content div is the element
756      * that starts the drag operation.
757      * @method setHandleElId
758      * @param id {string} the id of the element that will be used to
759      * initiate the drag.
760      */
761     setHandleElId: function(id) {
762         if (typeof id !== "string") {
763             id = Roo.id(id);
764         }
765         this.handleElId = id;
766         this.DDM.regHandle(this.id, id);
767     },
768
769     /**
770      * Allows you to set an element outside of the linked element as a drag
771      * handle
772      * @method setOuterHandleElId
773      * @param id the id of the element that will be used to initiate the drag
774      */
775     setOuterHandleElId: function(id) {
776         if (typeof id !== "string") {
777             id = Roo.id(id);
778         }
779         Event.on(id, "mousedown",
780                 this.handleMouseDown, this);
781         this.setHandleElId(id);
782
783         this.hasOuterHandles = true;
784     },
785
786     /**
787      * Remove all drag and drop hooks for this element
788      * @method unreg
789      */
790     unreg: function() {
791         Event.un(this.id, "mousedown",
792                 this.handleMouseDown);
793         this._domRef = null;
794         this.DDM._remove(this);
795     },
796
797     destroy : function(){
798         this.unreg();
799     },
800
801     /**
802      * Returns true if this instance is locked, or the drag drop mgr is locked
803      * (meaning that all drag/drop is disabled on the page.)
804      * @method isLocked
805      * @return {boolean} true if this obj or all drag/drop is locked, else
806      * false
807      */
808     isLocked: function() {
809         return (this.DDM.isLocked() || this.locked);
810     },
811
812     /**
813      * Fired when this object is clicked
814      * @method handleMouseDown
815      * @param {Event} e
816      * @param {Roo.dd.DragDrop} oDD the clicked dd object (this dd obj)
817      * @private
818      */
819     handleMouseDown: function(e, oDD){
820         if (this.primaryButtonOnly && e.button != 0) {
821             return;
822         }
823
824         if (this.isLocked()) {
825             return;
826         }
827
828         this.DDM.refreshCache(this.groups);
829
830         var pt = new Roo.lib.Point(Roo.lib.Event.getPageX(e), Roo.lib.Event.getPageY(e));
831         if (!this.hasOuterHandles && !this.DDM.isOverTarget(pt, this) )  {
832         } else {
833             if (this.clickValidator(e)) {
834
835                 // set the initial element position
836                 this.setStartPosition();
837
838
839                 this.b4MouseDown(e);
840                 this.onMouseDown(e);
841
842                 this.DDM.handleMouseDown(e, this);
843
844                 this.DDM.stopEvent(e);
845             } else {
846
847
848             }
849         }
850     },
851
852     clickValidator: function(e) {
853         var target = e.getTarget();
854         return ( this.isValidHandleChild(target) &&
855                     (this.id == this.handleElId ||
856                         this.DDM.handleWasClicked(target, this.id)) );
857     },
858
859     /**
860      * Allows you to specify a tag name that should not start a drag operation
861      * when clicked.  This is designed to facilitate embedding links within a
862      * drag handle that do something other than start the drag.
863      * @method addInvalidHandleType
864      * @param {string} tagName the type of element to exclude
865      */
866     addInvalidHandleType: function(tagName) {
867         var type = tagName.toUpperCase();
868         this.invalidHandleTypes[type] = type;
869     },
870
871     /**
872      * Lets you to specify an element id for a child of a drag handle
873      * that should not initiate a drag
874      * @method addInvalidHandleId
875      * @param {string} id the element id of the element you wish to ignore
876      */
877     addInvalidHandleId: function(id) {
878         if (typeof id !== "string") {
879             id = Roo.id(id);
880         }
881         this.invalidHandleIds[id] = id;
882     },
883
884     /**
885      * Lets you specify a css class of elements that will not initiate a drag
886      * @method addInvalidHandleClass
887      * @param {string} cssClass the class of the elements you wish to ignore
888      */
889     addInvalidHandleClass: function(cssClass) {
890         this.invalidHandleClasses.push(cssClass);
891     },
892
893     /**
894      * Unsets an excluded tag name set by addInvalidHandleType
895      * @method removeInvalidHandleType
896      * @param {string} tagName the type of element to unexclude
897      */
898     removeInvalidHandleType: function(tagName) {
899         var type = tagName.toUpperCase();
900         // this.invalidHandleTypes[type] = null;
901         delete this.invalidHandleTypes[type];
902     },
903
904     /**
905      * Unsets an invalid handle id
906      * @method removeInvalidHandleId
907      * @param {string} id the id of the element to re-enable
908      */
909     removeInvalidHandleId: function(id) {
910         if (typeof id !== "string") {
911             id = Roo.id(id);
912         }
913         delete this.invalidHandleIds[id];
914     },
915
916     /**
917      * Unsets an invalid css class
918      * @method removeInvalidHandleClass
919      * @param {string} cssClass the class of the element(s) you wish to
920      * re-enable
921      */
922     removeInvalidHandleClass: function(cssClass) {
923         for (var i=0, len=this.invalidHandleClasses.length; i<len; ++i) {
924             if (this.invalidHandleClasses[i] == cssClass) {
925                 delete this.invalidHandleClasses[i];
926             }
927         }
928     },
929
930     /**
931      * Checks the tag exclusion list to see if this click should be ignored
932      * @method isValidHandleChild
933      * @param {HTMLElement} node the HTMLElement to evaluate
934      * @return {boolean} true if this is a valid tag type, false if not
935      */
936     isValidHandleChild: function(node) {
937
938         var valid = true;
939         // var n = (node.nodeName == "#text") ? node.parentNode : node;
940         var nodeName;
941         try {
942             nodeName = node.nodeName.toUpperCase();
943         } catch(e) {
944             nodeName = node.nodeName;
945         }
946         valid = valid && !this.invalidHandleTypes[nodeName];
947         valid = valid && !this.invalidHandleIds[node.id];
948
949         for (var i=0, len=this.invalidHandleClasses.length; valid && i<len; ++i) {
950             valid = !Dom.hasClass(node, this.invalidHandleClasses[i]);
951         }
952
953
954         return valid;
955
956     },
957
958     /**
959      * Create the array of horizontal tick marks if an interval was specified
960      * in setXConstraint().
961      * @method setXTicks
962      * @private
963      */
964     setXTicks: function(iStartX, iTickSize) {
965         this.xTicks = [];
966         this.xTickSize = iTickSize;
967
968         var tickMap = {};
969
970         for (var i = this.initPageX; i >= this.minX; i = i - iTickSize) {
971             if (!tickMap[i]) {
972                 this.xTicks[this.xTicks.length] = i;
973                 tickMap[i] = true;
974             }
975         }
976
977         for (i = this.initPageX; i <= this.maxX; i = i + iTickSize) {
978             if (!tickMap[i]) {
979                 this.xTicks[this.xTicks.length] = i;
980                 tickMap[i] = true;
981             }
982         }
983
984         this.xTicks.sort(this.DDM.numericSort) ;
985     },
986
987     /**
988      * Create the array of vertical tick marks if an interval was specified in
989      * setYConstraint().
990      * @method setYTicks
991      * @private
992      */
993     setYTicks: function(iStartY, iTickSize) {
994         this.yTicks = [];
995         this.yTickSize = iTickSize;
996
997         var tickMap = {};
998
999         for (var i = this.initPageY; i >= this.minY; i = i - iTickSize) {
1000             if (!tickMap[i]) {
1001                 this.yTicks[this.yTicks.length] = i;
1002                 tickMap[i] = true;
1003             }
1004         }
1005
1006         for (i = this.initPageY; i <= this.maxY; i = i + iTickSize) {
1007             if (!tickMap[i]) {
1008                 this.yTicks[this.yTicks.length] = i;
1009                 tickMap[i] = true;
1010             }
1011         }
1012
1013         this.yTicks.sort(this.DDM.numericSort) ;
1014     },
1015
1016     /**
1017      * By default, the element can be dragged any place on the screen.  Use
1018      * this method to limit the horizontal travel of the element.  Pass in
1019      * 0,0 for the parameters if you want to lock the drag to the y axis.
1020      * @method setXConstraint
1021      * @param {int} iLeft the number of pixels the element can move to the left
1022      * @param {int} iRight the number of pixels the element can move to the
1023      * right
1024      * @param {int} iTickSize optional parameter for specifying that the
1025      * element
1026      * should move iTickSize pixels at a time.
1027      */
1028     setXConstraint: function(iLeft, iRight, iTickSize) {
1029         this.leftConstraint = iLeft;
1030         this.rightConstraint = iRight;
1031
1032         this.minX = this.initPageX - iLeft;
1033         this.maxX = this.initPageX + iRight;
1034         if (iTickSize) { this.setXTicks(this.initPageX, iTickSize); }
1035
1036         this.constrainX = true;
1037     },
1038
1039     /**
1040      * Clears any constraints applied to this instance.  Also clears ticks
1041      * since they can't exist independent of a constraint at this time.
1042      * @method clearConstraints
1043      */
1044     clearConstraints: function() {
1045         this.constrainX = false;
1046         this.constrainY = false;
1047         this.clearTicks();
1048     },
1049
1050     /**
1051      * Clears any tick interval defined for this instance
1052      * @method clearTicks
1053      */
1054     clearTicks: function() {
1055         this.xTicks = null;
1056         this.yTicks = null;
1057         this.xTickSize = 0;
1058         this.yTickSize = 0;
1059     },
1060
1061     /**
1062      * By default, the element can be dragged any place on the screen.  Set
1063      * this to limit the vertical travel of the element.  Pass in 0,0 for the
1064      * parameters if you want to lock the drag to the x axis.
1065      * @method setYConstraint
1066      * @param {int} iUp the number of pixels the element can move up
1067      * @param {int} iDown the number of pixels the element can move down
1068      * @param {int} iTickSize optional parameter for specifying that the
1069      * element should move iTickSize pixels at a time.
1070      */
1071     setYConstraint: function(iUp, iDown, iTickSize) {
1072         this.topConstraint = iUp;
1073         this.bottomConstraint = iDown;
1074
1075         this.minY = this.initPageY - iUp;
1076         this.maxY = this.initPageY + iDown;
1077         if (iTickSize) { this.setYTicks(this.initPageY, iTickSize); }
1078
1079         this.constrainY = true;
1080
1081     },
1082
1083     /**
1084      * resetConstraints must be called if you manually reposition a dd element.
1085      * @method resetConstraints
1086      * @param {boolean} maintainOffset
1087      */
1088     resetConstraints: function() {
1089
1090
1091         // Maintain offsets if necessary
1092         if (this.initPageX || this.initPageX === 0) {
1093             // figure out how much this thing has moved
1094             var dx = (this.maintainOffset) ? this.lastPageX - this.initPageX : 0;
1095             var dy = (this.maintainOffset) ? this.lastPageY - this.initPageY : 0;
1096
1097             this.setInitPosition(dx, dy);
1098
1099         // This is the first time we have detected the element's position
1100         } else {
1101             this.setInitPosition();
1102         }
1103
1104         if (this.constrainX) {
1105             this.setXConstraint( this.leftConstraint,
1106                                  this.rightConstraint,
1107                                  this.xTickSize        );
1108         }
1109
1110         if (this.constrainY) {
1111             this.setYConstraint( this.topConstraint,
1112                                  this.bottomConstraint,
1113                                  this.yTickSize         );
1114         }
1115     },
1116
1117     /**
1118      * Normally the drag element is moved pixel by pixel, but we can specify
1119      * that it move a number of pixels at a time.  This method resolves the
1120      * location when we have it set up like this.
1121      * @method getTick
1122      * @param {int} val where we want to place the object
1123      * @param {int[]} tickArray sorted array of valid points
1124      * @return {int} the closest tick
1125      * @private
1126      */
1127     getTick: function(val, tickArray) {
1128
1129         if (!tickArray) {
1130             // If tick interval is not defined, it is effectively 1 pixel,
1131             // so we return the value passed to us.
1132             return val;
1133         } else if (tickArray[0] >= val) {
1134             // The value is lower than the first tick, so we return the first
1135             // tick.
1136             return tickArray[0];
1137         } else {
1138             for (var i=0, len=tickArray.length; i<len; ++i) {
1139                 var next = i + 1;
1140                 if (tickArray[next] && tickArray[next] >= val) {
1141                     var diff1 = val - tickArray[i];
1142                     var diff2 = tickArray[next] - val;
1143                     return (diff2 > diff1) ? tickArray[i] : tickArray[next];
1144                 }
1145             }
1146
1147             // The value is larger than the last tick, so we return the last
1148             // tick.
1149             return tickArray[tickArray.length - 1];
1150         }
1151     },
1152
1153     /**
1154      * toString method
1155      * @method toString
1156      * @return {string} string representation of the dd obj
1157      */
1158     toString: function() {
1159         return ("DragDrop " + this.id);
1160     }
1161
1162 });
1163
1164 })();
1165 /*
1166  * Based on:
1167  * Ext JS Library 1.1.1
1168  * Copyright(c) 2006-2007, Ext JS, LLC.
1169  *
1170  * Originally Released Under LGPL - original licence link has changed is not relivant.
1171  *
1172  * Fork - LGPL
1173  * <script type="text/javascript">
1174  */
1175
1176
1177 /**
1178  * The drag and drop utility provides a framework for building drag and drop
1179  * applications.  In addition to enabling drag and drop for specific elements,
1180  * the drag and drop elements are tracked by the manager class, and the
1181  * interactions between the various elements are tracked during the drag and
1182  * the implementing code is notified about these important moments.
1183  */
1184
1185 // Only load the library once.  Rewriting the manager class would orphan
1186 // existing drag and drop instances.
1187 if (!Roo.dd.DragDropMgr) {
1188
1189 /**
1190  * @class Roo.dd.DragDropMgr
1191  * DragDropMgr is a singleton that tracks the element interaction for
1192  * all DragDrop items in the window.  Generally, you will not call
1193  * this class directly, but it does have helper methods that could
1194  * be useful in your DragDrop implementations.
1195  * @singleton
1196  */
1197 Roo.dd.DragDropMgr = function() {
1198
1199     var Event = Roo.EventManager;
1200
1201     return {
1202
1203         /**
1204          * Two dimensional Array of registered DragDrop objects.  The first
1205          * dimension is the DragDrop item group, the second the DragDrop
1206          * object.
1207          * @property ids
1208          * @type {string: string}
1209          * @private
1210          * @static
1211          */
1212         ids: {},
1213
1214         /**
1215          * Array of element ids defined as drag handles.  Used to determine
1216          * if the element that generated the mousedown event is actually the
1217          * handle and not the html element itself.
1218          * @property handleIds
1219          * @type {string: string}
1220          * @private
1221          * @static
1222          */
1223         handleIds: {},
1224
1225         /**
1226          * the DragDrop object that is currently being dragged
1227          * @property dragCurrent
1228          * @type DragDrop
1229          * @private
1230          * @static
1231          **/
1232         dragCurrent: null,
1233
1234         /**
1235          * the DragDrop object(s) that are being hovered over
1236          * @property dragOvers
1237          * @type Array
1238          * @private
1239          * @static
1240          */
1241         dragOvers: {},
1242
1243         /**
1244          * the X distance between the cursor and the object being dragged
1245          * @property deltaX
1246          * @type int
1247          * @private
1248          * @static
1249          */
1250         deltaX: 0,
1251
1252         /**
1253          * the Y distance between the cursor and the object being dragged
1254          * @property deltaY
1255          * @type int
1256          * @private
1257          * @static
1258          */
1259         deltaY: 0,
1260
1261         /**
1262          * Flag to determine if we should prevent the default behavior of the
1263          * events we define. By default this is true, but this can be set to
1264          * false if you need the default behavior (not recommended)
1265          * @property preventDefault
1266          * @type boolean
1267          * @static
1268          */
1269         preventDefault: true,
1270
1271         /**
1272          * Flag to determine if we should stop the propagation of the events
1273          * we generate. This is true by default but you may want to set it to
1274          * false if the html element contains other features that require the
1275          * mouse click.
1276          * @property stopPropagation
1277          * @type boolean
1278          * @static
1279          */
1280         stopPropagation: true,
1281
1282         /**
1283          * Internal flag that is set to true when drag and drop has been
1284          * intialized
1285          * @property initialized
1286          * @private
1287          * @static
1288          */
1289         initalized: false,
1290
1291         /**
1292          * All drag and drop can be disabled.
1293          * @property locked
1294          * @private
1295          * @static
1296          */
1297         locked: false,
1298
1299         /**
1300          * Called the first time an element is registered.
1301          * @method init
1302          * @private
1303          * @static
1304          */
1305         init: function() {
1306             this.initialized = true;
1307         },
1308
1309         /**
1310          * In point mode, drag and drop interaction is defined by the
1311          * location of the cursor during the drag/drop
1312          * @property POINT
1313          * @type int
1314          * @static
1315          */
1316         POINT: 0,
1317
1318         /**
1319          * In intersect mode, drag and drop interactio nis defined by the
1320          * overlap of two or more drag and drop objects.
1321          * @property INTERSECT
1322          * @type int
1323          * @static
1324          */
1325         INTERSECT: 1,
1326
1327         /**
1328          * The current drag and drop mode.  Default: POINT
1329          * @property mode
1330          * @type int
1331          * @static
1332          */
1333         mode: 0,
1334
1335         /**
1336          * Runs method on all drag and drop objects
1337          * @method _execOnAll
1338          * @private
1339          * @static
1340          */
1341         _execOnAll: function(sMethod, args) {
1342             for (var i in this.ids) {
1343                 for (var j in this.ids[i]) {
1344                     var oDD = this.ids[i][j];
1345                     if (! this.isTypeOfDD(oDD)) {
1346                         continue;
1347                     }
1348                     oDD[sMethod].apply(oDD, args);
1349                 }
1350             }
1351         },
1352
1353         /**
1354          * Drag and drop initialization.  Sets up the global event handlers
1355          * @method _onLoad
1356          * @private
1357          * @static
1358          */
1359         _onLoad: function() {
1360
1361             this.init();
1362
1363
1364             Event.on(document, "mouseup",   this.handleMouseUp, this, true);
1365             Event.on(document, "mousemove", this.handleMouseMove, this, true);
1366             Event.on(window,   "unload",    this._onUnload, this, true);
1367             Event.on(window,   "resize",    this._onResize, this, true);
1368             // Event.on(window,   "mouseout",    this._test);
1369
1370         },
1371
1372         /**
1373          * Reset constraints on all drag and drop objs
1374          * @method _onResize
1375          * @private
1376          * @static
1377          */
1378         _onResize: function(e) {
1379             this._execOnAll("resetConstraints", []);
1380         },
1381
1382         /**
1383          * Lock all drag and drop functionality
1384          * @method lock
1385          * @static
1386          */
1387         lock: function() { this.locked = true; },
1388
1389         /**
1390          * Unlock all drag and drop functionality
1391          * @method unlock
1392          * @static
1393          */
1394         unlock: function() { this.locked = false; },
1395
1396         /**
1397          * Is drag and drop locked?
1398          * @method isLocked
1399          * @return {boolean} True if drag and drop is locked, false otherwise.
1400          * @static
1401          */
1402         isLocked: function() { return this.locked; },
1403
1404         /**
1405          * Location cache that is set for all drag drop objects when a drag is
1406          * initiated, cleared when the drag is finished.
1407          * @property locationCache
1408          * @private
1409          * @static
1410          */
1411         locationCache: {},
1412
1413         /**
1414          * Set useCache to false if you want to force object the lookup of each
1415          * drag and drop linked element constantly during a drag.
1416          * @property useCache
1417          * @type boolean
1418          * @static
1419          */
1420         useCache: true,
1421
1422         /**
1423          * The number of pixels that the mouse needs to move after the
1424          * mousedown before the drag is initiated.  Default=3;
1425          * @property clickPixelThresh
1426          * @type int
1427          * @static
1428          */
1429         clickPixelThresh: 3,
1430
1431         /**
1432          * The number of milliseconds after the mousedown event to initiate the
1433          * drag if we don't get a mouseup event. Default=1000
1434          * @property clickTimeThresh
1435          * @type int
1436          * @static
1437          */
1438         clickTimeThresh: 350,
1439
1440         /**
1441          * Flag that indicates that either the drag pixel threshold or the
1442          * mousdown time threshold has been met
1443          * @property dragThreshMet
1444          * @type boolean
1445          * @private
1446          * @static
1447          */
1448         dragThreshMet: false,
1449
1450         /**
1451          * Timeout used for the click time threshold
1452          * @property clickTimeout
1453          * @type Object
1454          * @private
1455          * @static
1456          */
1457         clickTimeout: null,
1458
1459         /**
1460          * The X position of the mousedown event stored for later use when a
1461          * drag threshold is met.
1462          * @property startX
1463          * @type int
1464          * @private
1465          * @static
1466          */
1467         startX: 0,
1468
1469         /**
1470          * The Y position of the mousedown event stored for later use when a
1471          * drag threshold is met.
1472          * @property startY
1473          * @type int
1474          * @private
1475          * @static
1476          */
1477         startY: 0,
1478
1479         /**
1480          * Each DragDrop instance must be registered with the DragDropMgr.
1481          * This is executed in DragDrop.init()
1482          * @method regDragDrop
1483          * @param {DragDrop} oDD the DragDrop object to register
1484          * @param {String} sGroup the name of the group this element belongs to
1485          * @static
1486          */
1487         regDragDrop: function(oDD, sGroup) {
1488             if (!this.initialized) { this.init(); }
1489
1490             if (!this.ids[sGroup]) {
1491                 this.ids[sGroup] = {};
1492             }
1493             this.ids[sGroup][oDD.id] = oDD;
1494         },
1495
1496         /**
1497          * Removes the supplied dd instance from the supplied group. Executed
1498          * by DragDrop.removeFromGroup, so don't call this function directly.
1499          * @method removeDDFromGroup
1500          * @private
1501          * @static
1502          */
1503         removeDDFromGroup: function(oDD, sGroup) {
1504             if (!this.ids[sGroup]) {
1505                 this.ids[sGroup] = {};
1506             }
1507
1508             var obj = this.ids[sGroup];
1509             if (obj && obj[oDD.id]) {
1510                 delete obj[oDD.id];
1511             }
1512         },
1513
1514         /**
1515          * Unregisters a drag and drop item.  This is executed in
1516          * DragDrop.unreg, use that method instead of calling this directly.
1517          * @method _remove
1518          * @private
1519          * @static
1520          */
1521         _remove: function(oDD) {
1522             for (var g in oDD.groups) {
1523                 if (g && this.ids[g][oDD.id]) {
1524                     delete this.ids[g][oDD.id];
1525                 }
1526             }
1527             delete this.handleIds[oDD.id];
1528         },
1529
1530         /**
1531          * Each DragDrop handle element must be registered.  This is done
1532          * automatically when executing DragDrop.setHandleElId()
1533          * @method regHandle
1534          * @param {String} sDDId the DragDrop id this element is a handle for
1535          * @param {String} sHandleId the id of the element that is the drag
1536          * handle
1537          * @static
1538          */
1539         regHandle: function(sDDId, sHandleId) {
1540             if (!this.handleIds[sDDId]) {
1541                 this.handleIds[sDDId] = {};
1542             }
1543             this.handleIds[sDDId][sHandleId] = sHandleId;
1544         },
1545
1546         /**
1547          * Utility function to determine if a given element has been
1548          * registered as a drag drop item.
1549          * @method isDragDrop
1550          * @param {String} id the element id to check
1551          * @return {boolean} true if this element is a DragDrop item,
1552          * false otherwise
1553          * @static
1554          */
1555         isDragDrop: function(id) {
1556             return ( this.getDDById(id) ) ? true : false;
1557         },
1558
1559         /**
1560          * Returns the drag and drop instances that are in all groups the
1561          * passed in instance belongs to.
1562          * @method getRelated
1563          * @param {DragDrop} p_oDD the obj to get related data for
1564          * @param {boolean} bTargetsOnly if true, only return targetable objs
1565          * @return {DragDrop[]} the related instances
1566          * @static
1567          */
1568         getRelated: function(p_oDD, bTargetsOnly) {
1569             var oDDs = [];
1570             for (var i in p_oDD.groups) {
1571                 for (j in this.ids[i]) {
1572                     var dd = this.ids[i][j];
1573                     if (! this.isTypeOfDD(dd)) {
1574                         continue;
1575                     }
1576                     if (!bTargetsOnly || dd.isTarget) {
1577                         oDDs[oDDs.length] = dd;
1578                     }
1579                 }
1580             }
1581
1582             return oDDs;
1583         },
1584
1585         /**
1586          * Returns true if the specified dd target is a legal target for
1587          * the specifice drag obj
1588          * @method isLegalTarget
1589          * @param {DragDrop} the drag obj
1590          * @param {DragDrop} the target
1591          * @return {boolean} true if the target is a legal target for the
1592          * dd obj
1593          * @static
1594          */
1595         isLegalTarget: function (oDD, oTargetDD) {
1596             var targets = this.getRelated(oDD, true);
1597             for (var i=0, len=targets.length;i<len;++i) {
1598                 if (targets[i].id == oTargetDD.id) {
1599                     return true;
1600                 }
1601             }
1602
1603             return false;
1604         },
1605
1606         /**
1607          * My goal is to be able to transparently determine if an object is
1608          * typeof DragDrop, and the exact subclass of DragDrop.  typeof
1609          * returns "object", oDD.constructor.toString() always returns
1610          * "DragDrop" and not the name of the subclass.  So for now it just
1611          * evaluates a well-known variable in DragDrop.
1612          * @method isTypeOfDD
1613          * @param {Object} the object to evaluate
1614          * @return {boolean} true if typeof oDD = DragDrop
1615          * @static
1616          */
1617         isTypeOfDD: function (oDD) {
1618             return (oDD && oDD.__ygDragDrop);
1619         },
1620
1621         /**
1622          * Utility function to determine if a given element has been
1623          * registered as a drag drop handle for the given Drag Drop object.
1624          * @method isHandle
1625          * @param {String} id the element id to check
1626          * @return {boolean} true if this element is a DragDrop handle, false
1627          * otherwise
1628          * @static
1629          */
1630         isHandle: function(sDDId, sHandleId) {
1631             return ( this.handleIds[sDDId] &&
1632                             this.handleIds[sDDId][sHandleId] );
1633         },
1634
1635         /**
1636          * Returns the DragDrop instance for a given id
1637          * @method getDDById
1638          * @param {String} id the id of the DragDrop object
1639          * @return {DragDrop} the drag drop object, null if it is not found
1640          * @static
1641          */
1642         getDDById: function(id) {
1643             for (var i in this.ids) {
1644                 if (this.ids[i][id]) {
1645                     return this.ids[i][id];
1646                 }
1647             }
1648             return null;
1649         },
1650
1651         /**
1652          * Fired after a registered DragDrop object gets the mousedown event.
1653          * Sets up the events required to track the object being dragged
1654          * @method handleMouseDown
1655          * @param {Event} e the event
1656          * @param oDD the DragDrop object being dragged
1657          * @private
1658          * @static
1659          */
1660         handleMouseDown: function(e, oDD) {
1661             if(Roo.QuickTips){
1662                 Roo.QuickTips.disable();
1663             }
1664             this.currentTarget = e.getTarget();
1665
1666             this.dragCurrent = oDD;
1667
1668             var el = oDD.getEl();
1669
1670             // track start position
1671             this.startX = e.getPageX();
1672             this.startY = e.getPageY();
1673
1674             this.deltaX = this.startX - el.offsetLeft;
1675             this.deltaY = this.startY - el.offsetTop;
1676
1677             this.dragThreshMet = false;
1678
1679             this.clickTimeout = setTimeout(
1680                     function() {
1681                         var DDM = Roo.dd.DDM;
1682                         DDM.startDrag(DDM.startX, DDM.startY);
1683                     },
1684                     this.clickTimeThresh );
1685         },
1686
1687         /**
1688          * Fired when either the drag pixel threshol or the mousedown hold
1689          * time threshold has been met.
1690          * @method startDrag
1691          * @param x {int} the X position of the original mousedown
1692          * @param y {int} the Y position of the original mousedown
1693          * @static
1694          */
1695         startDrag: function(x, y) {
1696             clearTimeout(this.clickTimeout);
1697             if (this.dragCurrent) {
1698                 this.dragCurrent.b4StartDrag(x, y);
1699                 this.dragCurrent.startDrag(x, y);
1700             }
1701             this.dragThreshMet = true;
1702         },
1703
1704         /**
1705          * Internal function to handle the mouseup event.  Will be invoked
1706          * from the context of the document.
1707          * @method handleMouseUp
1708          * @param {Event} e the event
1709          * @private
1710          * @static
1711          */
1712         handleMouseUp: function(e) {
1713
1714             if(Roo.QuickTips){
1715                 Roo.QuickTips.enable();
1716             }
1717             if (! this.dragCurrent) {
1718                 return;
1719             }
1720
1721             clearTimeout(this.clickTimeout);
1722
1723             if (this.dragThreshMet) {
1724                 this.fireEvents(e, true);
1725             } else {
1726             }
1727
1728             this.stopDrag(e);
1729
1730             this.stopEvent(e);
1731         },
1732
1733         /**
1734          * Utility to stop event propagation and event default, if these
1735          * features are turned on.
1736          * @method stopEvent
1737          * @param {Event} e the event as returned by this.getEvent()
1738          * @static
1739          */
1740         stopEvent: function(e){
1741             if(this.stopPropagation) {
1742                 e.stopPropagation();
1743             }
1744
1745             if (this.preventDefault) {
1746                 e.preventDefault();
1747             }
1748         },
1749
1750         /**
1751          * Internal function to clean up event handlers after the drag
1752          * operation is complete
1753          * @method stopDrag
1754          * @param {Event} e the event
1755          * @private
1756          * @static
1757          */
1758         stopDrag: function(e) {
1759             // Fire the drag end event for the item that was dragged
1760             if (this.dragCurrent) {
1761                 if (this.dragThreshMet) {
1762                     this.dragCurrent.b4EndDrag(e);
1763                     this.dragCurrent.endDrag(e);
1764                 }
1765
1766                 this.dragCurrent.onMouseUp(e);
1767             }
1768
1769             this.dragCurrent = null;
1770             this.dragOvers = {};
1771         },
1772
1773         /**
1774          * Internal function to handle the mousemove event.  Will be invoked
1775          * from the context of the html element.
1776          *
1777          * @TODO figure out what we can do about mouse events lost when the
1778          * user drags objects beyond the window boundary.  Currently we can
1779          * detect this in internet explorer by verifying that the mouse is
1780          * down during the mousemove event.  Firefox doesn't give us the
1781          * button state on the mousemove event.
1782          * @method handleMouseMove
1783          * @param {Event} e the event
1784          * @private
1785          * @static
1786          */
1787         handleMouseMove: function(e) {
1788             if (! this.dragCurrent) {
1789                 return true;
1790             }
1791
1792             // var button = e.which || e.button;
1793
1794             // check for IE mouseup outside of page boundary
1795             if (Roo.isIE && (e.button !== 0 && e.button !== 1 && e.button !== 2)) {
1796                 this.stopEvent(e);
1797                 return this.handleMouseUp(e);
1798             }
1799
1800             if (!this.dragThreshMet) {
1801                 var diffX = Math.abs(this.startX - e.getPageX());
1802                 var diffY = Math.abs(this.startY - e.getPageY());
1803                 if (diffX > this.clickPixelThresh ||
1804                             diffY > this.clickPixelThresh) {
1805                     this.startDrag(this.startX, this.startY);
1806                 }
1807             }
1808
1809             if (this.dragThreshMet) {
1810                 this.dragCurrent.b4Drag(e);
1811                 this.dragCurrent.onDrag(e);
1812                 if(!this.dragCurrent.moveOnly){
1813                     this.fireEvents(e, false);
1814                 }
1815             }
1816
1817             this.stopEvent(e);
1818
1819             return true;
1820         },
1821
1822         /**
1823          * Iterates over all of the DragDrop elements to find ones we are
1824          * hovering over or dropping on
1825          * @method fireEvents
1826          * @param {Event} e the event
1827          * @param {boolean} isDrop is this a drop op or a mouseover op?
1828          * @private
1829          * @static
1830          */
1831         fireEvents: function(e, isDrop) {
1832             var dc = this.dragCurrent;
1833
1834             // If the user did the mouse up outside of the window, we could
1835             // get here even though we have ended the drag.
1836             if (!dc || dc.isLocked()) {
1837                 return;
1838             }
1839
1840             var pt = e.getPoint();
1841
1842             // cache the previous dragOver array
1843             var oldOvers = [];
1844
1845             var outEvts   = [];
1846             var overEvts  = [];
1847             var dropEvts  = [];
1848             var enterEvts = [];
1849
1850             // Check to see if the object(s) we were hovering over is no longer
1851             // being hovered over so we can fire the onDragOut event
1852             for (var i in this.dragOvers) {
1853
1854                 var ddo = this.dragOvers[i];
1855
1856                 if (! this.isTypeOfDD(ddo)) {
1857                     continue;
1858                 }
1859
1860                 if (! this.isOverTarget(pt, ddo, this.mode)) {
1861                     outEvts.push( ddo );
1862                 }
1863
1864                 oldOvers[i] = true;
1865                 delete this.dragOvers[i];
1866             }
1867
1868             for (var sGroup in dc.groups) {
1869
1870                 if ("string" != typeof sGroup) {
1871                     continue;
1872                 }
1873
1874                 for (i in this.ids[sGroup]) {
1875                     var oDD = this.ids[sGroup][i];
1876                     if (! this.isTypeOfDD(oDD)) {
1877                         continue;
1878                     }
1879
1880                     if (oDD.isTarget && !oDD.isLocked() && oDD != dc) {
1881                         if (this.isOverTarget(pt, oDD, this.mode)) {
1882                             // look for drop interactions
1883                             if (isDrop) {
1884                                 dropEvts.push( oDD );
1885                             // look for drag enter and drag over interactions
1886                             } else {
1887
1888                                 // initial drag over: dragEnter fires
1889                                 if (!oldOvers[oDD.id]) {
1890                                     enterEvts.push( oDD );
1891                                 // subsequent drag overs: dragOver fires
1892                                 } else {
1893                                     overEvts.push( oDD );
1894                                 }
1895
1896                                 this.dragOvers[oDD.id] = oDD;
1897                             }
1898                         }
1899                     }
1900                 }
1901             }
1902
1903             if (this.mode) {
1904                 if (outEvts.length) {
1905                     dc.b4DragOut(e, outEvts);
1906                     dc.onDragOut(e, outEvts);
1907                 }
1908
1909                 if (enterEvts.length) {
1910                     dc.onDragEnter(e, enterEvts);
1911                 }
1912
1913                 if (overEvts.length) {
1914                     dc.b4DragOver(e, overEvts);
1915                     dc.onDragOver(e, overEvts);
1916                 }
1917
1918                 if (dropEvts.length) {
1919                     dc.b4DragDrop(e, dropEvts);
1920                     dc.onDragDrop(e, dropEvts);
1921                 }
1922
1923             } else {
1924                 // fire dragout events
1925                 var len = 0;
1926                 for (i=0, len=outEvts.length; i<len; ++i) {
1927                     dc.b4DragOut(e, outEvts[i].id);
1928                     dc.onDragOut(e, outEvts[i].id);
1929                 }
1930
1931                 // fire enter events
1932                 for (i=0,len=enterEvts.length; i<len; ++i) {
1933                     // dc.b4DragEnter(e, oDD.id);
1934                     dc.onDragEnter(e, enterEvts[i].id);
1935                 }
1936
1937                 // fire over events
1938                 for (i=0,len=overEvts.length; i<len; ++i) {
1939                     dc.b4DragOver(e, overEvts[i].id);
1940                     dc.onDragOver(e, overEvts[i].id);
1941                 }
1942
1943                 // fire drop events
1944                 for (i=0, len=dropEvts.length; i<len; ++i) {
1945                     dc.b4DragDrop(e, dropEvts[i].id);
1946                     dc.onDragDrop(e, dropEvts[i].id);
1947                 }
1948
1949             }
1950
1951             // notify about a drop that did not find a target
1952             if (isDrop && !dropEvts.length) {
1953                 dc.onInvalidDrop(e);
1954             }
1955
1956         },
1957
1958         /**
1959          * Helper function for getting the best match from the list of drag
1960          * and drop objects returned by the drag and drop events when we are
1961          * in INTERSECT mode.  It returns either the first object that the
1962          * cursor is over, or the object that has the greatest overlap with
1963          * the dragged element.
1964          * @method getBestMatch
1965          * @param  {DragDrop[]} dds The array of drag and drop objects
1966          * targeted
1967          * @return {DragDrop}       The best single match
1968          * @static
1969          */
1970         getBestMatch: function(dds) {
1971             var winner = null;
1972             // Return null if the input is not what we expect
1973             //if (!dds || !dds.length || dds.length == 0) {
1974                // winner = null;
1975             // If there is only one item, it wins
1976             //} else if (dds.length == 1) {
1977
1978             var len = dds.length;
1979
1980             if (len == 1) {
1981                 winner = dds[0];
1982             } else {
1983                 // Loop through the targeted items
1984                 for (var i=0; i<len; ++i) {
1985                     var dd = dds[i];
1986                     // If the cursor is over the object, it wins.  If the
1987                     // cursor is over multiple matches, the first one we come
1988                     // to wins.
1989                     if (dd.cursorIsOver) {
1990                         winner = dd;
1991                         break;
1992                     // Otherwise the object with the most overlap wins
1993                     } else {
1994                         if (!winner ||
1995                             winner.overlap.getArea() < dd.overlap.getArea()) {
1996                             winner = dd;
1997                         }
1998                     }
1999                 }
2000             }
2001
2002             return winner;
2003         },
2004
2005         /**
2006          * Refreshes the cache of the top-left and bottom-right points of the
2007          * drag and drop objects in the specified group(s).  This is in the
2008          * format that is stored in the drag and drop instance, so typical
2009          * usage is:
2010          * <code>
2011          * Roo.dd.DragDropMgr.refreshCache(ddinstance.groups);
2012          * </code>
2013          * Alternatively:
2014          * <code>
2015          * Roo.dd.DragDropMgr.refreshCache({group1:true, group2:true});
2016          * </code>
2017          * @TODO this really should be an indexed array.  Alternatively this
2018          * method could accept both.
2019          * @method refreshCache
2020          * @param {Object} groups an associative array of groups to refresh
2021          * @static
2022          */
2023         refreshCache: function(groups) {
2024             for (var sGroup in groups) {
2025                 if ("string" != typeof sGroup) {
2026                     continue;
2027                 }
2028                 for (var i in this.ids[sGroup]) {
2029                     var oDD = this.ids[sGroup][i];
2030
2031                     if (this.isTypeOfDD(oDD)) {
2032                     // if (this.isTypeOfDD(oDD) && oDD.isTarget) {
2033                         var loc = this.getLocation(oDD);
2034                         if (loc) {
2035                             this.locationCache[oDD.id] = loc;
2036                         } else {
2037                             delete this.locationCache[oDD.id];
2038                             // this will unregister the drag and drop object if
2039                             // the element is not in a usable state
2040                             // oDD.unreg();
2041                         }
2042                     }
2043                 }
2044             }
2045         },
2046
2047         /**
2048          * This checks to make sure an element exists and is in the DOM.  The
2049          * main purpose is to handle cases where innerHTML is used to remove
2050          * drag and drop objects from the DOM.  IE provides an 'unspecified
2051          * error' when trying to access the offsetParent of such an element
2052          * @method verifyEl
2053          * @param {HTMLElement} el the element to check
2054          * @return {boolean} true if the element looks usable
2055          * @static
2056          */
2057         verifyEl: function(el) {
2058             if (el) {
2059                 var parent;
2060                 if(Roo.isIE){
2061                     try{
2062                         parent = el.offsetParent;
2063                     }catch(e){}
2064                 }else{
2065                     parent = el.offsetParent;
2066                 }
2067                 if (parent) {
2068                     return true;
2069                 }
2070             }
2071
2072             return false;
2073         },
2074
2075         /**
2076          * Returns a Region object containing the drag and drop element's position
2077          * and size, including the padding configured for it
2078          * @method getLocation
2079          * @param {DragDrop} oDD the drag and drop object to get the
2080          *                       location for
2081          * @return {Roo.lib.Region} a Region object representing the total area
2082          *                             the element occupies, including any padding
2083          *                             the instance is configured for.
2084          * @static
2085          */
2086         getLocation: function(oDD) {
2087             if (! this.isTypeOfDD(oDD)) {
2088                 return null;
2089             }
2090
2091             var el = oDD.getEl(), pos, x1, x2, y1, y2, t, r, b, l;
2092
2093             try {
2094                 pos= Roo.lib.Dom.getXY(el);
2095             } catch (e) { }
2096
2097             if (!pos) {
2098                 return null;
2099             }
2100
2101             x1 = pos[0];
2102             x2 = x1 + el.offsetWidth;
2103             y1 = pos[1];
2104             y2 = y1 + el.offsetHeight;
2105
2106             t = y1 - oDD.padding[0];
2107             r = x2 + oDD.padding[1];
2108             b = y2 + oDD.padding[2];
2109             l = x1 - oDD.padding[3];
2110
2111             return new Roo.lib.Region( t, r, b, l );
2112         },
2113
2114         /**
2115          * Checks the cursor location to see if it over the target
2116          * @method isOverTarget
2117          * @param {Roo.lib.Point} pt The point to evaluate
2118          * @param {DragDrop} oTarget the DragDrop object we are inspecting
2119          * @return {boolean} true if the mouse is over the target
2120          * @private
2121          * @static
2122          */
2123         isOverTarget: function(pt, oTarget, intersect) {
2124             // use cache if available
2125             var loc = this.locationCache[oTarget.id];
2126             if (!loc || !this.useCache) {
2127                 loc = this.getLocation(oTarget);
2128                 this.locationCache[oTarget.id] = loc;
2129
2130             }
2131
2132             if (!loc) {
2133                 return false;
2134             }
2135
2136             oTarget.cursorIsOver = loc.contains( pt );
2137
2138             // DragDrop is using this as a sanity check for the initial mousedown
2139             // in this case we are done.  In POINT mode, if the drag obj has no
2140             // contraints, we are also done. Otherwise we need to evaluate the
2141             // location of the target as related to the actual location of the
2142             // dragged element.
2143             var dc = this.dragCurrent;
2144             if (!dc || !dc.getTargetCoord ||
2145                     (!intersect && !dc.constrainX && !dc.constrainY)) {
2146                 return oTarget.cursorIsOver;
2147             }
2148
2149             oTarget.overlap = null;
2150
2151             // Get the current location of the drag element, this is the
2152             // location of the mouse event less the delta that represents
2153             // where the original mousedown happened on the element.  We
2154             // need to consider constraints and ticks as well.
2155             var pos = dc.getTargetCoord(pt.x, pt.y);
2156
2157             var el = dc.getDragEl();
2158             var curRegion = new Roo.lib.Region( pos.y,
2159                                                    pos.x + el.offsetWidth,
2160                                                    pos.y + el.offsetHeight,
2161                                                    pos.x );
2162
2163             var overlap = curRegion.intersect(loc);
2164
2165             if (overlap) {
2166                 oTarget.overlap = overlap;
2167                 return (intersect) ? true : oTarget.cursorIsOver;
2168             } else {
2169                 return false;
2170             }
2171         },
2172
2173         /**
2174          * unload event handler
2175          * @method _onUnload
2176          * @private
2177          * @static
2178          */
2179         _onUnload: function(e, me) {
2180             Roo.dd.DragDropMgr.unregAll();
2181         },
2182
2183         /**
2184          * Cleans up the drag and drop events and objects.
2185          * @method unregAll
2186          * @private
2187          * @static
2188          */
2189         unregAll: function() {
2190
2191             if (this.dragCurrent) {
2192                 this.stopDrag();
2193                 this.dragCurrent = null;
2194             }
2195
2196             this._execOnAll("unreg", []);
2197
2198             for (i in this.elementCache) {
2199                 delete this.elementCache[i];
2200             }
2201
2202             this.elementCache = {};
2203             this.ids = {};
2204         },
2205
2206         /**
2207          * A cache of DOM elements
2208          * @property elementCache
2209          * @private
2210          * @static
2211          */
2212         elementCache: {},
2213
2214         /**
2215          * Get the wrapper for the DOM element specified
2216          * @method getElWrapper
2217          * @param {String} id the id of the element to get
2218          * @return {Roo.dd.DDM.ElementWrapper} the wrapped element
2219          * @private
2220          * @deprecated This wrapper isn't that useful
2221          * @static
2222          */
2223         getElWrapper: function(id) {
2224             var oWrapper = this.elementCache[id];
2225             if (!oWrapper || !oWrapper.el) {
2226                 oWrapper = this.elementCache[id] =
2227                     new this.ElementWrapper(Roo.getDom(id));
2228             }
2229             return oWrapper;
2230         },
2231
2232         /**
2233          * Returns the actual DOM element
2234          * @method getElement
2235          * @param {String} id the id of the elment to get
2236          * @return {Object} The element
2237          * @deprecated use Roo.getDom instead
2238          * @static
2239          */
2240         getElement: function(id) {
2241             return Roo.getDom(id);
2242         },
2243
2244         /**
2245          * Returns the style property for the DOM element (i.e.,
2246          * document.getElById(id).style)
2247          * @method getCss
2248          * @param {String} id the id of the elment to get
2249          * @return {Object} The style property of the element
2250          * @deprecated use Roo.getDom instead
2251          * @static
2252          */
2253         getCss: function(id) {
2254             var el = Roo.getDom(id);
2255             return (el) ? el.style : null;
2256         },
2257
2258         /**
2259          * Inner class for cached elements
2260          * @class DragDropMgr.ElementWrapper
2261          * @for DragDropMgr
2262          * @private
2263          * @deprecated
2264          */
2265         ElementWrapper: function(el) {
2266                 /**
2267                  * The element
2268                  * @property el
2269                  */
2270                 this.el = el || null;
2271                 /**
2272                  * The element id
2273                  * @property id
2274                  */
2275                 this.id = this.el && el.id;
2276                 /**
2277                  * A reference to the style property
2278                  * @property css
2279                  */
2280                 this.css = this.el && el.style;
2281             },
2282
2283         /**
2284          * Returns the X position of an html element
2285          * @method getPosX
2286          * @param el the element for which to get the position
2287          * @return {int} the X coordinate
2288          * @for DragDropMgr
2289          * @deprecated use Roo.lib.Dom.getX instead
2290          * @static
2291          */
2292         getPosX: function(el) {
2293             return Roo.lib.Dom.getX(el);
2294         },
2295
2296         /**
2297          * Returns the Y position of an html element
2298          * @method getPosY
2299          * @param el the element for which to get the position
2300          * @return {int} the Y coordinate
2301          * @deprecated use Roo.lib.Dom.getY instead
2302          * @static
2303          */
2304         getPosY: function(el) {
2305             return Roo.lib.Dom.getY(el);
2306         },
2307
2308         /**
2309          * Swap two nodes.  In IE, we use the native method, for others we
2310          * emulate the IE behavior
2311          * @method swapNode
2312          * @param n1 the first node to swap
2313          * @param n2 the other node to swap
2314          * @static
2315          */
2316         swapNode: function(n1, n2) {
2317             if (n1.swapNode) {
2318                 n1.swapNode(n2);
2319             } else {
2320                 var p = n2.parentNode;
2321                 var s = n2.nextSibling;
2322
2323                 if (s == n1) {
2324                     p.insertBefore(n1, n2);
2325                 } else if (n2 == n1.nextSibling) {
2326                     p.insertBefore(n2, n1);
2327                 } else {
2328                     n1.parentNode.replaceChild(n2, n1);
2329                     p.insertBefore(n1, s);
2330                 }
2331             }
2332         },
2333
2334         /**
2335          * Returns the current scroll position
2336          * @method getScroll
2337          * @private
2338          * @static
2339          */
2340         getScroll: function () {
2341             var t, l, dde=document.documentElement, db=document.body;
2342             if (dde && (dde.scrollTop || dde.scrollLeft)) {
2343                 t = dde.scrollTop;
2344                 l = dde.scrollLeft;
2345             } else if (db) {
2346                 t = db.scrollTop;
2347                 l = db.scrollLeft;
2348             } else {
2349
2350             }
2351             return { top: t, left: l };
2352         },
2353
2354         /**
2355          * Returns the specified element style property
2356          * @method getStyle
2357          * @param {HTMLElement} el          the element
2358          * @param {string}      styleProp   the style property
2359          * @return {string} The value of the style property
2360          * @deprecated use Roo.lib.Dom.getStyle
2361          * @static
2362          */
2363         getStyle: function(el, styleProp) {
2364             return Roo.fly(el).getStyle(styleProp);
2365         },
2366
2367         /**
2368          * Gets the scrollTop
2369          * @method getScrollTop
2370          * @return {int} the document's scrollTop
2371          * @static
2372          */
2373         getScrollTop: function () { return this.getScroll().top; },
2374
2375         /**
2376          * Gets the scrollLeft
2377          * @method getScrollLeft
2378          * @return {int} the document's scrollTop
2379          * @static
2380          */
2381         getScrollLeft: function () { return this.getScroll().left; },
2382
2383         /**
2384          * Sets the x/y position of an element to the location of the
2385          * target element.
2386          * @method moveToEl
2387          * @param {HTMLElement} moveEl      The element to move
2388          * @param {HTMLElement} targetEl    The position reference element
2389          * @static
2390          */
2391         moveToEl: function (moveEl, targetEl) {
2392             var aCoord = Roo.lib.Dom.getXY(targetEl);
2393             Roo.lib.Dom.setXY(moveEl, aCoord);
2394         },
2395
2396         /**
2397          * Numeric array sort function
2398          * @method numericSort
2399          * @static
2400          */
2401         numericSort: function(a, b) { return (a - b); },
2402
2403         /**
2404          * Internal counter
2405          * @property _timeoutCount
2406          * @private
2407          * @static
2408          */
2409         _timeoutCount: 0,
2410
2411         /**
2412          * Trying to make the load order less important.  Without this we get
2413          * an error if this file is loaded before the Event Utility.
2414          * @method _addListeners
2415          * @private
2416          * @static
2417          */
2418         _addListeners: function() {
2419             var DDM = Roo.dd.DDM;
2420             if ( Roo.lib.Event && document ) {
2421                 DDM._onLoad();
2422             } else {
2423                 if (DDM._timeoutCount > 2000) {
2424                 } else {
2425                     setTimeout(DDM._addListeners, 10);
2426                     if (document && document.body) {
2427                         DDM._timeoutCount += 1;
2428                     }
2429                 }
2430             }
2431         },
2432
2433         /**
2434          * Recursively searches the immediate parent and all child nodes for
2435          * the handle element in order to determine wheter or not it was
2436          * clicked.
2437          * @method handleWasClicked
2438          * @param node the html element to inspect
2439          * @static
2440          */
2441         handleWasClicked: function(node, id) {
2442             if (this.isHandle(id, node.id)) {
2443                 return true;
2444             } else {
2445                 // check to see if this is a text node child of the one we want
2446                 var p = node.parentNode;
2447
2448                 while (p) {
2449                     if (this.isHandle(id, p.id)) {
2450                         return true;
2451                     } else {
2452                         p = p.parentNode;
2453                     }
2454                 }
2455             }
2456
2457             return false;
2458         }
2459
2460     };
2461
2462 }();
2463
2464 // shorter alias, save a few bytes
2465 Roo.dd.DDM = Roo.dd.DragDropMgr;
2466 Roo.dd.DDM._addListeners();
2467
2468 }/*
2469  * Based on:
2470  * Ext JS Library 1.1.1
2471  * Copyright(c) 2006-2007, Ext JS, LLC.
2472  *
2473  * Originally Released Under LGPL - original licence link has changed is not relivant.
2474  *
2475  * Fork - LGPL
2476  * <script type="text/javascript">
2477  */
2478
2479 /**
2480  * @class Roo.dd.DD
2481  * A DragDrop implementation where the linked element follows the
2482  * mouse cursor during a drag.
2483  * @extends Roo.dd.DragDrop
2484  * @constructor
2485  * @param {String} id the id of the linked element
2486  * @param {String} sGroup the group of related DragDrop items
2487  * @param {object} config an object containing configurable attributes
2488  *                Valid properties for DD:
2489  *                    scroll
2490  */
2491 Roo.dd.DD = function(id, sGroup, config) {
2492     if (id) {
2493         this.init(id, sGroup, config);
2494     }
2495 };
2496
2497 Roo.extend(Roo.dd.DD, Roo.dd.DragDrop, {
2498
2499     /**
2500      * When set to true, the utility automatically tries to scroll the browser
2501      * window wehn a drag and drop element is dragged near the viewport boundary.
2502      * Defaults to true.
2503      * @property scroll
2504      * @type boolean
2505      */
2506     scroll: true,
2507
2508     /**
2509      * Sets the pointer offset to the distance between the linked element's top
2510      * left corner and the location the element was clicked
2511      * @method autoOffset
2512      * @param {int} iPageX the X coordinate of the click
2513      * @param {int} iPageY the Y coordinate of the click
2514      */
2515     autoOffset: function(iPageX, iPageY) {
2516         var x = iPageX - this.startPageX;
2517         var y = iPageY - this.startPageY;
2518         this.setDelta(x, y);
2519     },
2520
2521     /**
2522      * Sets the pointer offset.  You can call this directly to force the
2523      * offset to be in a particular location (e.g., pass in 0,0 to set it
2524      * to the center of the object)
2525      * @method setDelta
2526      * @param {int} iDeltaX the distance from the left
2527      * @param {int} iDeltaY the distance from the top
2528      */
2529     setDelta: function(iDeltaX, iDeltaY) {
2530         this.deltaX = iDeltaX;
2531         this.deltaY = iDeltaY;
2532     },
2533
2534     /**
2535      * Sets the drag element to the location of the mousedown or click event,
2536      * maintaining the cursor location relative to the location on the element
2537      * that was clicked.  Override this if you want to place the element in a
2538      * location other than where the cursor is.
2539      * @method setDragElPos
2540      * @param {int} iPageX the X coordinate of the mousedown or drag event
2541      * @param {int} iPageY the Y coordinate of the mousedown or drag event
2542      */
2543     setDragElPos: function(iPageX, iPageY) {
2544         // the first time we do this, we are going to check to make sure
2545         // the element has css positioning
2546
2547         var el = this.getDragEl();
2548         this.alignElWithMouse(el, iPageX, iPageY);
2549     },
2550
2551     /**
2552      * Sets the element to the location of the mousedown or click event,
2553      * maintaining the cursor location relative to the location on the element
2554      * that was clicked.  Override this if you want to place the element in a
2555      * location other than where the cursor is.
2556      * @method alignElWithMouse
2557      * @param {HTMLElement} el the element to move
2558      * @param {int} iPageX the X coordinate of the mousedown or drag event
2559      * @param {int} iPageY the Y coordinate of the mousedown or drag event
2560      */
2561     alignElWithMouse: function(el, iPageX, iPageY) {
2562         var oCoord = this.getTargetCoord(iPageX, iPageY);
2563         var fly = el.dom ? el : Roo.fly(el);
2564         if (!this.deltaSetXY) {
2565             var aCoord = [oCoord.x, oCoord.y];
2566             fly.setXY(aCoord);
2567             var newLeft = fly.getLeft(true);
2568             var newTop  = fly.getTop(true);
2569             this.deltaSetXY = [ newLeft - oCoord.x, newTop - oCoord.y ];
2570         } else {
2571             fly.setLeftTop(oCoord.x + this.deltaSetXY[0], oCoord.y + this.deltaSetXY[1]);
2572         }
2573
2574         this.cachePosition(oCoord.x, oCoord.y);
2575         this.autoScroll(oCoord.x, oCoord.y, el.offsetHeight, el.offsetWidth);
2576         return oCoord;
2577     },
2578
2579     /**
2580      * Saves the most recent position so that we can reset the constraints and
2581      * tick marks on-demand.  We need to know this so that we can calculate the
2582      * number of pixels the element is offset from its original position.
2583      * @method cachePosition
2584      * @param iPageX the current x position (optional, this just makes it so we
2585      * don't have to look it up again)
2586      * @param iPageY the current y position (optional, this just makes it so we
2587      * don't have to look it up again)
2588      */
2589     cachePosition: function(iPageX, iPageY) {
2590         if (iPageX) {
2591             this.lastPageX = iPageX;
2592             this.lastPageY = iPageY;
2593         } else {
2594             var aCoord = Roo.lib.Dom.getXY(this.getEl());
2595             this.lastPageX = aCoord[0];
2596             this.lastPageY = aCoord[1];
2597         }
2598     },
2599
2600     /**
2601      * Auto-scroll the window if the dragged object has been moved beyond the
2602      * visible window boundary.
2603      * @method autoScroll
2604      * @param {int} x the drag element's x position
2605      * @param {int} y the drag element's y position
2606      * @param {int} h the height of the drag element
2607      * @param {int} w the width of the drag element
2608      * @private
2609      */
2610     autoScroll: function(x, y, h, w) {
2611
2612         if (this.scroll) {
2613             // The client height
2614             var clientH = Roo.lib.Dom.getViewWidth();
2615
2616             // The client width
2617             var clientW = Roo.lib.Dom.getViewHeight();
2618
2619             // The amt scrolled down
2620             var st = this.DDM.getScrollTop();
2621
2622             // The amt scrolled right
2623             var sl = this.DDM.getScrollLeft();
2624
2625             // Location of the bottom of the element
2626             var bot = h + y;
2627
2628             // Location of the right of the element
2629             var right = w + x;
2630
2631             // The distance from the cursor to the bottom of the visible area,
2632             // adjusted so that we don't scroll if the cursor is beyond the
2633             // element drag constraints
2634             var toBot = (clientH + st - y - this.deltaY);
2635
2636             // The distance from the cursor to the right of the visible area
2637             var toRight = (clientW + sl - x - this.deltaX);
2638
2639
2640             // How close to the edge the cursor must be before we scroll
2641             // var thresh = (document.all) ? 100 : 40;
2642             var thresh = 40;
2643
2644             // How many pixels to scroll per autoscroll op.  This helps to reduce
2645             // clunky scrolling. IE is more sensitive about this ... it needs this
2646             // value to be higher.
2647             var scrAmt = (document.all) ? 80 : 30;
2648
2649             // Scroll down if we are near the bottom of the visible page and the
2650             // obj extends below the crease
2651             if ( bot > clientH && toBot < thresh ) {
2652                 window.scrollTo(sl, st + scrAmt);
2653             }
2654
2655             // Scroll up if the window is scrolled down and the top of the object
2656             // goes above the top border
2657             if ( y < st && st > 0 && y - st < thresh ) {
2658                 window.scrollTo(sl, st - scrAmt);
2659             }
2660
2661             // Scroll right if the obj is beyond the right border and the cursor is
2662             // near the border.
2663             if ( right > clientW && toRight < thresh ) {
2664                 window.scrollTo(sl + scrAmt, st);
2665             }
2666
2667             // Scroll left if the window has been scrolled to the right and the obj
2668             // extends past the left border
2669             if ( x < sl && sl > 0 && x - sl < thresh ) {
2670                 window.scrollTo(sl - scrAmt, st);
2671             }
2672         }
2673     },
2674
2675     /**
2676      * Finds the location the element should be placed if we want to move
2677      * it to where the mouse location less the click offset would place us.
2678      * @method getTargetCoord
2679      * @param {int} iPageX the X coordinate of the click
2680      * @param {int} iPageY the Y coordinate of the click
2681      * @return an object that contains the coordinates (Object.x and Object.y)
2682      * @private
2683      */
2684     getTargetCoord: function(iPageX, iPageY) {
2685
2686
2687         var x = iPageX - this.deltaX;
2688         var y = iPageY - this.deltaY;
2689
2690         if (this.constrainX) {
2691             if (x < this.minX) { x = this.minX; }
2692             if (x > this.maxX) { x = this.maxX; }
2693         }
2694
2695         if (this.constrainY) {
2696             if (y < this.minY) { y = this.minY; }
2697             if (y > this.maxY) { y = this.maxY; }
2698         }
2699
2700         x = this.getTick(x, this.xTicks);
2701         y = this.getTick(y, this.yTicks);
2702
2703
2704         return {x:x, y:y};
2705     },
2706
2707     /*
2708      * Sets up config options specific to this class. Overrides
2709      * Roo.dd.DragDrop, but all versions of this method through the
2710      * inheritance chain are called
2711      */
2712     applyConfig: function() {
2713         Roo.dd.DD.superclass.applyConfig.call(this);
2714         this.scroll = (this.config.scroll !== false);
2715     },
2716
2717     /*
2718      * Event that fires prior to the onMouseDown event.  Overrides
2719      * Roo.dd.DragDrop.
2720      */
2721     b4MouseDown: function(e) {
2722         // this.resetConstraints();
2723         this.autoOffset(e.getPageX(),
2724                             e.getPageY());
2725     },
2726
2727     /*
2728      * Event that fires prior to the onDrag event.  Overrides
2729      * Roo.dd.DragDrop.
2730      */
2731     b4Drag: function(e) {
2732         this.setDragElPos(e.getPageX(),
2733                             e.getPageY());
2734     },
2735
2736     toString: function() {
2737         return ("DD " + this.id);
2738     }
2739
2740     //////////////////////////////////////////////////////////////////////////
2741     // Debugging ygDragDrop events that can be overridden
2742     //////////////////////////////////////////////////////////////////////////
2743     /*
2744     startDrag: function(x, y) {
2745     },
2746
2747     onDrag: function(e) {
2748     },
2749
2750     onDragEnter: function(e, id) {
2751     },
2752
2753     onDragOver: function(e, id) {
2754     },
2755
2756     onDragOut: function(e, id) {
2757     },
2758
2759     onDragDrop: function(e, id) {
2760     },
2761
2762     endDrag: function(e) {
2763     }
2764
2765     */
2766
2767 });/*
2768  * Based on:
2769  * Ext JS Library 1.1.1
2770  * Copyright(c) 2006-2007, Ext JS, LLC.
2771  *
2772  * Originally Released Under LGPL - original licence link has changed is not relivant.
2773  *
2774  * Fork - LGPL
2775  * <script type="text/javascript">
2776  */
2777
2778 /**
2779  * @class Roo.dd.DDProxy
2780  * A DragDrop implementation that inserts an empty, bordered div into
2781  * the document that follows the cursor during drag operations.  At the time of
2782  * the click, the frame div is resized to the dimensions of the linked html
2783  * element, and moved to the exact location of the linked element.
2784  *
2785  * References to the "frame" element refer to the single proxy element that
2786  * was created to be dragged in place of all DDProxy elements on the
2787  * page.
2788  *
2789  * @extends Roo.dd.DD
2790  * @constructor
2791  * @param {String} id the id of the linked html element
2792  * @param {String} sGroup the group of related DragDrop objects
2793  * @param {object} config an object containing configurable attributes
2794  *                Valid properties for DDProxy in addition to those in DragDrop:
2795  *                   resizeFrame, centerFrame, dragElId
2796  */
2797 Roo.dd.DDProxy = function(id, sGroup, config) {
2798     if (id) {
2799         this.init(id, sGroup, config);
2800         this.initFrame();
2801     }
2802 };
2803
2804 /**
2805  * The default drag frame div id
2806  * @property Roo.dd.DDProxy.dragElId
2807  * @type String
2808  * @static
2809  */
2810 Roo.dd.DDProxy.dragElId = "ygddfdiv";
2811
2812 Roo.extend(Roo.dd.DDProxy, Roo.dd.DD, {
2813
2814     /**
2815      * By default we resize the drag frame to be the same size as the element
2816      * we want to drag (this is to get the frame effect).  We can turn it off
2817      * if we want a different behavior.
2818      * @property resizeFrame
2819      * @type boolean
2820      */
2821     resizeFrame: true,
2822
2823     /**
2824      * By default the frame is positioned exactly where the drag element is, so
2825      * we use the cursor offset provided by Roo.dd.DD.  Another option that works only if
2826      * you do not have constraints on the obj is to have the drag frame centered
2827      * around the cursor.  Set centerFrame to true for this effect.
2828      * @property centerFrame
2829      * @type boolean
2830      */
2831     centerFrame: false,
2832
2833     /**
2834      * Creates the proxy element if it does not yet exist
2835      * @method createFrame
2836      */
2837     createFrame: function() {
2838         var self = this;
2839         var body = document.body;
2840
2841         if (!body || !body.firstChild) {
2842             setTimeout( function() { self.createFrame(); }, 50 );
2843             return;
2844         }
2845
2846         var div = this.getDragEl();
2847
2848         if (!div) {
2849             div    = document.createElement("div");
2850             div.id = this.dragElId;
2851             var s  = div.style;
2852
2853             s.position   = "absolute";
2854             s.visibility = "hidden";
2855             s.cursor     = "move";
2856             s.border     = "2px solid #aaa";
2857             s.zIndex     = 999;
2858
2859             // appendChild can blow up IE if invoked prior to the window load event
2860             // while rendering a table.  It is possible there are other scenarios
2861             // that would cause this to happen as well.
2862             body.insertBefore(div, body.firstChild);
2863         }
2864     },
2865
2866     /**
2867      * Initialization for the drag frame element.  Must be called in the
2868      * constructor of all subclasses
2869      * @method initFrame
2870      */
2871     initFrame: function() {
2872         this.createFrame();
2873     },
2874
2875     applyConfig: function() {
2876         Roo.dd.DDProxy.superclass.applyConfig.call(this);
2877
2878         this.resizeFrame = (this.config.resizeFrame !== false);
2879         this.centerFrame = (this.config.centerFrame);
2880         this.setDragElId(this.config.dragElId || Roo.dd.DDProxy.dragElId);
2881     },
2882
2883     /**
2884      * Resizes the drag frame to the dimensions of the clicked object, positions
2885      * it over the object, and finally displays it
2886      * @method showFrame
2887      * @param {int} iPageX X click position
2888      * @param {int} iPageY Y click position
2889      * @private
2890      */
2891     showFrame: function(iPageX, iPageY) {
2892         var el = this.getEl();
2893         var dragEl = this.getDragEl();
2894         var s = dragEl.style;
2895
2896         this._resizeProxy();
2897
2898         if (this.centerFrame) {
2899             this.setDelta( Math.round(parseInt(s.width,  10)/2),
2900                            Math.round(parseInt(s.height, 10)/2) );
2901         }
2902
2903         this.setDragElPos(iPageX, iPageY);
2904
2905         Roo.fly(dragEl).show();
2906     },
2907
2908     /**
2909      * The proxy is automatically resized to the dimensions of the linked
2910      * element when a drag is initiated, unless resizeFrame is set to false
2911      * @method _resizeProxy
2912      * @private
2913      */
2914     _resizeProxy: function() {
2915         if (this.resizeFrame) {
2916             var el = this.getEl();
2917             Roo.fly(this.getDragEl()).setSize(el.offsetWidth, el.offsetHeight);
2918         }
2919     },
2920
2921     // overrides Roo.dd.DragDrop
2922     b4MouseDown: function(e) {
2923         var x = e.getPageX();
2924         var y = e.getPageY();
2925         this.autoOffset(x, y);
2926         this.setDragElPos(x, y);
2927     },
2928
2929     // overrides Roo.dd.DragDrop
2930     b4StartDrag: function(x, y) {
2931         // show the drag frame
2932         this.showFrame(x, y);
2933     },
2934
2935     // overrides Roo.dd.DragDrop
2936     b4EndDrag: function(e) {
2937         Roo.fly(this.getDragEl()).hide();
2938     },
2939
2940     // overrides Roo.dd.DragDrop
2941     // By default we try to move the element to the last location of the frame.
2942     // This is so that the default behavior mirrors that of Roo.dd.DD.
2943     endDrag: function(e) {
2944
2945         var lel = this.getEl();
2946         var del = this.getDragEl();
2947
2948         // Show the drag frame briefly so we can get its position
2949         del.style.visibility = "";
2950
2951         this.beforeMove();
2952         // Hide the linked element before the move to get around a Safari
2953         // rendering bug.
2954         lel.style.visibility = "hidden";
2955         Roo.dd.DDM.moveToEl(lel, del);
2956         del.style.visibility = "hidden";
2957         lel.style.visibility = "";
2958
2959         this.afterDrag();
2960     },
2961
2962     beforeMove : function(){
2963
2964     },
2965
2966     afterDrag : function(){
2967
2968     },
2969
2970     toString: function() {
2971         return ("DDProxy " + this.id);
2972     }
2973
2974 });
2975 /*
2976  * Based on:
2977  * Ext JS Library 1.1.1
2978  * Copyright(c) 2006-2007, Ext JS, LLC.
2979  *
2980  * Originally Released Under LGPL - original licence link has changed is not relivant.
2981  *
2982  * Fork - LGPL
2983  * <script type="text/javascript">
2984  */
2985
2986  /**
2987  * @class Roo.dd.DDTarget
2988  * A DragDrop implementation that does not move, but can be a drop
2989  * target.  You would get the same result by simply omitting implementation
2990  * for the event callbacks, but this way we reduce the processing cost of the
2991  * event listener and the callbacks.
2992  * @extends Roo.dd.DragDrop
2993  * @constructor
2994  * @param {String} id the id of the element that is a drop target
2995  * @param {String} sGroup the group of related DragDrop objects
2996  * @param {object} config an object containing configurable attributes
2997  *                 Valid properties for DDTarget in addition to those in
2998  *                 DragDrop:
2999  *                    none
3000  */
3001 Roo.dd.DDTarget = function(id, sGroup, config) {
3002     if (id) {
3003         this.initTarget(id, sGroup, config);
3004     }
3005     if (config.listeners || config.events) { 
3006        Roo.dd.DragDrop.superclass.constructor.call(this,  { 
3007             listeners : config.listeners || {}, 
3008             events : config.events || {} 
3009         });    
3010     }
3011 };
3012
3013 // Roo.dd.DDTarget.prototype = new Roo.dd.DragDrop();
3014 Roo.extend(Roo.dd.DDTarget, Roo.dd.DragDrop, {
3015     toString: function() {
3016         return ("DDTarget " + this.id);
3017     }
3018 });
3019 /*
3020  * Based on:
3021  * Ext JS Library 1.1.1
3022  * Copyright(c) 2006-2007, Ext JS, LLC.
3023  *
3024  * Originally Released Under LGPL - original licence link has changed is not relivant.
3025  *
3026  * Fork - LGPL
3027  * <script type="text/javascript">
3028  */
3029  
3030
3031 /**
3032  * @class Roo.dd.ScrollManager
3033  * Provides automatic scrolling of overflow regions in the page during drag operations.<br><br>
3034  * <b>Note: This class uses "Point Mode" and is untested in "Intersect Mode".</b>
3035  * @singleton
3036  */
3037 Roo.dd.ScrollManager = function(){
3038     var ddm = Roo.dd.DragDropMgr;
3039     var els = {};
3040     var dragEl = null;
3041     var proc = {};
3042     
3043     
3044     
3045     var onStop = function(e){
3046         dragEl = null;
3047         clearProc();
3048     };
3049     
3050     var triggerRefresh = function(){
3051         if(ddm.dragCurrent){
3052              ddm.refreshCache(ddm.dragCurrent.groups);
3053         }
3054     };
3055     
3056     var doScroll = function(){
3057         if(ddm.dragCurrent){
3058             var dds = Roo.dd.ScrollManager;
3059             if(!dds.animate){
3060                 if(proc.el.scroll(proc.dir, dds.increment)){
3061                     triggerRefresh();
3062                 }
3063             }else{
3064                 proc.el.scroll(proc.dir, dds.increment, true, dds.animDuration, triggerRefresh);
3065             }
3066         }
3067     };
3068     
3069     var clearProc = function(){
3070         if(proc.id){
3071             clearInterval(proc.id);
3072         }
3073         proc.id = 0;
3074         proc.el = null;
3075         proc.dir = "";
3076     };
3077     
3078     var startProc = function(el, dir){
3079          Roo.log('scroll startproc');
3080         clearProc();
3081         proc.el = el;
3082         proc.dir = dir;
3083         proc.id = setInterval(doScroll, Roo.dd.ScrollManager.frequency);
3084     };
3085     
3086     var onFire = function(e, isDrop){
3087        
3088         if(isDrop || !ddm.dragCurrent){ return; }
3089         var dds = Roo.dd.ScrollManager;
3090         if(!dragEl || dragEl != ddm.dragCurrent){
3091             dragEl = ddm.dragCurrent;
3092             // refresh regions on drag start
3093             dds.refreshCache();
3094         }
3095         
3096         var xy = Roo.lib.Event.getXY(e);
3097         var pt = new Roo.lib.Point(xy[0], xy[1]);
3098         for(var id in els){
3099             var el = els[id], r = el._region;
3100             if(r && r.contains(pt) && el.isScrollable()){
3101                 if(r.bottom - pt.y <= dds.thresh){
3102                     if(proc.el != el){
3103                         startProc(el, "down");
3104                     }
3105                     return;
3106                 }else if(r.right - pt.x <= dds.thresh){
3107                     if(proc.el != el){
3108                         startProc(el, "left");
3109                     }
3110                     return;
3111                 }else if(pt.y - r.top <= dds.thresh){
3112                     if(proc.el != el){
3113                         startProc(el, "up");
3114                     }
3115                     return;
3116                 }else if(pt.x - r.left <= dds.thresh){
3117                     if(proc.el != el){
3118                         startProc(el, "right");
3119                     }
3120                     return;
3121                 }
3122             }
3123         }
3124         clearProc();
3125     };
3126     
3127     ddm.fireEvents = ddm.fireEvents.createSequence(onFire, ddm);
3128     ddm.stopDrag = ddm.stopDrag.createSequence(onStop, ddm);
3129     
3130     return {
3131         /**
3132          * Registers new overflow element(s) to auto scroll
3133          * @param {String/HTMLElement/Element/Array} el The id of or the element to be scrolled or an array of either
3134          */
3135         register : function(el){
3136             if(el instanceof Array){
3137                 for(var i = 0, len = el.length; i < len; i++) {
3138                         this.register(el[i]);
3139                 }
3140             }else{
3141                 el = Roo.get(el);
3142                 els[el.id] = el;
3143             }
3144             Roo.dd.ScrollManager.els = els;
3145         },
3146         
3147         /**
3148          * Unregisters overflow element(s) so they are no longer scrolled
3149          * @param {String/HTMLElement/Element/Array} el The id of or the element to be removed or an array of either
3150          */
3151         unregister : function(el){
3152             if(el instanceof Array){
3153                 for(var i = 0, len = el.length; i < len; i++) {
3154                         this.unregister(el[i]);
3155                 }
3156             }else{
3157                 el = Roo.get(el);
3158                 delete els[el.id];
3159             }
3160         },
3161         
3162         /**
3163          * The number of pixels from the edge of a container the pointer needs to be to 
3164          * trigger scrolling (defaults to 25)
3165          * @type Number
3166          */
3167         thresh : 25,
3168         
3169         /**
3170          * The number of pixels to scroll in each scroll increment (defaults to 50)
3171          * @type Number
3172          */
3173         increment : 100,
3174         
3175         /**
3176          * The frequency of scrolls in milliseconds (defaults to 500)
3177          * @type Number
3178          */
3179         frequency : 500,
3180         
3181         /**
3182          * True to animate the scroll (defaults to true)
3183          * @type Boolean
3184          */
3185         animate: true,
3186         
3187         /**
3188          * The animation duration in seconds - 
3189          * MUST BE less than Roo.dd.ScrollManager.frequency! (defaults to .4)
3190          * @type Number
3191          */
3192         animDuration: .4,
3193         
3194         /**
3195          * Manually trigger a cache refresh.
3196          */
3197         refreshCache : function(){
3198             for(var id in els){
3199                 if(typeof els[id] == 'object'){ // for people extending the object prototype
3200                     els[id]._region = els[id].getRegion();
3201                 }
3202             }
3203         }
3204     };
3205 }();/*
3206  * Based on:
3207  * Ext JS Library 1.1.1
3208  * Copyright(c) 2006-2007, Ext JS, LLC.
3209  *
3210  * Originally Released Under LGPL - original licence link has changed is not relivant.
3211  *
3212  * Fork - LGPL
3213  * <script type="text/javascript">
3214  */
3215  
3216
3217 /**
3218  * @class Roo.dd.Registry
3219  * Provides easy access to all drag drop components that are registered on a page.  Items can be retrieved either
3220  * directly by DOM node id, or by passing in the drag drop event that occurred and looking up the event target.
3221  * @singleton
3222  */
3223 Roo.dd.Registry = function(){
3224     var elements = {}; 
3225     var handles = {}; 
3226     var autoIdSeed = 0;
3227
3228     var getId = function(el, autogen){
3229         if(typeof el == "string"){
3230             return el;
3231         }
3232         var id = el.id;
3233         if(!id && autogen !== false){
3234             id = "roodd-" + (++autoIdSeed);
3235             el.id = id;
3236         }
3237         return id;
3238     };
3239     
3240     return {
3241     /**
3242      * Register a drag drop element
3243      * @param {String|HTMLElement} element The id or DOM node to register
3244      * @param {Object} data (optional) A custom data object that will be passed between the elements that are involved
3245      * in drag drop operations.  You can populate this object with any arbitrary properties that your own code
3246      * knows how to interpret, plus there are some specific properties known to the Registry that should be
3247      * populated in the data object (if applicable):
3248      * <pre>
3249 Value      Description<br />
3250 ---------  ------------------------------------------<br />
3251 handles    Array of DOM nodes that trigger dragging<br />
3252            for the element being registered<br />
3253 isHandle   True if the element passed in triggers<br />
3254            dragging itself, else false
3255 </pre>
3256      */
3257         register : function(el, data){
3258             data = data || {};
3259             if(typeof el == "string"){
3260                 el = document.getElementById(el);
3261             }
3262             data.ddel = el;
3263             elements[getId(el)] = data;
3264             if(data.isHandle !== false){
3265                 handles[data.ddel.id] = data;
3266             }
3267             if(data.handles){
3268                 var hs = data.handles;
3269                 for(var i = 0, len = hs.length; i < len; i++){
3270                         handles[getId(hs[i])] = data;
3271                 }
3272             }
3273         },
3274
3275     /**
3276      * Unregister a drag drop element
3277      * @param {String|HTMLElement}  element The id or DOM node to unregister
3278      */
3279         unregister : function(el){
3280             var id = getId(el, false);
3281             var data = elements[id];
3282             if(data){
3283                 delete elements[id];
3284                 if(data.handles){
3285                     var hs = data.handles;
3286                     for(var i = 0, len = hs.length; i < len; i++){
3287                         delete handles[getId(hs[i], false)];
3288                     }
3289                 }
3290             }
3291         },
3292
3293     /**
3294      * Returns the handle registered for a DOM Node by id
3295      * @param {String|HTMLElement} id The DOM node or id to look up
3296      * @return {Object} handle The custom handle data
3297      */
3298         getHandle : function(id){
3299             if(typeof id != "string"){ // must be element?
3300                 id = id.id;
3301             }
3302             return handles[id];
3303         },
3304
3305     /**
3306      * Returns the handle that is registered for the DOM node that is the target of the event
3307      * @param {Event} e The event
3308      * @return {Object} handle The custom handle data
3309      */
3310         getHandleFromEvent : function(e){
3311             var t = Roo.lib.Event.getTarget(e);
3312             return t ? handles[t.id] : null;
3313         },
3314
3315     /**
3316      * Returns a custom data object that is registered for a DOM node by id
3317      * @param {String|HTMLElement} id The DOM node or id to look up
3318      * @return {Object} data The custom data
3319      */
3320         getTarget : function(id){
3321             if(typeof id != "string"){ // must be element?
3322                 id = id.id;
3323             }
3324             return elements[id];
3325         },
3326
3327     /**
3328      * Returns a custom data object that is registered for the DOM node that is the target of the event
3329      * @param {Event} e The event
3330      * @return {Object} data The custom data
3331      */
3332         getTargetFromEvent : function(e){
3333             var t = Roo.lib.Event.getTarget(e);
3334             return t ? elements[t.id] || handles[t.id] : null;
3335         }
3336     };
3337 }();/*
3338  * Based on:
3339  * Ext JS Library 1.1.1
3340  * Copyright(c) 2006-2007, Ext JS, LLC.
3341  *
3342  * Originally Released Under LGPL - original licence link has changed is not relivant.
3343  *
3344  * Fork - LGPL
3345  * <script type="text/javascript">
3346  */
3347  
3348
3349 /**
3350  * @class Roo.dd.StatusProxy
3351  * A specialized drag proxy that supports a drop status icon, {@link Roo.Layer} styles and auto-repair.  This is the
3352  * default drag proxy used by all Roo.dd components.
3353  * @constructor
3354  * @param {Object} config
3355  */
3356 Roo.dd.StatusProxy = function(config){
3357     Roo.apply(this, config);
3358     this.id = this.id || Roo.id();
3359     this.el = new Roo.Layer({
3360         dh: {
3361             id: this.id, tag: "div", cls: "x-dd-drag-proxy "+this.dropNotAllowed, children: [
3362                 {tag: "div", cls: "x-dd-drop-icon"},
3363                 {tag: "div", cls: "x-dd-drag-ghost"}
3364             ]
3365         }, 
3366         shadow: !config || config.shadow !== false
3367     });
3368     this.ghost = Roo.get(this.el.dom.childNodes[1]);
3369     this.dropStatus = this.dropNotAllowed;
3370 };
3371
3372 Roo.dd.StatusProxy.prototype = {
3373     /**
3374      * @cfg {String} dropAllowed
3375      * The CSS class to apply to the status element when drop is allowed (defaults to "x-dd-drop-ok").
3376      */
3377     dropAllowed : "x-dd-drop-ok",
3378     /**
3379      * @cfg {String} dropNotAllowed
3380      * The CSS class to apply to the status element when drop is not allowed (defaults to "x-dd-drop-nodrop").
3381      */
3382     dropNotAllowed : "x-dd-drop-nodrop",
3383
3384     /**
3385      * Updates the proxy's visual element to indicate the status of whether or not drop is allowed
3386      * over the current target element.
3387      * @param {String} cssClass The css class for the new drop status indicator image
3388      */
3389     setStatus : function(cssClass){
3390         cssClass = cssClass || this.dropNotAllowed;
3391         if(this.dropStatus != cssClass){
3392             this.el.replaceClass(this.dropStatus, cssClass);
3393             this.dropStatus = cssClass;
3394         }
3395     },
3396
3397     /**
3398      * Resets the status indicator to the default dropNotAllowed value
3399      * @param {Boolean} clearGhost True to also remove all content from the ghost, false to preserve it
3400      */
3401     reset : function(clearGhost){
3402         this.el.dom.className = "x-dd-drag-proxy " + this.dropNotAllowed;
3403         this.dropStatus = this.dropNotAllowed;
3404         if(clearGhost){
3405             this.ghost.update("");
3406         }
3407     },
3408
3409     /**
3410      * Updates the contents of the ghost element
3411      * @param {String} html The html that will replace the current innerHTML of the ghost element
3412      */
3413     update : function(html){
3414         if(typeof html == "string"){
3415             this.ghost.update(html);
3416         }else{
3417             this.ghost.update("");
3418             html.style.margin = "0";
3419             this.ghost.dom.appendChild(html);
3420         }
3421         // ensure float = none set?? cant remember why though.
3422         var el = this.ghost.dom.firstChild;
3423                 if(el){
3424                         Roo.fly(el).setStyle('float', 'none');
3425                 }
3426     },
3427     
3428     /**
3429      * Returns the underlying proxy {@link Roo.Layer}
3430      * @return {Roo.Layer} el
3431     */
3432     getEl : function(){
3433         return this.el;
3434     },
3435
3436     /**
3437      * Returns the ghost element
3438      * @return {Roo.Element} el
3439      */
3440     getGhost : function(){
3441         return this.ghost;
3442     },
3443
3444     /**
3445      * Hides the proxy
3446      * @param {Boolean} clear True to reset the status and clear the ghost contents, false to preserve them
3447      */
3448     hide : function(clear){
3449         this.el.hide();
3450         if(clear){
3451             this.reset(true);
3452         }
3453     },
3454
3455     /**
3456      * Stops the repair animation if it's currently running
3457      */
3458     stop : function(){
3459         if(this.anim && this.anim.isAnimated && this.anim.isAnimated()){
3460             this.anim.stop();
3461         }
3462     },
3463
3464     /**
3465      * Displays this proxy
3466      */
3467     show : function(){
3468         this.el.show();
3469     },
3470
3471     /**
3472      * Force the Layer to sync its shadow and shim positions to the element
3473      */
3474     sync : function(){
3475         this.el.sync();
3476     },
3477
3478     /**
3479      * Causes the proxy to return to its position of origin via an animation.  Should be called after an
3480      * invalid drop operation by the item being dragged.
3481      * @param {Array} xy The XY position of the element ([x, y])
3482      * @param {Function} callback The function to call after the repair is complete
3483      * @param {Object} scope The scope in which to execute the callback
3484      */
3485     repair : function(xy, callback, scope){
3486         this.callback = callback;
3487         this.scope = scope;
3488         if(xy && this.animRepair !== false){
3489             this.el.addClass("x-dd-drag-repair");
3490             this.el.hideUnders(true);
3491             this.anim = this.el.shift({
3492                 duration: this.repairDuration || .5,
3493                 easing: 'easeOut',
3494                 xy: xy,
3495                 stopFx: true,
3496                 callback: this.afterRepair,
3497                 scope: this
3498             });
3499         }else{
3500             this.afterRepair();
3501         }
3502     },
3503
3504     // private
3505     afterRepair : function(){
3506         this.hide(true);
3507         if(typeof this.callback == "function"){
3508             this.callback.call(this.scope || this);
3509         }
3510         this.callback = null;
3511         this.scope = null;
3512     }
3513 };/*
3514  * Based on:
3515  * Ext JS Library 1.1.1
3516  * Copyright(c) 2006-2007, Ext JS, LLC.
3517  *
3518  * Originally Released Under LGPL - original licence link has changed is not relivant.
3519  *
3520  * Fork - LGPL
3521  * <script type="text/javascript">
3522  */
3523
3524 /**
3525  * @class Roo.dd.DragSource
3526  * @extends Roo.dd.DDProxy
3527  * A simple class that provides the basic implementation needed to make any element draggable.
3528  * @constructor
3529  * @param {String/HTMLElement/Element} el The container element
3530  * @param {Object} config
3531  */
3532 Roo.dd.DragSource = function(el, config){
3533     this.el = Roo.get(el);
3534     this.dragData = {};
3535     
3536     Roo.apply(this, config);
3537     
3538     if(!this.proxy){
3539         this.proxy = new Roo.dd.StatusProxy();
3540     }
3541
3542     Roo.dd.DragSource.superclass.constructor.call(this, this.el.dom, this.ddGroup || this.group,
3543           {dragElId : this.proxy.id, resizeFrame: false, isTarget: false, scroll: this.scroll === true});
3544     
3545     this.dragging = false;
3546 };
3547
3548 Roo.extend(Roo.dd.DragSource, Roo.dd.DDProxy, {
3549     /**
3550      * @cfg {String} dropAllowed
3551      * The CSS class returned to the drag source when drop is allowed (defaults to "x-dd-drop-ok").
3552      */
3553     dropAllowed : "x-dd-drop-ok",
3554     /**
3555      * @cfg {String} dropNotAllowed
3556      * The CSS class returned to the drag source when drop is not allowed (defaults to "x-dd-drop-nodrop").
3557      */
3558     dropNotAllowed : "x-dd-drop-nodrop",
3559
3560     /**
3561      * Returns the data object associated with this drag source
3562      * @return {Object} data An object containing arbitrary data
3563      */
3564     getDragData : function(e){
3565         return this.dragData;
3566     },
3567
3568     // private
3569     onDragEnter : function(e, id){
3570         var target = Roo.dd.DragDropMgr.getDDById(id);
3571         this.cachedTarget = target;
3572         if(this.beforeDragEnter(target, e, id) !== false){
3573             if(target.isNotifyTarget){
3574                 var status = target.notifyEnter(this, e, this.dragData);
3575                 this.proxy.setStatus(status);
3576             }else{
3577                 this.proxy.setStatus(this.dropAllowed);
3578             }
3579             
3580             if(this.afterDragEnter){
3581                 /**
3582                  * An empty function by default, but provided so that you can perform a custom action
3583                  * when the dragged item enters the drop target by providing an implementation.
3584                  * @param {Roo.dd.DragDrop} target The drop target
3585                  * @param {Event} e The event object
3586                  * @param {String} id The id of the dragged element
3587                  * @method afterDragEnter
3588                  */
3589                 this.afterDragEnter(target, e, id);
3590             }
3591         }
3592     },
3593
3594     /**
3595      * An empty function by default, but provided so that you can perform a custom action
3596      * before the dragged item enters the drop target and optionally cancel the onDragEnter.
3597      * @param {Roo.dd.DragDrop} target The drop target
3598      * @param {Event} e The event object
3599      * @param {String} id The id of the dragged element
3600      * @return {Boolean} isValid True if the drag event is valid, else false to cancel
3601      */
3602     beforeDragEnter : function(target, e, id){
3603         return true;
3604     },
3605
3606     // private
3607     alignElWithMouse: function() {
3608         Roo.dd.DragSource.superclass.alignElWithMouse.apply(this, arguments);
3609         this.proxy.sync();
3610     },
3611
3612     // private
3613     onDragOver : function(e, id){
3614         var target = this.cachedTarget || Roo.dd.DragDropMgr.getDDById(id);
3615         if(this.beforeDragOver(target, e, id) !== false){
3616             if(target.isNotifyTarget){
3617                 var status = target.notifyOver(this, e, this.dragData);
3618                 this.proxy.setStatus(status);
3619             }
3620
3621             if(this.afterDragOver){
3622                 /**
3623                  * An empty function by default, but provided so that you can perform a custom action
3624                  * while the dragged item is over the drop target by providing an implementation.
3625                  * @param {Roo.dd.DragDrop} target The drop target
3626                  * @param {Event} e The event object
3627                  * @param {String} id The id of the dragged element
3628                  * @method afterDragOver
3629                  */
3630                 this.afterDragOver(target, e, id);
3631             }
3632         }
3633     },
3634
3635     /**
3636      * An empty function by default, but provided so that you can perform a custom action
3637      * while the dragged item is over the drop target and optionally cancel the onDragOver.
3638      * @param {Roo.dd.DragDrop} target The drop target
3639      * @param {Event} e The event object
3640      * @param {String} id The id of the dragged element
3641      * @return {Boolean} isValid True if the drag event is valid, else false to cancel
3642      */
3643     beforeDragOver : function(target, e, id){
3644         return true;
3645     },
3646
3647     // private
3648     onDragOut : function(e, id){
3649         var target = this.cachedTarget || Roo.dd.DragDropMgr.getDDById(id);
3650         if(this.beforeDragOut(target, e, id) !== false){
3651             if(target.isNotifyTarget){
3652                 target.notifyOut(this, e, this.dragData);
3653             }
3654             this.proxy.reset();
3655             if(this.afterDragOut){
3656                 /**
3657                  * An empty function by default, but provided so that you can perform a custom action
3658                  * after the dragged item is dragged out of the target without dropping.
3659                  * @param {Roo.dd.DragDrop} target The drop target
3660                  * @param {Event} e The event object
3661                  * @param {String} id The id of the dragged element
3662                  * @method afterDragOut
3663                  */
3664                 this.afterDragOut(target, e, id);
3665             }
3666         }
3667         this.cachedTarget = null;
3668     },
3669
3670     /**
3671      * An empty function by default, but provided so that you can perform a custom action before the dragged
3672      * item is dragged out of the target without dropping, and optionally cancel the onDragOut.
3673      * @param {Roo.dd.DragDrop} target The drop target
3674      * @param {Event} e The event object
3675      * @param {String} id The id of the dragged element
3676      * @return {Boolean} isValid True if the drag event is valid, else false to cancel
3677      */
3678     beforeDragOut : function(target, e, id){
3679         return true;
3680     },
3681     
3682     // private
3683     onDragDrop : function(e, id){
3684         var target = this.cachedTarget || Roo.dd.DragDropMgr.getDDById(id);
3685         if(this.beforeDragDrop(target, e, id) !== false){
3686             if(target.isNotifyTarget){
3687                 if(target.notifyDrop(this, e, this.dragData)){ // valid drop?
3688                     this.onValidDrop(target, e, id);
3689                 }else{
3690                     this.onInvalidDrop(target, e, id);
3691                 }
3692             }else{
3693                 this.onValidDrop(target, e, id);
3694             }
3695             
3696             if(this.afterDragDrop){
3697                 /**
3698                  * An empty function by default, but provided so that you can perform a custom action
3699                  * after a valid drag drop has occurred by providing an implementation.
3700                  * @param {Roo.dd.DragDrop} target The drop target
3701                  * @param {Event} e The event object
3702                  * @param {String} id The id of the dropped element
3703                  * @method afterDragDrop
3704                  */
3705                 this.afterDragDrop(target, e, id);
3706             }
3707         }
3708         delete this.cachedTarget;
3709     },
3710
3711     /**
3712      * An empty function by default, but provided so that you can perform a custom action before the dragged
3713      * item is dropped onto the target and optionally cancel the onDragDrop.
3714      * @param {Roo.dd.DragDrop} target The drop target
3715      * @param {Event} e The event object
3716      * @param {String} id The id of the dragged element
3717      * @return {Boolean} isValid True if the drag drop event is valid, else false to cancel
3718      */
3719     beforeDragDrop : function(target, e, id){
3720         return true;
3721     },
3722
3723     // private
3724     onValidDrop : function(target, e, id){
3725         this.hideProxy();
3726         if(this.afterValidDrop){
3727             /**
3728              * An empty function by default, but provided so that you can perform a custom action
3729              * after a valid drop has occurred by providing an implementation.
3730              * @param {Object} target The target DD 
3731              * @param {Event} e The event object
3732              * @param {String} id The id of the dropped element
3733              * @method afterInvalidDrop
3734              */
3735             this.afterValidDrop(target, e, id);
3736         }
3737     },
3738
3739     // private
3740     getRepairXY : function(e, data){
3741         return this.el.getXY();  
3742     },
3743
3744     // private
3745     onInvalidDrop : function(target, e, id){
3746         this.beforeInvalidDrop(target, e, id);
3747         if(this.cachedTarget){
3748             if(this.cachedTarget.isNotifyTarget){
3749                 this.cachedTarget.notifyOut(this, e, this.dragData);
3750             }
3751             this.cacheTarget = null;
3752         }
3753         this.proxy.repair(this.getRepairXY(e, this.dragData), this.afterRepair, this);
3754
3755         if(this.afterInvalidDrop){
3756             /**
3757              * An empty function by default, but provided so that you can perform a custom action
3758              * after an invalid drop has occurred by providing an implementation.
3759              * @param {Event} e The event object
3760              * @param {String} id The id of the dropped element
3761              * @method afterInvalidDrop
3762              */
3763             this.afterInvalidDrop(e, id);
3764         }
3765     },
3766
3767     // private
3768     afterRepair : function(){
3769         if(Roo.enableFx){
3770             this.el.highlight(this.hlColor || "c3daf9");
3771         }
3772         this.dragging = false;
3773     },
3774
3775     /**
3776      * An empty function by default, but provided so that you can perform a custom action after an invalid
3777      * drop has occurred.
3778      * @param {Roo.dd.DragDrop} target The drop target
3779      * @param {Event} e The event object
3780      * @param {String} id The id of the dragged element
3781      * @return {Boolean} isValid True if the invalid drop should proceed, else false to cancel
3782      */
3783     beforeInvalidDrop : function(target, e, id){
3784         return true;
3785     },
3786
3787     // private
3788     handleMouseDown : function(e){
3789         if(this.dragging) {
3790             return;
3791         }
3792         var data = this.getDragData(e);
3793         if(data && this.onBeforeDrag(data, e) !== false){
3794             this.dragData = data;
3795             this.proxy.stop();
3796             Roo.dd.DragSource.superclass.handleMouseDown.apply(this, arguments);
3797         } 
3798     },
3799
3800     /**
3801      * An empty function by default, but provided so that you can perform a custom action before the initial
3802      * drag event begins and optionally cancel it.
3803      * @param {Object} data An object containing arbitrary data to be shared with drop targets
3804      * @param {Event} e The event object
3805      * @return {Boolean} isValid True if the drag event is valid, else false to cancel
3806      */
3807     onBeforeDrag : function(data, e){
3808         return true;
3809     },
3810
3811     /**
3812      * An empty function by default, but provided so that you can perform a custom action once the initial
3813      * drag event has begun.  The drag cannot be canceled from this function.
3814      * @param {Number} x The x position of the click on the dragged object
3815      * @param {Number} y The y position of the click on the dragged object
3816      */
3817     onStartDrag : Roo.emptyFn,
3818
3819     // private - YUI override
3820     startDrag : function(x, y){
3821         this.proxy.reset();
3822         this.dragging = true;
3823         this.proxy.update("");
3824         this.onInitDrag(x, y);
3825         this.proxy.show();
3826     },
3827
3828     // private
3829     onInitDrag : function(x, y){
3830         var clone = this.el.dom.cloneNode(true);
3831         clone.id = Roo.id(); // prevent duplicate ids
3832         this.proxy.update(clone);
3833         this.onStartDrag(x, y);
3834         return true;
3835     },
3836
3837     /**
3838      * Returns the drag source's underlying {@link Roo.dd.StatusProxy}
3839      * @return {Roo.dd.StatusProxy} proxy The StatusProxy
3840      */
3841     getProxy : function(){
3842         return this.proxy;  
3843     },
3844
3845     /**
3846      * Hides the drag source's {@link Roo.dd.StatusProxy}
3847      */
3848     hideProxy : function(){
3849         this.proxy.hide();  
3850         this.proxy.reset(true);
3851         this.dragging = false;
3852     },
3853
3854     // private
3855     triggerCacheRefresh : function(){
3856         Roo.dd.DDM.refreshCache(this.groups);
3857     },
3858
3859     // private - override to prevent hiding
3860     b4EndDrag: function(e) {
3861     },
3862
3863     // private - override to prevent moving
3864     endDrag : function(e){
3865         this.onEndDrag(this.dragData, e);
3866     },
3867
3868     // private
3869     onEndDrag : function(data, e){
3870     },
3871     
3872     // private - pin to cursor
3873     autoOffset : function(x, y) {
3874         this.setDelta(-12, -20);
3875     }    
3876 });/*
3877  * Based on:
3878  * Ext JS Library 1.1.1
3879  * Copyright(c) 2006-2007, Ext JS, LLC.
3880  *
3881  * Originally Released Under LGPL - original licence link has changed is not relivant.
3882  *
3883  * Fork - LGPL
3884  * <script type="text/javascript">
3885  */
3886
3887
3888 /**
3889  * @class Roo.dd.DropTarget
3890  * @extends Roo.dd.DDTarget
3891  * A simple class that provides the basic implementation needed to make any element a drop target that can have
3892  * draggable items dropped onto it.  The drop has no effect until an implementation of notifyDrop is provided.
3893  * @constructor
3894  * @param {String/HTMLElement/Element} el The container element
3895  * @param {Object} config
3896  */
3897 Roo.dd.DropTarget = function(el, config){
3898     this.el = Roo.get(el);
3899     
3900     var listeners = false; ;
3901     if (config && config.listeners) {
3902         listeners= config.listeners;
3903         delete config.listeners;
3904     }
3905     Roo.apply(this, config);
3906     
3907     if(this.containerScroll){
3908         Roo.dd.ScrollManager.register(this.el);
3909     }
3910     this.addEvents( {
3911          /**
3912          * @scope Roo.dd.DropTarget
3913          */
3914          
3915          /**
3916          * @event enter
3917          * The function a {@link Roo.dd.DragSource} calls once to notify this drop target that the source is now over the
3918          * target.  This default implementation adds the CSS class specified by overClass (if any) to the drop element
3919          * and returns the dropAllowed config value.  This method should be overridden if drop validation is required.
3920          * 
3921          * IMPORTANT : it should set this.overClass and this.dropAllowed
3922          * 
3923          * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop target
3924          * @param {Event} e The event
3925          * @param {Object} data An object containing arbitrary data supplied by the drag source
3926          */
3927         "enter" : true,
3928         
3929          /**
3930          * @event over
3931          * The function a {@link Roo.dd.DragSource} calls continuously while it is being dragged over the target.
3932          * This method will be called on every mouse movement while the drag source is over the drop target.
3933          * This default implementation simply returns the dropAllowed config value.
3934          * 
3935          * IMPORTANT : it should set this.dropAllowed
3936          * 
3937          * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop target
3938          * @param {Event} e The event
3939          * @param {Object} data An object containing arbitrary data supplied by the drag source
3940          
3941          */
3942         "over" : true,
3943         /**
3944          * @event out
3945          * The function a {@link Roo.dd.DragSource} calls once to notify this drop target that the source has been dragged
3946          * out of the target without dropping.  This default implementation simply removes the CSS class specified by
3947          * overClass (if any) from the drop element.
3948          * 
3949          * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop target
3950          * @param {Event} e The event
3951          * @param {Object} data An object containing arbitrary data supplied by the drag source
3952          */
3953          "out" : true,
3954          
3955         /**
3956          * @event drop
3957          * The function a {@link Roo.dd.DragSource} calls once to notify this drop target that the dragged item has
3958          * been dropped on it.  This method has no default implementation and returns false, so you must provide an
3959          * implementation that does something to process the drop event and returns true so that the drag source's
3960          * repair action does not run.
3961          * 
3962          * IMPORTANT : it should set this.success
3963          * 
3964          * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop target
3965          * @param {Event} e The event
3966          * @param {Object} data An object containing arbitrary data supplied by the drag source
3967         */
3968          "drop" : true
3969     });
3970             
3971      
3972     Roo.dd.DropTarget.superclass.constructor.call(  this, 
3973         this.el.dom, 
3974         this.ddGroup || this.group,
3975         {
3976             isTarget: true,
3977             listeners : listeners || {} 
3978            
3979         
3980         }
3981     );
3982
3983 };
3984
3985 Roo.extend(Roo.dd.DropTarget, Roo.dd.DDTarget, {
3986     /**
3987      * @cfg {String} overClass
3988      * The CSS class applied to the drop target element while the drag source is over it (defaults to "").
3989      */
3990      /**
3991      * @cfg {String} ddGroup
3992      * The drag drop group to handle drop events for
3993      */
3994      
3995     /**
3996      * @cfg {String} dropAllowed
3997      * The CSS class returned to the drag source when drop is allowed (defaults to "x-dd-drop-ok").
3998      */
3999     dropAllowed : "x-dd-drop-ok",
4000     /**
4001      * @cfg {String} dropNotAllowed
4002      * The CSS class returned to the drag source when drop is not allowed (defaults to "x-dd-drop-nodrop").
4003      */
4004     dropNotAllowed : "x-dd-drop-nodrop",
4005     /**
4006      * @cfg {boolean} success
4007      * set this after drop listener.. 
4008      */
4009     success : false,
4010     /**
4011      * @cfg {boolean|String} valid true/false or string (ok-add/ok-sub/ok/nodrop)
4012      * if the drop point is valid for over/enter..
4013      */
4014     valid : false,
4015     // private
4016     isTarget : true,
4017
4018     // private
4019     isNotifyTarget : true,
4020     
4021     /**
4022      * @hide
4023      */
4024     notifyEnter : function(dd, e, data)
4025     {
4026         this.valid = true;
4027         this.fireEvent('enter', dd, e, data);
4028         if(this.overClass){
4029             this.el.addClass(this.overClass);
4030         }
4031         return typeof(this.valid) == 'string' ? 'x-dd-drop-' + this.valid : (
4032             this.valid ? this.dropAllowed : this.dropNotAllowed
4033         );
4034     },
4035
4036     /**
4037      * @hide
4038      */
4039     notifyOver : function(dd, e, data)
4040     {
4041         this.valid = true;
4042         this.fireEvent('over', dd, e, data);
4043         return typeof(this.valid) == 'string' ? 'x-dd-drop-' + this.valid : (
4044             this.valid ? this.dropAllowed : this.dropNotAllowed
4045         );
4046     },
4047
4048     /**
4049      * @hide
4050      */
4051     notifyOut : function(dd, e, data)
4052     {
4053         this.fireEvent('out', dd, e, data);
4054         if(this.overClass){
4055             this.el.removeClass(this.overClass);
4056         }
4057     },
4058
4059     /**
4060      * @hide
4061      */
4062     notifyDrop : function(dd, e, data)
4063     {
4064         this.success = false;
4065         this.fireEvent('drop', dd, e, data);
4066         return this.success;
4067     }
4068 });/*
4069  * Based on:
4070  * Ext JS Library 1.1.1
4071  * Copyright(c) 2006-2007, Ext JS, LLC.
4072  *
4073  * Originally Released Under LGPL - original licence link has changed is not relivant.
4074  *
4075  * Fork - LGPL
4076  * <script type="text/javascript">
4077  */
4078
4079
4080 /**
4081  * @class Roo.dd.DragZone
4082  * @extends Roo.dd.DragSource
4083  * This class provides a container DD instance that proxies for multiple child node sources.<br />
4084  * By default, this class requires that draggable child nodes are registered with {@link Roo.dd.Registry}.
4085  * @constructor
4086  * @param {String/HTMLElement/Element} el The container element
4087  * @param {Object} config
4088  */
4089 Roo.dd.DragZone = function(el, config){
4090     Roo.dd.DragZone.superclass.constructor.call(this, el, config);
4091     if(this.containerScroll){
4092         Roo.dd.ScrollManager.register(this.el);
4093     }
4094 };
4095
4096 Roo.extend(Roo.dd.DragZone, Roo.dd.DragSource, {
4097     /**
4098      * @cfg {Boolean} containerScroll True to register this container with the Scrollmanager
4099      * for auto scrolling during drag operations.
4100      */
4101     /**
4102      * @cfg {String} hlColor The color to use when visually highlighting the drag source in the afterRepair
4103      * method after a failed drop (defaults to "c3daf9" - light blue)
4104      */
4105
4106     /**
4107      * Called when a mousedown occurs in this container. Looks in {@link Roo.dd.Registry}
4108      * for a valid target to drag based on the mouse down. Override this method
4109      * to provide your own lookup logic (e.g. finding a child by class name). Make sure your returned
4110      * object has a "ddel" attribute (with an HTML Element) for other functions to work.
4111      * @param {EventObject} e The mouse down event
4112      * @return {Object} The dragData
4113      */
4114     getDragData : function(e){
4115         return Roo.dd.Registry.getHandleFromEvent(e);
4116     },
4117     
4118     /**
4119      * Called once drag threshold has been reached to initialize the proxy element. By default, it clones the
4120      * this.dragData.ddel
4121      * @param {Number} x The x position of the click on the dragged object
4122      * @param {Number} y The y position of the click on the dragged object
4123      * @return {Boolean} true to continue the drag, false to cancel
4124      */
4125     onInitDrag : function(x, y){
4126         this.proxy.update(this.dragData.ddel.cloneNode(true));
4127         this.onStartDrag(x, y);
4128         return true;
4129     },
4130     
4131     /**
4132      * Called after a repair of an invalid drop. By default, highlights this.dragData.ddel 
4133      */
4134     afterRepair : function(){
4135         if(Roo.enableFx){
4136             Roo.Element.fly(this.dragData.ddel).highlight(this.hlColor || "c3daf9");
4137         }
4138         this.dragging = false;
4139     },
4140
4141     /**
4142      * Called before a repair of an invalid drop to get the XY to animate to. By default returns
4143      * the XY of this.dragData.ddel
4144      * @param {EventObject} e The mouse up event
4145      * @return {Array} The xy location (e.g. [100, 200])
4146      */
4147     getRepairXY : function(e){
4148         return Roo.Element.fly(this.dragData.ddel).getXY();  
4149     }
4150 });/*
4151  * Based on:
4152  * Ext JS Library 1.1.1
4153  * Copyright(c) 2006-2007, Ext JS, LLC.
4154  *
4155  * Originally Released Under LGPL - original licence link has changed is not relivant.
4156  *
4157  * Fork - LGPL
4158  * <script type="text/javascript">
4159  */
4160 /**
4161  * @class Roo.dd.DropZone
4162  * @extends Roo.dd.DropTarget
4163  * This class provides a container DD instance that proxies for multiple child node targets.<br />
4164  * By default, this class requires that child nodes accepting drop are registered with {@link Roo.dd.Registry}.
4165  * @constructor
4166  * @param {String/HTMLElement/Element} el The container element
4167  * @param {Object} config
4168  */
4169 Roo.dd.DropZone = function(el, config){
4170     Roo.dd.DropZone.superclass.constructor.call(this, el, config);
4171 };
4172
4173 Roo.extend(Roo.dd.DropZone, Roo.dd.DropTarget, {
4174     /**
4175      * Returns a custom data object associated with the DOM node that is the target of the event.  By default
4176      * this looks up the event target in the {@link Roo.dd.Registry}, although you can override this method to
4177      * provide your own custom lookup.
4178      * @param {Event} e The event
4179      * @return {Object} data The custom data
4180      */
4181     getTargetFromEvent : function(e){
4182         return Roo.dd.Registry.getTargetFromEvent(e);
4183     },
4184
4185     /**
4186      * Called internally when the DropZone determines that a {@link Roo.dd.DragSource} has entered a drop node
4187      * that it has registered.  This method has no default implementation and should be overridden to provide
4188      * node-specific processing if necessary.
4189      * @param {Object} nodeData The custom data associated with the drop node (this is the same value returned from 
4190      * {@link #getTargetFromEvent} for this node)
4191      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
4192      * @param {Event} e The event
4193      * @param {Object} data An object containing arbitrary data supplied by the drag source
4194      */
4195     onNodeEnter : function(n, dd, e, data){
4196         
4197     },
4198
4199     /**
4200      * Called internally while the DropZone determines that a {@link Roo.dd.DragSource} is over a drop node
4201      * that it has registered.  The default implementation returns this.dropNotAllowed, so it should be
4202      * overridden to provide the proper feedback.
4203      * @param {Object} nodeData The custom data associated with the drop node (this is the same value returned from
4204      * {@link #getTargetFromEvent} for this node)
4205      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
4206      * @param {Event} e The event
4207      * @param {Object} data An object containing arbitrary data supplied by the drag source
4208      * @return {String} status The CSS class that communicates the drop status back to the source so that the
4209      * underlying {@link Roo.dd.StatusProxy} can be updated
4210      */
4211     onNodeOver : function(n, dd, e, data){
4212         return this.dropAllowed;
4213     },
4214
4215     /**
4216      * Called internally when the DropZone determines that a {@link Roo.dd.DragSource} has been dragged out of
4217      * the drop node without dropping.  This method has no default implementation and should be overridden to provide
4218      * node-specific processing if necessary.
4219      * @param {Object} nodeData The custom data associated with the drop node (this is the same value returned from
4220      * {@link #getTargetFromEvent} for this node)
4221      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
4222      * @param {Event} e The event
4223      * @param {Object} data An object containing arbitrary data supplied by the drag source
4224      */
4225     onNodeOut : function(n, dd, e, data){
4226         
4227     },
4228
4229     /**
4230      * Called internally when the DropZone determines that a {@link Roo.dd.DragSource} has been dropped onto
4231      * the drop node.  The default implementation returns false, so it should be overridden to provide the
4232      * appropriate processing of the drop event and return true so that the drag source's repair action does not run.
4233      * @param {Object} nodeData The custom data associated with the drop node (this is the same value returned from
4234      * {@link #getTargetFromEvent} for this node)
4235      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
4236      * @param {Event} e The event
4237      * @param {Object} data An object containing arbitrary data supplied by the drag source
4238      * @return {Boolean} True if the drop was valid, else false
4239      */
4240     onNodeDrop : function(n, dd, e, data){
4241         return false;
4242     },
4243
4244     /**
4245      * Called internally while the DropZone determines that a {@link Roo.dd.DragSource} is being dragged over it,
4246      * but not over any of its registered drop nodes.  The default implementation returns this.dropNotAllowed, so
4247      * it should be overridden to provide the proper feedback if necessary.
4248      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
4249      * @param {Event} e The event
4250      * @param {Object} data An object containing arbitrary data supplied by the drag source
4251      * @return {String} status The CSS class that communicates the drop status back to the source so that the
4252      * underlying {@link Roo.dd.StatusProxy} can be updated
4253      */
4254     onContainerOver : function(dd, e, data){
4255         return this.dropNotAllowed;
4256     },
4257
4258     /**
4259      * Called internally when the DropZone determines that a {@link Roo.dd.DragSource} has been dropped on it,
4260      * but not on any of its registered drop nodes.  The default implementation returns false, so it should be
4261      * overridden to provide the appropriate processing of the drop event if you need the drop zone itself to
4262      * be able to accept drops.  It should return true when valid so that the drag source's repair action does not run.
4263      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
4264      * @param {Event} e The event
4265      * @param {Object} data An object containing arbitrary data supplied by the drag source
4266      * @return {Boolean} True if the drop was valid, else false
4267      */
4268     onContainerDrop : function(dd, e, data){
4269         return false;
4270     },
4271
4272     /**
4273      * The function a {@link Roo.dd.DragSource} calls once to notify this drop zone that the source is now over
4274      * the zone.  The default implementation returns this.dropNotAllowed and expects that only registered drop
4275      * nodes can process drag drop operations, so if you need the drop zone itself to be able to process drops
4276      * you should override this method and provide a custom implementation.
4277      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
4278      * @param {Event} e The event
4279      * @param {Object} data An object containing arbitrary data supplied by the drag source
4280      * @return {String} status The CSS class that communicates the drop status back to the source so that the
4281      * underlying {@link Roo.dd.StatusProxy} can be updated
4282      */
4283     notifyEnter : function(dd, e, data){
4284         return this.dropNotAllowed;
4285     },
4286
4287     /**
4288      * The function a {@link Roo.dd.DragSource} calls continuously while it is being dragged over the drop zone.
4289      * This method will be called on every mouse movement while the drag source is over the drop zone.
4290      * It will call {@link #onNodeOver} while the drag source is over a registered node, and will also automatically
4291      * delegate to the appropriate node-specific methods as necessary when the drag source enters and exits
4292      * registered nodes ({@link #onNodeEnter}, {@link #onNodeOut}). If the drag source is not currently over a
4293      * registered node, it will call {@link #onContainerOver}.
4294      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
4295      * @param {Event} e The event
4296      * @param {Object} data An object containing arbitrary data supplied by the drag source
4297      * @return {String} status The CSS class that communicates the drop status back to the source so that the
4298      * underlying {@link Roo.dd.StatusProxy} can be updated
4299      */
4300     notifyOver : function(dd, e, data){
4301         var n = this.getTargetFromEvent(e);
4302         if(!n){ // not over valid drop target
4303             if(this.lastOverNode){
4304                 this.onNodeOut(this.lastOverNode, dd, e, data);
4305                 this.lastOverNode = null;
4306             }
4307             return this.onContainerOver(dd, e, data);
4308         }
4309         if(this.lastOverNode != n){
4310             if(this.lastOverNode){
4311                 this.onNodeOut(this.lastOverNode, dd, e, data);
4312             }
4313             this.onNodeEnter(n, dd, e, data);
4314             this.lastOverNode = n;
4315         }
4316         return this.onNodeOver(n, dd, e, data);
4317     },
4318
4319     /**
4320      * The function a {@link Roo.dd.DragSource} calls once to notify this drop zone that the source has been dragged
4321      * out of the zone without dropping.  If the drag source is currently over a registered node, the notification
4322      * will be delegated to {@link #onNodeOut} for node-specific handling, otherwise it will be ignored.
4323      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop target
4324      * @param {Event} e The event
4325      * @param {Object} data An object containing arbitrary data supplied by the drag zone
4326      */
4327     notifyOut : function(dd, e, data){
4328         if(this.lastOverNode){
4329             this.onNodeOut(this.lastOverNode, dd, e, data);
4330             this.lastOverNode = null;
4331         }
4332     },
4333
4334     /**
4335      * The function a {@link Roo.dd.DragSource} calls once to notify this drop zone that the dragged item has
4336      * been dropped on it.  The drag zone will look up the target node based on the event passed in, and if there
4337      * is a node registered for that event, it will delegate to {@link #onNodeDrop} for node-specific handling,
4338      * otherwise it will call {@link #onContainerDrop}.
4339      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
4340      * @param {Event} e The event
4341      * @param {Object} data An object containing arbitrary data supplied by the drag source
4342      * @return {Boolean} True if the drop was valid, else false
4343      */
4344     notifyDrop : function(dd, e, data){
4345         if(this.lastOverNode){
4346             this.onNodeOut(this.lastOverNode, dd, e, data);
4347             this.lastOverNode = null;
4348         }
4349         var n = this.getTargetFromEvent(e);
4350         return n ?
4351             this.onNodeDrop(n, dd, e, data) :
4352             this.onContainerDrop(dd, e, data);
4353     },
4354
4355     // private
4356     triggerCacheRefresh : function(){
4357         Roo.dd.DDM.refreshCache(this.groups);
4358     }  
4359 });/*
4360  * Based on:
4361  * Ext JS Library 1.1.1
4362  * Copyright(c) 2006-2007, Ext JS, LLC.
4363  *
4364  * Originally Released Under LGPL - original licence link has changed is not relivant.
4365  *
4366  * Fork - LGPL
4367  * <script type="text/javascript">
4368  */
4369
4370
4371 /**
4372  * @class Roo.data.SortTypes
4373  * @singleton
4374  * Defines the default sorting (casting?) comparison functions used when sorting data.
4375  */
4376 Roo.data.SortTypes = {
4377     /**
4378      * Default sort that does nothing
4379      * @param {Mixed} s The value being converted
4380      * @return {Mixed} The comparison value
4381      */
4382     none : function(s){
4383         return s;
4384     },
4385     
4386     /**
4387      * The regular expression used to strip tags
4388      * @type {RegExp}
4389      * @property
4390      */
4391     stripTagsRE : /<\/?[^>]+>/gi,
4392     
4393     /**
4394      * Strips all HTML tags to sort on text only
4395      * @param {Mixed} s The value being converted
4396      * @return {String} The comparison value
4397      */
4398     asText : function(s){
4399         return String(s).replace(this.stripTagsRE, "");
4400     },
4401     
4402     /**
4403      * Strips all HTML tags to sort on text only - Case insensitive
4404      * @param {Mixed} s The value being converted
4405      * @return {String} The comparison value
4406      */
4407     asUCText : function(s){
4408         return String(s).toUpperCase().replace(this.stripTagsRE, "");
4409     },
4410     
4411     /**
4412      * Case insensitive string
4413      * @param {Mixed} s The value being converted
4414      * @return {String} The comparison value
4415      */
4416     asUCString : function(s) {
4417         return String(s).toUpperCase();
4418     },
4419     
4420     /**
4421      * Date sorting
4422      * @param {Mixed} s The value being converted
4423      * @return {Number} The comparison value
4424      */
4425     asDate : function(s) {
4426         if(!s){
4427             return 0;
4428         }
4429         if(s instanceof Date){
4430             return s.getTime();
4431         }
4432         return Date.parse(String(s));
4433     },
4434     
4435     /**
4436      * Float sorting
4437      * @param {Mixed} s The value being converted
4438      * @return {Float} The comparison value
4439      */
4440     asFloat : function(s) {
4441         var val = parseFloat(String(s).replace(/,/g, ""));
4442         if(isNaN(val)) val = 0;
4443         return val;
4444     },
4445     
4446     /**
4447      * Integer sorting
4448      * @param {Mixed} s The value being converted
4449      * @return {Number} The comparison value
4450      */
4451     asInt : function(s) {
4452         var val = parseInt(String(s).replace(/,/g, ""));
4453         if(isNaN(val)) val = 0;
4454         return val;
4455     }
4456 };/*
4457  * Based on:
4458  * Ext JS Library 1.1.1
4459  * Copyright(c) 2006-2007, Ext JS, LLC.
4460  *
4461  * Originally Released Under LGPL - original licence link has changed is not relivant.
4462  *
4463  * Fork - LGPL
4464  * <script type="text/javascript">
4465  */
4466
4467 /**
4468 * @class Roo.data.Record
4469  * Instances of this class encapsulate both record <em>definition</em> information, and record
4470  * <em>value</em> information for use in {@link Roo.data.Store} objects, or any code which needs
4471  * to access Records cached in an {@link Roo.data.Store} object.<br>
4472  * <p>
4473  * Constructors for this class are generated by passing an Array of field definition objects to {@link #create}.
4474  * Instances are usually only created by {@link Roo.data.Reader} implementations when processing unformatted data
4475  * objects.<br>
4476  * <p>
4477  * Record objects generated by this constructor inherit all the methods of Roo.data.Record listed below.
4478  * @constructor
4479  * This constructor should not be used to create Record objects. Instead, use the constructor generated by
4480  * {@link #create}. The parameters are the same.
4481  * @param {Array} data An associative Array of data values keyed by the field name.
4482  * @param {Object} id (Optional) The id of the record. This id should be unique, and is used by the
4483  * {@link Roo.data.Store} object which owns the Record to index its collection of Records. If
4484  * not specified an integer id is generated.
4485  */
4486 Roo.data.Record = function(data, id){
4487     this.id = (id || id === 0) ? id : ++Roo.data.Record.AUTO_ID;
4488     this.data = data;
4489 };
4490
4491 /**
4492  * Generate a constructor for a specific record layout.
4493  * @param {Array} o An Array of field definition objects which specify field names, and optionally,
4494  * data types, and a mapping for an {@link Roo.data.Reader} to extract the field's value from a data object.
4495  * Each field definition object may contain the following properties: <ul>
4496  * <li><b>name</b> : String<p style="margin-left:1em">The name by which the field is referenced within the Record. This is referenced by,
4497  * for example the <em>dataIndex</em> property in column definition objects passed to {@link Roo.grid.ColumnModel}</p></li>
4498  * <li><b>mapping</b> : String<p style="margin-left:1em">(Optional) A path specification for use by the {@link Roo.data.Reader} implementation
4499  * that is creating the Record to access the data value from the data object. If an {@link Roo.data.JsonReader}
4500  * is being used, then this is a string containing the javascript expression to reference the data relative to 
4501  * the record item's root. If an {@link Roo.data.XmlReader} is being used, this is an {@link Roo.DomQuery} path
4502  * to the data item relative to the record element. If the mapping expression is the same as the field name,
4503  * this may be omitted.</p></li>
4504  * <li><b>type</b> : String<p style="margin-left:1em">(Optional) The data type for conversion to displayable value. Possible values are
4505  * <ul><li>auto (Default, implies no conversion)</li>
4506  * <li>string</li>
4507  * <li>int</li>
4508  * <li>float</li>
4509  * <li>boolean</li>
4510  * <li>date</li></ul></p></li>
4511  * <li><b>sortType</b> : Mixed<p style="margin-left:1em">(Optional) A member of {@link Roo.data.SortTypes}.</p></li>
4512  * <li><b>sortDir</b> : String<p style="margin-left:1em">(Optional) Initial direction to sort. "ASC" or "DESC"</p></li>
4513  * <li><b>convert</b> : Function<p style="margin-left:1em">(Optional) A function which converts the value provided
4514  * by the Reader into an object that will be stored in the Record. It is passed the
4515  * following parameters:<ul>
4516  * <li><b>v</b> : Mixed<p style="margin-left:1em">The data value as read by the Reader.</p></li>
4517  * </ul></p></li>
4518  * <li><b>dateFormat</b> : String<p style="margin-left:1em">(Optional) A format String for the Date.parseDate function.</p></li>
4519  * </ul>
4520  * <br>usage:<br><pre><code>
4521 var TopicRecord = Roo.data.Record.create(
4522     {name: 'title', mapping: 'topic_title'},
4523     {name: 'author', mapping: 'username'},
4524     {name: 'totalPosts', mapping: 'topic_replies', type: 'int'},
4525     {name: 'lastPost', mapping: 'post_time', type: 'date'},
4526     {name: 'lastPoster', mapping: 'user2'},
4527     {name: 'excerpt', mapping: 'post_text'}
4528 );
4529
4530 var myNewRecord = new TopicRecord({
4531     title: 'Do my job please',
4532     author: 'noobie',
4533     totalPosts: 1,
4534     lastPost: new Date(),
4535     lastPoster: 'Animal',
4536     excerpt: 'No way dude!'
4537 });
4538 myStore.add(myNewRecord);
4539 </code></pre>
4540  * @method create
4541  * @static
4542  */
4543 Roo.data.Record.create = function(o){
4544     var f = function(){
4545         f.superclass.constructor.apply(this, arguments);
4546     };
4547     Roo.extend(f, Roo.data.Record);
4548     var p = f.prototype;
4549     p.fields = new Roo.util.MixedCollection(false, function(field){
4550         return field.name;
4551     });
4552     for(var i = 0, len = o.length; i < len; i++){
4553         p.fields.add(new Roo.data.Field(o[i]));
4554     }
4555     f.getField = function(name){
4556         return p.fields.get(name);  
4557     };
4558     return f;
4559 };
4560
4561 Roo.data.Record.AUTO_ID = 1000;
4562 Roo.data.Record.EDIT = 'edit';
4563 Roo.data.Record.REJECT = 'reject';
4564 Roo.data.Record.COMMIT = 'commit';
4565
4566 Roo.data.Record.prototype = {
4567     /**
4568      * Readonly flag - true if this record has been modified.
4569      * @type Boolean
4570      */
4571     dirty : false,
4572     editing : false,
4573     error: null,
4574     modified: null,
4575
4576     // private
4577     join : function(store){
4578         this.store = store;
4579     },
4580
4581     /**
4582      * Set the named field to the specified value.
4583      * @param {String} name The name of the field to set.
4584      * @param {Object} value The value to set the field to.
4585      */
4586     set : function(name, value){
4587         if(this.data[name] == value){
4588             return;
4589         }
4590         this.dirty = true;
4591         if(!this.modified){
4592             this.modified = {};
4593         }
4594         if(typeof this.modified[name] == 'undefined'){
4595             this.modified[name] = this.data[name];
4596         }
4597         this.data[name] = value;
4598         if(!this.editing && this.store){
4599             this.store.afterEdit(this);
4600         }       
4601     },
4602
4603     /**
4604      * Get the value of the named field.
4605      * @param {String} name The name of the field to get the value of.
4606      * @return {Object} The value of the field.
4607      */
4608     get : function(name){
4609         return this.data[name]; 
4610     },
4611
4612     // private
4613     beginEdit : function(){
4614         this.editing = true;
4615         this.modified = {}; 
4616     },
4617
4618     // private
4619     cancelEdit : function(){
4620         this.editing = false;
4621         delete this.modified;
4622     },
4623
4624     // private
4625     endEdit : function(){
4626         this.editing = false;
4627         if(this.dirty && this.store){
4628             this.store.afterEdit(this);
4629         }
4630     },
4631
4632     /**
4633      * Usually called by the {@link Roo.data.Store} which owns the Record.
4634      * Rejects all changes made to the Record since either creation, or the last commit operation.
4635      * Modified fields are reverted to their original values.
4636      * <p>
4637      * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
4638      * of reject operations.
4639      */
4640     reject : function(){
4641         var m = this.modified;
4642         for(var n in m){
4643             if(typeof m[n] != "function"){
4644                 this.data[n] = m[n];
4645             }
4646         }
4647         this.dirty = false;
4648         delete this.modified;
4649         this.editing = false;
4650         if(this.store){
4651             this.store.afterReject(this);
4652         }
4653     },
4654
4655     /**
4656      * Usually called by the {@link Roo.data.Store} which owns the Record.
4657      * Commits all changes made to the Record since either creation, or the last commit operation.
4658      * <p>
4659      * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
4660      * of commit operations.
4661      */
4662     commit : function(){
4663         this.dirty = false;
4664         delete this.modified;
4665         this.editing = false;
4666         if(this.store){
4667             this.store.afterCommit(this);
4668         }
4669     },
4670
4671     // private
4672     hasError : function(){
4673         return this.error != null;
4674     },
4675
4676     // private
4677     clearError : function(){
4678         this.error = null;
4679     },
4680
4681     /**
4682      * Creates a copy of this record.
4683      * @param {String} id (optional) A new record id if you don't want to use this record's id
4684      * @return {Record}
4685      */
4686     copy : function(newId) {
4687         return new this.constructor(Roo.apply({}, this.data), newId || this.id);
4688     }
4689 };/*
4690  * Based on:
4691  * Ext JS Library 1.1.1
4692  * Copyright(c) 2006-2007, Ext JS, LLC.
4693  *
4694  * Originally Released Under LGPL - original licence link has changed is not relivant.
4695  *
4696  * Fork - LGPL
4697  * <script type="text/javascript">
4698  */
4699
4700
4701
4702 /**
4703  * @class Roo.data.Store
4704  * @extends Roo.util.Observable
4705  * The Store class encapsulates a client side cache of {@link Roo.data.Record} objects which provide input data
4706  * for widgets such as the Roo.grid.Grid, or the Roo.form.ComboBox.<br>
4707  * <p>
4708  * A Store object uses an implementation of {@link Roo.data.DataProxy} to access a data object unless you call loadData() directly and pass in your data. The Store object
4709  * has no knowledge of the format of the data returned by the Proxy.<br>
4710  * <p>
4711  * A Store object uses its configured implementation of {@link Roo.data.DataReader} to create {@link Roo.data.Record}
4712  * instances from the data object. These records are cached and made available through accessor functions.
4713  * @constructor
4714  * Creates a new Store.
4715  * @param {Object} config A config object containing the objects needed for the Store to access data,
4716  * and read the data into Records.
4717  */
4718 Roo.data.Store = function(config){
4719     this.data = new Roo.util.MixedCollection(false);
4720     this.data.getKey = function(o){
4721         return o.id;
4722     };
4723     this.baseParams = {};
4724     // private
4725     this.paramNames = {
4726         "start" : "start",
4727         "limit" : "limit",
4728         "sort" : "sort",
4729         "dir" : "dir",
4730         "multisort" : "_multisort"
4731     };
4732
4733     if(config && config.data){
4734         this.inlineData = config.data;
4735         delete config.data;
4736     }
4737
4738     Roo.apply(this, config);
4739     
4740     if(this.reader){ // reader passed
4741         this.reader = Roo.factory(this.reader, Roo.data);
4742         this.reader.xmodule = this.xmodule || false;
4743         if(!this.recordType){
4744             this.recordType = this.reader.recordType;
4745         }
4746         if(this.reader.onMetaChange){
4747             this.reader.onMetaChange = this.onMetaChange.createDelegate(this);
4748         }
4749     }
4750
4751     if(this.recordType){
4752         this.fields = this.recordType.prototype.fields;
4753     }
4754     this.modified = [];
4755
4756     this.addEvents({
4757         /**
4758          * @event datachanged
4759          * Fires when the data cache has changed, and a widget which is using this Store
4760          * as a Record cache should refresh its view.
4761          * @param {Store} this
4762          */
4763         datachanged : true,
4764         /**
4765          * @event metachange
4766          * Fires when this store's reader provides new metadata (fields). This is currently only support for JsonReaders.
4767          * @param {Store} this
4768          * @param {Object} meta The JSON metadata
4769          */
4770         metachange : true,
4771         /**
4772          * @event add
4773          * Fires when Records have been added to the Store
4774          * @param {Store} this
4775          * @param {Roo.data.Record[]} records The array of Records added
4776          * @param {Number} index The index at which the record(s) were added
4777          */
4778         add : true,
4779         /**
4780          * @event remove
4781          * Fires when a Record has been removed from the Store
4782          * @param {Store} this
4783          * @param {Roo.data.Record} record The Record that was removed
4784          * @param {Number} index The index at which the record was removed
4785          */
4786         remove : true,
4787         /**
4788          * @event update
4789          * Fires when a Record has been updated
4790          * @param {Store} this
4791          * @param {Roo.data.Record} record The Record that was updated
4792          * @param {String} operation The update operation being performed.  Value may be one of:
4793          * <pre><code>
4794  Roo.data.Record.EDIT
4795  Roo.data.Record.REJECT
4796  Roo.data.Record.COMMIT
4797          * </code></pre>
4798          */
4799         update : true,
4800         /**
4801          * @event clear
4802          * Fires when the data cache has been cleared.
4803          * @param {Store} this
4804          */
4805         clear : true,
4806         /**
4807          * @event beforeload
4808          * Fires before a request is made for a new data object.  If the beforeload handler returns false
4809          * the load action will be canceled.
4810          * @param {Store} this
4811          * @param {Object} options The loading options that were specified (see {@link #load} for details)
4812          */
4813         beforeload : true,
4814         /**
4815          * @event beforeloadadd
4816          * Fires after a new set of Records has been loaded.
4817          * @param {Store} this
4818          * @param {Roo.data.Record[]} records The Records that were loaded
4819          * @param {Object} options The loading options that were specified (see {@link #load} for details)
4820          */
4821         beforeloadadd : true,
4822         /**
4823          * @event load
4824          * Fires after a new set of Records has been loaded, before they are added to the store.
4825          * @param {Store} this
4826          * @param {Roo.data.Record[]} records The Records that were loaded
4827          * @param {Object} options The loading options that were specified (see {@link #load} for details)
4828          * @params {Object} return from reader
4829          */
4830         load : true,
4831         /**
4832          * @event loadexception
4833          * Fires if an exception occurs in the Proxy during loading.
4834          * Called with the signature of the Proxy's "loadexception" event.
4835          * If you return Json { data: [] , success: false, .... } then this will be thrown with the following args
4836          * 
4837          * @param {Proxy} 
4838          * @param {Object} return from JsonData.reader() - success, totalRecords, records
4839          * @param {Object} load options 
4840          * @param {Object} jsonData from your request (normally this contains the Exception)
4841          */
4842         loadexception : true
4843     });
4844     
4845     if(this.proxy){
4846         this.proxy = Roo.factory(this.proxy, Roo.data);
4847         this.proxy.xmodule = this.xmodule || false;
4848         this.relayEvents(this.proxy,  ["loadexception"]);
4849     }
4850     this.sortToggle = {};
4851     this.sortOrder = []; // array of order of sorting - updated by grid if multisort is enabled.
4852
4853     Roo.data.Store.superclass.constructor.call(this);
4854
4855     if(this.inlineData){
4856         this.loadData(this.inlineData);
4857         delete this.inlineData;
4858     }
4859 };
4860
4861 Roo.extend(Roo.data.Store, Roo.util.Observable, {
4862      /**
4863     * @cfg {boolean} isLocal   flag if data is locally available (and can be always looked up
4864     * without a remote query - used by combo/forms at present.
4865     */
4866     
4867     /**
4868     * @cfg {Roo.data.DataProxy} proxy The Proxy object which provides access to a data object.
4869     */
4870     /**
4871     * @cfg {Array} data Inline data to be loaded when the store is initialized.
4872     */
4873     /**
4874     * @cfg {Roo.data.Reader} reader The Reader object which processes the data object and returns
4875     * an Array of Roo.data.record objects which are cached keyed by their <em>id</em> property.
4876     */
4877     /**
4878     * @cfg {Object} baseParams An object containing properties which are to be sent as parameters
4879     * on any HTTP request
4880     */
4881     /**
4882     * @cfg {Object} sortInfo A config object in the format: {field: "fieldName", direction: "ASC|DESC"}
4883     */
4884     /**
4885     * @cfg {Boolean} multiSort enable multi column sorting (sort is based on the order of columns, remote only at present)
4886     */
4887     multiSort: false,
4888     /**
4889     * @cfg {boolean} remoteSort True if sorting is to be handled by requesting the Proxy to provide a refreshed
4890     * version of the data object in sorted order, as opposed to sorting the Record cache in place (defaults to false).
4891     */
4892     remoteSort : false,
4893
4894     /**
4895     * @cfg {boolean} pruneModifiedRecords True to clear all modified record information each time the store is
4896      * loaded or when a record is removed. (defaults to false).
4897     */
4898     pruneModifiedRecords : false,
4899
4900     // private
4901     lastOptions : null,
4902
4903     /**
4904      * Add Records to the Store and fires the add event.
4905      * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
4906      */
4907     add : function(records){
4908         records = [].concat(records);
4909         for(var i = 0, len = records.length; i < len; i++){
4910             records[i].join(this);
4911         }
4912         var index = this.data.length;
4913         this.data.addAll(records);
4914         this.fireEvent("add", this, records, index);
4915     },
4916
4917     /**
4918      * Remove a Record from the Store and fires the remove event.
4919      * @param {Ext.data.Record} record The Roo.data.Record object to remove from the cache.
4920      */
4921     remove : function(record){
4922         var index = this.data.indexOf(record);
4923         this.data.removeAt(index);
4924         if(this.pruneModifiedRecords){
4925             this.modified.remove(record);
4926         }
4927         this.fireEvent("remove", this, record, index);
4928     },
4929
4930     /**
4931      * Remove all Records from the Store and fires the clear event.
4932      */
4933     removeAll : function(){
4934         this.data.clear();
4935         if(this.pruneModifiedRecords){
4936             this.modified = [];
4937         }
4938         this.fireEvent("clear", this);
4939     },
4940
4941     /**
4942      * Inserts Records to the Store at the given index and fires the add event.
4943      * @param {Number} index The start index at which to insert the passed Records.
4944      * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
4945      */
4946     insert : function(index, records){
4947         records = [].concat(records);
4948         for(var i = 0, len = records.length; i < len; i++){
4949             this.data.insert(index, records[i]);
4950             records[i].join(this);
4951         }
4952         this.fireEvent("add", this, records, index);
4953     },
4954
4955     /**
4956      * Get the index within the cache of the passed Record.
4957      * @param {Roo.data.Record} record The Roo.data.Record object to to find.
4958      * @return {Number} The index of the passed Record. Returns -1 if not found.
4959      */
4960     indexOf : function(record){
4961         return this.data.indexOf(record);
4962     },
4963
4964     /**
4965      * Get the index within the cache of the Record with the passed id.
4966      * @param {String} id The id of the Record to find.
4967      * @return {Number} The index of the Record. Returns -1 if not found.
4968      */
4969     indexOfId : function(id){
4970         return this.data.indexOfKey(id);
4971     },
4972
4973     /**
4974      * Get the Record with the specified id.
4975      * @param {String} id The id of the Record to find.
4976      * @return {Roo.data.Record} The Record with the passed id. Returns undefined if not found.
4977      */
4978     getById : function(id){
4979         return this.data.key(id);
4980     },
4981
4982     /**
4983      * Get the Record at the specified index.
4984      * @param {Number} index The index of the Record to find.
4985      * @return {Roo.data.Record} The Record at the passed index. Returns undefined if not found.
4986      */
4987     getAt : function(index){
4988         return this.data.itemAt(index);
4989     },
4990
4991     /**
4992      * Returns a range of Records between specified indices.
4993      * @param {Number} startIndex (optional) The starting index (defaults to 0)
4994      * @param {Number} endIndex (optional) The ending index (defaults to the last Record in the Store)
4995      * @return {Roo.data.Record[]} An array of Records
4996      */
4997     getRange : function(start, end){
4998         return this.data.getRange(start, end);
4999     },
5000
5001     // private
5002     storeOptions : function(o){
5003         o = Roo.apply({}, o);
5004         delete o.callback;
5005         delete o.scope;
5006         this.lastOptions = o;
5007     },
5008
5009     /**
5010      * Loads the Record cache from the configured Proxy using the configured Reader.
5011      * <p>
5012      * If using remote paging, then the first load call must specify the <em>start</em>
5013      * and <em>limit</em> properties in the options.params property to establish the initial
5014      * position within the dataset, and the number of Records to cache on each read from the Proxy.
5015      * <p>
5016      * <strong>It is important to note that for remote data sources, loading is asynchronous,
5017      * and this call will return before the new data has been loaded. Perform any post-processing
5018      * in a callback function, or in a "load" event handler.</strong>
5019      * <p>
5020      * @param {Object} options An object containing properties which control loading options:<ul>
5021      * <li>params {Object} An object containing properties to pass as HTTP parameters to a remote data source.</li>
5022      * <li>callback {Function} A function to be called after the Records have been loaded. The callback is
5023      * passed the following arguments:<ul>
5024      * <li>r : Roo.data.Record[]</li>
5025      * <li>options: Options object from the load call</li>
5026      * <li>success: Boolean success indicator</li></ul></li>
5027      * <li>scope {Object} Scope with which to call the callback (defaults to the Store object)</li>
5028      * <li>add {Boolean} indicator to append loaded records rather than replace the current cache.</li>
5029      * </ul>
5030      */
5031     load : function(options){
5032         options = options || {};
5033         if(this.fireEvent("beforeload", this, options) !== false){
5034             this.storeOptions(options);
5035             var p = Roo.apply(options.params || {}, this.baseParams);
5036             // if meta was not loaded from remote source.. try requesting it.
5037             if (!this.reader.metaFromRemote) {
5038                 p._requestMeta = 1;
5039             }
5040             if(this.sortInfo && this.remoteSort){
5041                 var pn = this.paramNames;
5042                 p[pn["sort"]] = this.sortInfo.field;
5043                 p[pn["dir"]] = this.sortInfo.direction;
5044             }
5045             if (this.multiSort) {
5046                 var pn = this.paramNames;
5047                 p[pn["multisort"]] = Roo.encode( { sort : this.sortToggle, order: this.sortOrder });
5048             }
5049             
5050             this.proxy.load(p, this.reader, this.loadRecords, this, options);
5051         }
5052     },
5053
5054     /**
5055      * Reloads the Record cache from the configured Proxy using the configured Reader and
5056      * the options from the last load operation performed.
5057      * @param {Object} options (optional) An object containing properties which may override the options
5058      * used in the last load operation. See {@link #load} for details (defaults to null, in which case
5059      * the most recently used options are reused).
5060      */
5061     reload : function(options){
5062         this.load(Roo.applyIf(options||{}, this.lastOptions));
5063     },
5064
5065     // private
5066     // Called as a callback by the Reader during a load operation.
5067     loadRecords : function(o, options, success){
5068         if(!o || success === false){
5069             if(success !== false){
5070                 this.fireEvent("load", this, [], options, o);
5071             }
5072             if(options.callback){
5073                 options.callback.call(options.scope || this, [], options, false);
5074             }
5075             return;
5076         }
5077         // if data returned failure - throw an exception.
5078         if (o.success === false) {
5079             // show a message if no listener is registered.
5080             if (!this.hasListener('loadexception') && typeof(o.raw.errorMsg) != 'undefined') {
5081                     Roo.MessageBox.alert("Error loading",o.raw.errorMsg);
5082             }
5083             // loadmask wil be hooked into this..
5084             this.fireEvent("loadexception", this, o, options, o.raw.errorMsg);
5085             return;
5086         }
5087         var r = o.records, t = o.totalRecords || r.length;
5088         
5089         this.fireEvent("beforeloadadd", this, r, options, o);
5090         
5091         if(!options || options.add !== true){
5092             if(this.pruneModifiedRecords){
5093                 this.modified = [];
5094             }
5095             for(var i = 0, len = r.length; i < len; i++){
5096                 r[i].join(this);
5097             }
5098             if(this.snapshot){
5099                 this.data = this.snapshot;
5100                 delete this.snapshot;
5101             }
5102             this.data.clear();
5103             this.data.addAll(r);
5104             this.totalLength = t;
5105             this.applySort();
5106             this.fireEvent("datachanged", this);
5107         }else{
5108             this.totalLength = Math.max(t, this.data.length+r.length);
5109             this.add(r);
5110         }
5111         this.fireEvent("load", this, r, options, o);
5112         if(options.callback){
5113             options.callback.call(options.scope || this, r, options, true);
5114         }
5115     },
5116
5117
5118     /**
5119      * Loads data from a passed data block. A Reader which understands the format of the data
5120      * must have been configured in the constructor.
5121      * @param {Object} data The data block from which to read the Records.  The format of the data expected
5122      * is dependent on the type of Reader that is configured and should correspond to that Reader's readRecords parameter.
5123      * @param {Boolean} append (Optional) True to append the new Records rather than replace the existing cache.
5124      */
5125     loadData : function(o, append){
5126         var r = this.reader.readRecords(o);
5127         this.loadRecords(r, {add: append}, true);
5128     },
5129
5130     /**
5131      * Gets the number of cached records.
5132      * <p>
5133      * <em>If using paging, this may not be the total size of the dataset. If the data object
5134      * used by the Reader contains the dataset size, then the getTotalCount() function returns
5135      * the data set size</em>
5136      */
5137     getCount : function(){
5138         return this.data.length || 0;
5139     },
5140
5141     /**
5142      * Gets the total number of records in the dataset as returned by the server.
5143      * <p>
5144      * <em>If using paging, for this to be accurate, the data object used by the Reader must contain
5145      * the dataset size</em>
5146      */
5147     getTotalCount : function(){
5148         return this.totalLength || 0;
5149     },
5150
5151     /**
5152      * Returns the sort state of the Store as an object with two properties:
5153      * <pre><code>
5154  field {String} The name of the field by which the Records are sorted
5155  direction {String} The sort order, "ASC" or "DESC"
5156      * </code></pre>
5157      */
5158     getSortState : function(){
5159         return this.sortInfo;
5160     },
5161
5162     // private
5163     applySort : function(){
5164         if(this.sortInfo && !this.remoteSort){
5165             var s = this.sortInfo, f = s.field;
5166             var st = this.fields.get(f).sortType;
5167             var fn = function(r1, r2){
5168                 var v1 = st(r1.data[f]), v2 = st(r2.data[f]);
5169                 return v1 > v2 ? 1 : (v1 < v2 ? -1 : 0);
5170             };
5171             this.data.sort(s.direction, fn);
5172             if(this.snapshot && this.snapshot != this.data){
5173                 this.snapshot.sort(s.direction, fn);
5174             }
5175         }
5176     },
5177
5178     /**
5179      * Sets the default sort column and order to be used by the next load operation.
5180      * @param {String} fieldName The name of the field to sort by.
5181      * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
5182      */
5183     setDefaultSort : function(field, dir){
5184         this.sortInfo = {field: field, direction: dir ? dir.toUpperCase() : "ASC"};
5185     },
5186
5187     /**
5188      * Sort the Records.
5189      * If remote sorting is used, the sort is performed on the server, and the cache is
5190      * reloaded. If local sorting is used, the cache is sorted internally.
5191      * @param {String} fieldName The name of the field to sort by.
5192      * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
5193      */
5194     sort : function(fieldName, dir){
5195         var f = this.fields.get(fieldName);
5196         if(!dir){
5197             this.sortToggle[f.name] = this.sortToggle[f.name] || f.sortDir;
5198             
5199             if(this.multiSort || (this.sortInfo && this.sortInfo.field == f.name) ){ // toggle sort dir
5200                 dir = (this.sortToggle[f.name] || "ASC").toggle("ASC", "DESC");
5201             }else{
5202                 dir = f.sortDir;
5203             }
5204         }
5205         this.sortToggle[f.name] = dir;
5206         this.sortInfo = {field: f.name, direction: dir};
5207         if(!this.remoteSort){
5208             this.applySort();
5209             this.fireEvent("datachanged", this);
5210         }else{
5211             this.load(this.lastOptions);
5212         }
5213     },
5214
5215     /**
5216      * Calls the specified function for each of the Records in the cache.
5217      * @param {Function} fn The function to call. The Record is passed as the first parameter.
5218      * Returning <em>false</em> aborts and exits the iteration.
5219      * @param {Object} scope (optional) The scope in which to call the function (defaults to the Record).
5220      */
5221     each : function(fn, scope){
5222         this.data.each(fn, scope);
5223     },
5224
5225     /**
5226      * Gets all records modified since the last commit.  Modified records are persisted across load operations
5227      * (e.g., during paging).
5228      * @return {Roo.data.Record[]} An array of Records containing outstanding modifications.
5229      */
5230     getModifiedRecords : function(){
5231         return this.modified;
5232     },
5233
5234     // private
5235     createFilterFn : function(property, value, anyMatch){
5236         if(!value.exec){ // not a regex
5237             value = String(value);
5238             if(value.length == 0){
5239                 return false;
5240             }
5241             value = new RegExp((anyMatch === true ? '' : '^') + Roo.escapeRe(value), "i");
5242         }
5243         return function(r){
5244             return value.test(r.data[property]);
5245         };
5246     },
5247
5248     /**
5249      * Sums the value of <i>property</i> for each record between start and end and returns the result.
5250      * @param {String} property A field on your records
5251      * @param {Number} start The record index to start at (defaults to 0)
5252      * @param {Number} end The last record index to include (defaults to length - 1)
5253      * @return {Number} The sum
5254      */
5255     sum : function(property, start, end){
5256         var rs = this.data.items, v = 0;
5257         start = start || 0;
5258         end = (end || end === 0) ? end : rs.length-1;
5259
5260         for(var i = start; i <= end; i++){
5261             v += (rs[i].data[property] || 0);
5262         }
5263         return v;
5264     },
5265
5266     /**
5267      * Filter 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      */
5273     filter : function(property, value, anyMatch){
5274         var fn = this.createFilterFn(property, value, anyMatch);
5275         return fn ? this.filterBy(fn) : this.clearFilter();
5276     },
5277
5278     /**
5279      * Filter by a function. The specified function will be called with each
5280      * record in this data source. If the function returns true the record is included,
5281      * otherwise it is filtered.
5282      * @param {Function} fn The function to be called, it will receive 2 args (record, id)
5283      * @param {Object} scope (optional) The scope of the function (defaults to this)
5284      */
5285     filterBy : function(fn, scope){
5286         this.snapshot = this.snapshot || this.data;
5287         this.data = this.queryBy(fn, scope||this);
5288         this.fireEvent("datachanged", this);
5289     },
5290
5291     /**
5292      * Query the records by a specified property.
5293      * @param {String} field A field on your records
5294      * @param {String/RegExp} value Either a string that the field
5295      * should start with or a RegExp to test against the field
5296      * @param {Boolean} anyMatch True to match any part not just the beginning
5297      * @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
5298      */
5299     query : function(property, value, anyMatch){
5300         var fn = this.createFilterFn(property, value, anyMatch);
5301         return fn ? this.queryBy(fn) : this.data.clone();
5302     },
5303
5304     /**
5305      * Query by a function. The specified function will be called with each
5306      * record in this data source. If the function returns true the record is included
5307      * in the results.
5308      * @param {Function} fn The function to be called, it will receive 2 args (record, id)
5309      * @param {Object} scope (optional) The scope of the function (defaults to this)
5310       @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
5311      **/
5312     queryBy : function(fn, scope){
5313         var data = this.snapshot || this.data;
5314         return data.filterBy(fn, scope||this);
5315     },
5316
5317     /**
5318      * Collects unique values for a particular dataIndex from this store.
5319      * @param {String} dataIndex The property to collect
5320      * @param {Boolean} allowNull (optional) Pass true to allow null, undefined or empty string values
5321      * @param {Boolean} bypassFilter (optional) Pass true to collect from all records, even ones which are filtered
5322      * @return {Array} An array of the unique values
5323      **/
5324     collect : function(dataIndex, allowNull, bypassFilter){
5325         var d = (bypassFilter === true && this.snapshot) ?
5326                 this.snapshot.items : this.data.items;
5327         var v, sv, r = [], l = {};
5328         for(var i = 0, len = d.length; i < len; i++){
5329             v = d[i].data[dataIndex];
5330             sv = String(v);
5331             if((allowNull || !Roo.isEmpty(v)) && !l[sv]){
5332                 l[sv] = true;
5333                 r[r.length] = v;
5334             }
5335         }
5336         return r;
5337     },
5338
5339     /**
5340      * Revert to a view of the Record cache with no filtering applied.
5341      * @param {Boolean} suppressEvent If true the filter is cleared silently without notifying listeners
5342      */
5343     clearFilter : function(suppressEvent){
5344         if(this.snapshot && this.snapshot != this.data){
5345             this.data = this.snapshot;
5346             delete this.snapshot;
5347             if(suppressEvent !== true){
5348                 this.fireEvent("datachanged", this);
5349             }
5350         }
5351     },
5352
5353     // private
5354     afterEdit : function(record){
5355         if(this.modified.indexOf(record) == -1){
5356             this.modified.push(record);
5357         }
5358         this.fireEvent("update", this, record, Roo.data.Record.EDIT);
5359     },
5360     
5361     // private
5362     afterReject : function(record){
5363         this.modified.remove(record);
5364         this.fireEvent("update", this, record, Roo.data.Record.REJECT);
5365     },
5366
5367     // private
5368     afterCommit : function(record){
5369         this.modified.remove(record);
5370         this.fireEvent("update", this, record, Roo.data.Record.COMMIT);
5371     },
5372
5373     /**
5374      * Commit all Records with outstanding changes. To handle updates for changes, subscribe to the
5375      * Store's "update" event, and perform updating when the third parameter is Roo.data.Record.COMMIT.
5376      */
5377     commitChanges : function(){
5378         var m = this.modified.slice(0);
5379         this.modified = [];
5380         for(var i = 0, len = m.length; i < len; i++){
5381             m[i].commit();
5382         }
5383     },
5384
5385     /**
5386      * Cancel outstanding changes on all changed records.
5387      */
5388     rejectChanges : function(){
5389         var m = this.modified.slice(0);
5390         this.modified = [];
5391         for(var i = 0, len = m.length; i < len; i++){
5392             m[i].reject();
5393         }
5394     },
5395
5396     onMetaChange : function(meta, rtype, o){
5397         this.recordType = rtype;
5398         this.fields = rtype.prototype.fields;
5399         delete this.snapshot;
5400         this.sortInfo = meta.sortInfo || this.sortInfo;
5401         this.modified = [];
5402         this.fireEvent('metachange', this, this.reader.meta);
5403     }
5404 });/*
5405  * Based on:
5406  * Ext JS Library 1.1.1
5407  * Copyright(c) 2006-2007, Ext JS, LLC.
5408  *
5409  * Originally Released Under LGPL - original licence link has changed is not relivant.
5410  *
5411  * Fork - LGPL
5412  * <script type="text/javascript">
5413  */
5414
5415 /**
5416  * @class Roo.data.SimpleStore
5417  * @extends Roo.data.Store
5418  * Small helper class to make creating Stores from Array data easier.
5419  * @cfg {Number} id The array index of the record id. Leave blank to auto generate ids.
5420  * @cfg {Array} fields An array of field definition objects, or field name strings.
5421  * @cfg {Array} data The multi-dimensional array of data
5422  * @constructor
5423  * @param {Object} config
5424  */
5425 Roo.data.SimpleStore = function(config){
5426     Roo.data.SimpleStore.superclass.constructor.call(this, {
5427         isLocal : true,
5428         reader: new Roo.data.ArrayReader({
5429                 id: config.id
5430             },
5431             Roo.data.Record.create(config.fields)
5432         ),
5433         proxy : new Roo.data.MemoryProxy(config.data)
5434     });
5435     this.load();
5436 };
5437 Roo.extend(Roo.data.SimpleStore, Roo.data.Store);/*
5438  * Based on:
5439  * Ext JS Library 1.1.1
5440  * Copyright(c) 2006-2007, Ext JS, LLC.
5441  *
5442  * Originally Released Under LGPL - original licence link has changed is not relivant.
5443  *
5444  * Fork - LGPL
5445  * <script type="text/javascript">
5446  */
5447
5448 /**
5449 /**
5450  * @extends Roo.data.Store
5451  * @class Roo.data.JsonStore
5452  * Small helper class to make creating Stores for JSON data easier. <br/>
5453 <pre><code>
5454 var store = new Roo.data.JsonStore({
5455     url: 'get-images.php',
5456     root: 'images',
5457     fields: ['name', 'url', {name:'size', type: 'float'}, {name:'lastmod', type:'date'}]
5458 });
5459 </code></pre>
5460  * <b>Note: Although they are not listed, this class inherits all of the config options of Store,
5461  * JsonReader and HttpProxy (unless inline data is provided).</b>
5462  * @cfg {Array} fields An array of field definition objects, or field name strings.
5463  * @constructor
5464  * @param {Object} config
5465  */
5466 Roo.data.JsonStore = function(c){
5467     Roo.data.JsonStore.superclass.constructor.call(this, Roo.apply(c, {
5468         proxy: !c.data ? new Roo.data.HttpProxy({url: c.url}) : undefined,
5469         reader: new Roo.data.JsonReader(c, c.fields)
5470     }));
5471 };
5472 Roo.extend(Roo.data.JsonStore, Roo.data.Store);/*
5473  * Based on:
5474  * Ext JS Library 1.1.1
5475  * Copyright(c) 2006-2007, Ext JS, LLC.
5476  *
5477  * Originally Released Under LGPL - original licence link has changed is not relivant.
5478  *
5479  * Fork - LGPL
5480  * <script type="text/javascript">
5481  */
5482
5483  
5484 Roo.data.Field = function(config){
5485     if(typeof config == "string"){
5486         config = {name: config};
5487     }
5488     Roo.apply(this, config);
5489     
5490     if(!this.type){
5491         this.type = "auto";
5492     }
5493     
5494     var st = Roo.data.SortTypes;
5495     // named sortTypes are supported, here we look them up
5496     if(typeof this.sortType == "string"){
5497         this.sortType = st[this.sortType];
5498     }
5499     
5500     // set default sortType for strings and dates
5501     if(!this.sortType){
5502         switch(this.type){
5503             case "string":
5504                 this.sortType = st.asUCString;
5505                 break;
5506             case "date":
5507                 this.sortType = st.asDate;
5508                 break;
5509             default:
5510                 this.sortType = st.none;
5511         }
5512     }
5513
5514     // define once
5515     var stripRe = /[\$,%]/g;
5516
5517     // prebuilt conversion function for this field, instead of
5518     // switching every time we're reading a value
5519     if(!this.convert){
5520         var cv, dateFormat = this.dateFormat;
5521         switch(this.type){
5522             case "":
5523             case "auto":
5524             case undefined:
5525                 cv = function(v){ return v; };
5526                 break;
5527             case "string":
5528                 cv = function(v){ return (v === undefined || v === null) ? '' : String(v); };
5529                 break;
5530             case "int":
5531                 cv = function(v){
5532                     return v !== undefined && v !== null && v !== '' ?
5533                            parseInt(String(v).replace(stripRe, ""), 10) : '';
5534                     };
5535                 break;
5536             case "float":
5537                 cv = function(v){
5538                     return v !== undefined && v !== null && v !== '' ?
5539                            parseFloat(String(v).replace(stripRe, ""), 10) : ''; 
5540                     };
5541                 break;
5542             case "bool":
5543             case "boolean":
5544                 cv = function(v){ return v === true || v === "true" || v == 1; };
5545                 break;
5546             case "date":
5547                 cv = function(v){
5548                     if(!v){
5549                         return '';
5550                     }
5551                     if(v instanceof Date){
5552                         return v;
5553                     }
5554                     if(dateFormat){
5555                         if(dateFormat == "timestamp"){
5556                             return new Date(v*1000);
5557                         }
5558                         return Date.parseDate(v, dateFormat);
5559                     }
5560                     var parsed = Date.parse(v);
5561                     return parsed ? new Date(parsed) : null;
5562                 };
5563              break;
5564             
5565         }
5566         this.convert = cv;
5567     }
5568 };
5569
5570 Roo.data.Field.prototype = {
5571     dateFormat: null,
5572     defaultValue: "",
5573     mapping: null,
5574     sortType : null,
5575     sortDir : "ASC"
5576 };/*
5577  * Based on:
5578  * Ext JS Library 1.1.1
5579  * Copyright(c) 2006-2007, Ext JS, LLC.
5580  *
5581  * Originally Released Under LGPL - original licence link has changed is not relivant.
5582  *
5583  * Fork - LGPL
5584  * <script type="text/javascript">
5585  */
5586  
5587 // Base class for reading structured data from a data source.  This class is intended to be
5588 // extended (see ArrayReader, JsonReader and XmlReader) and should not be created directly.
5589
5590 /**
5591  * @class Roo.data.DataReader
5592  * Base class for reading structured data from a data source.  This class is intended to be
5593  * extended (see {Roo.data.ArrayReader}, {Roo.data.JsonReader} and {Roo.data.XmlReader}) and should not be created directly.
5594  */
5595
5596 Roo.data.DataReader = function(meta, recordType){
5597     
5598     this.meta = meta;
5599     
5600     this.recordType = recordType instanceof Array ? 
5601         Roo.data.Record.create(recordType) : recordType;
5602 };
5603
5604 Roo.data.DataReader.prototype = {
5605      /**
5606      * Create an empty record
5607      * @param {Object} data (optional) - overlay some values
5608      * @return {Roo.data.Record} record created.
5609      */
5610     newRow :  function(d) {
5611         var da =  {};
5612         this.recordType.prototype.fields.each(function(c) {
5613             switch( c.type) {
5614                 case 'int' : da[c.name] = 0; break;
5615                 case 'date' : da[c.name] = new Date(); break;
5616                 case 'float' : da[c.name] = 0.0; break;
5617                 case 'boolean' : da[c.name] = false; break;
5618                 default : da[c.name] = ""; break;
5619             }
5620             
5621         });
5622         return new this.recordType(Roo.apply(da, d));
5623     }
5624     
5625 };/*
5626  * Based on:
5627  * Ext JS Library 1.1.1
5628  * Copyright(c) 2006-2007, Ext JS, LLC.
5629  *
5630  * Originally Released Under LGPL - original licence link has changed is not relivant.
5631  *
5632  * Fork - LGPL
5633  * <script type="text/javascript">
5634  */
5635
5636 /**
5637  * @class Roo.data.DataProxy
5638  * @extends Roo.data.Observable
5639  * This class is an abstract base class for implementations which provide retrieval of
5640  * unformatted data objects.<br>
5641  * <p>
5642  * DataProxy implementations are usually used in conjunction with an implementation of Roo.data.DataReader
5643  * (of the appropriate type which knows how to parse the data object) to provide a block of
5644  * {@link Roo.data.Records} to an {@link Roo.data.Store}.<br>
5645  * <p>
5646  * Custom implementations must implement the load method as described in
5647  * {@link Roo.data.HttpProxy#load}.
5648  */
5649 Roo.data.DataProxy = function(){
5650     this.addEvents({
5651         /**
5652          * @event beforeload
5653          * Fires before a network request is made to retrieve a data object.
5654          * @param {Object} This DataProxy object.
5655          * @param {Object} params The params parameter to the load function.
5656          */
5657         beforeload : true,
5658         /**
5659          * @event load
5660          * Fires before the load method's callback is called.
5661          * @param {Object} This DataProxy object.
5662          * @param {Object} o The data object.
5663          * @param {Object} arg The callback argument object passed to the load function.
5664          */
5665         load : true,
5666         /**
5667          * @event loadexception
5668          * Fires if an Exception occurs during data retrieval.
5669          * @param {Object} This DataProxy object.
5670          * @param {Object} o The data object.
5671          * @param {Object} arg The callback argument object passed to the load function.
5672          * @param {Object} e The Exception.
5673          */
5674         loadexception : true
5675     });
5676     Roo.data.DataProxy.superclass.constructor.call(this);
5677 };
5678
5679 Roo.extend(Roo.data.DataProxy, Roo.util.Observable);
5680
5681     /**
5682      * @cfg {void} listeners (Not available) Constructor blocks listeners from being set
5683      */
5684 /*
5685  * Based on:
5686  * Ext JS Library 1.1.1
5687  * Copyright(c) 2006-2007, Ext JS, LLC.
5688  *
5689  * Originally Released Under LGPL - original licence link has changed is not relivant.
5690  *
5691  * Fork - LGPL
5692  * <script type="text/javascript">
5693  */
5694 /**
5695  * @class Roo.data.MemoryProxy
5696  * An implementation of Roo.data.DataProxy that simply passes the data specified in its constructor
5697  * to the Reader when its load method is called.
5698  * @constructor
5699  * @param {Object} data The data object which the Reader uses to construct a block of Roo.data.Records.
5700  */
5701 Roo.data.MemoryProxy = function(data){
5702     if (data.data) {
5703         data = data.data;
5704     }
5705     Roo.data.MemoryProxy.superclass.constructor.call(this);
5706     this.data = data;
5707 };
5708
5709 Roo.extend(Roo.data.MemoryProxy, Roo.data.DataProxy, {
5710     /**
5711      * Load data from the requested source (in this case an in-memory
5712      * data object passed to the constructor), read the data object into
5713      * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
5714      * process that block using the passed callback.
5715      * @param {Object} params This parameter is not used by the MemoryProxy class.
5716      * @param {Roo.data.DataReader} reader The Reader object which converts the data
5717      * object into a block of Roo.data.Records.
5718      * @param {Function} callback The function into which to pass the block of Roo.data.records.
5719      * The function must be passed <ul>
5720      * <li>The Record block object</li>
5721      * <li>The "arg" argument from the load function</li>
5722      * <li>A boolean success indicator</li>
5723      * </ul>
5724      * @param {Object} scope The scope in which to call the callback
5725      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
5726      */
5727     load : function(params, reader, callback, scope, arg){
5728         params = params || {};
5729         var result;
5730         try {
5731             result = reader.readRecords(this.data);
5732         }catch(e){
5733             this.fireEvent("loadexception", this, arg, null, e);
5734             callback.call(scope, null, arg, false);
5735             return;
5736         }
5737         callback.call(scope, result, arg, true);
5738     },
5739     
5740     // private
5741     update : function(params, records){
5742         
5743     }
5744 });/*
5745  * Based on:
5746  * Ext JS Library 1.1.1
5747  * Copyright(c) 2006-2007, Ext JS, LLC.
5748  *
5749  * Originally Released Under LGPL - original licence link has changed is not relivant.
5750  *
5751  * Fork - LGPL
5752  * <script type="text/javascript">
5753  */
5754 /**
5755  * @class Roo.data.HttpProxy
5756  * @extends Roo.data.DataProxy
5757  * An implementation of {@link Roo.data.DataProxy} that reads a data object from an {@link Roo.data.Connection} object
5758  * configured to reference a certain URL.<br><br>
5759  * <p>
5760  * <em>Note that this class cannot be used to retrieve data from a domain other than the domain
5761  * from which the running page was served.<br><br>
5762  * <p>
5763  * For cross-domain access to remote data, use an {@link Roo.data.ScriptTagProxy}.</em><br><br>
5764  * <p>
5765  * Be aware that to enable the browser to parse an XML document, the server must set
5766  * the Content-Type header in the HTTP response to "text/xml".
5767  * @constructor
5768  * @param {Object} conn Connection config options to add to each request (e.g. {url: 'foo.php'} or
5769  * an {@link Roo.data.Connection} object.  If a Connection config is passed, the singleton {@link Roo.Ajax} object
5770  * will be used to make the request.
5771  */
5772 Roo.data.HttpProxy = function(conn){
5773     Roo.data.HttpProxy.superclass.constructor.call(this);
5774     // is conn a conn config or a real conn?
5775     this.conn = conn;
5776     this.useAjax = !conn || !conn.events;
5777   
5778 };
5779
5780 Roo.extend(Roo.data.HttpProxy, Roo.data.DataProxy, {
5781     // thse are take from connection...
5782     
5783     /**
5784      * @cfg {String} url (Optional) The default URL to be used for requests to the server. (defaults to undefined)
5785      */
5786     /**
5787      * @cfg {Object} extraParams (Optional) An object containing properties which are used as
5788      * extra parameters to each request made by this object. (defaults to undefined)
5789      */
5790     /**
5791      * @cfg {Object} defaultHeaders (Optional) An object containing request headers which are added
5792      *  to each request made by this object. (defaults to undefined)
5793      */
5794     /**
5795      * @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)
5796      */
5797     /**
5798      * @cfg {Number} timeout (Optional) The timeout in milliseconds to be used for requests. (defaults to 30000)
5799      */
5800      /**
5801      * @cfg {Boolean} autoAbort (Optional) Whether this request should abort any pending requests. (defaults to false)
5802      * @type Boolean
5803      */
5804   
5805
5806     /**
5807      * @cfg {Boolean} disableCaching (Optional) True to add a unique cache-buster param to GET requests. (defaults to true)
5808      * @type Boolean
5809      */
5810     /**
5811      * Return the {@link Roo.data.Connection} object being used by this Proxy.
5812      * @return {Connection} The Connection object. This object may be used to subscribe to events on
5813      * a finer-grained basis than the DataProxy events.
5814      */
5815     getConnection : function(){
5816         return this.useAjax ? Roo.Ajax : this.conn;
5817     },
5818
5819     /**
5820      * Load data from the configured {@link Roo.data.Connection}, read the data object into
5821      * a block of Roo.data.Records using the passed {@link Roo.data.DataReader} implementation, and
5822      * process that block using the passed callback.
5823      * @param {Object} params An object containing properties which are to be used as HTTP parameters
5824      * for the request to the remote server.
5825      * @param {Roo.data.DataReader} reader The Reader object which converts the data
5826      * object into a block of Roo.data.Records.
5827      * @param {Function} callback The function into which to pass the block of Roo.data.Records.
5828      * The function must be passed <ul>
5829      * <li>The Record block object</li>
5830      * <li>The "arg" argument from the load function</li>
5831      * <li>A boolean success indicator</li>
5832      * </ul>
5833      * @param {Object} scope The scope in which to call the callback
5834      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
5835      */
5836     load : function(params, reader, callback, scope, arg){
5837         if(this.fireEvent("beforeload", this, params) !== false){
5838             var  o = {
5839                 params : params || {},
5840                 request: {
5841                     callback : callback,
5842                     scope : scope,
5843                     arg : arg
5844                 },
5845                 reader: reader,
5846                 callback : this.loadResponse,
5847                 scope: this
5848             };
5849             if(this.useAjax){
5850                 Roo.applyIf(o, this.conn);
5851                 if(this.activeRequest){
5852                     Roo.Ajax.abort(this.activeRequest);
5853                 }
5854                 this.activeRequest = Roo.Ajax.request(o);
5855             }else{
5856                 this.conn.request(o);
5857             }
5858         }else{
5859             callback.call(scope||this, null, arg, false);
5860         }
5861     },
5862
5863     // private
5864     loadResponse : function(o, success, response){
5865         delete this.activeRequest;
5866         if(!success){
5867             this.fireEvent("loadexception", this, o, response);
5868             o.request.callback.call(o.request.scope, null, o.request.arg, false);
5869             return;
5870         }
5871         var result;
5872         try {
5873             result = o.reader.read(response);
5874         }catch(e){
5875             this.fireEvent("loadexception", this, o, response, e);
5876             o.request.callback.call(o.request.scope, null, o.request.arg, false);
5877             return;
5878         }
5879         
5880         this.fireEvent("load", this, o, o.request.arg);
5881         o.request.callback.call(o.request.scope, result, o.request.arg, true);
5882     },
5883
5884     // private
5885     update : function(dataSet){
5886
5887     },
5888
5889     // private
5890     updateResponse : function(dataSet){
5891
5892     }
5893 });/*
5894  * Based on:
5895  * Ext JS Library 1.1.1
5896  * Copyright(c) 2006-2007, Ext JS, LLC.
5897  *
5898  * Originally Released Under LGPL - original licence link has changed is not relivant.
5899  *
5900  * Fork - LGPL
5901  * <script type="text/javascript">
5902  */
5903
5904 /**
5905  * @class Roo.data.ScriptTagProxy
5906  * An implementation of Roo.data.DataProxy that reads a data object from a URL which may be in a domain
5907  * other than the originating domain of the running page.<br><br>
5908  * <p>
5909  * <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
5910  * of the running page, you must use this class, rather than DataProxy.</em><br><br>
5911  * <p>
5912  * The content passed back from a server resource requested by a ScriptTagProxy is executable JavaScript
5913  * source code that is used as the source inside a &lt;script> tag.<br><br>
5914  * <p>
5915  * In order for the browser to process the returned data, the server must wrap the data object
5916  * with a call to a callback function, the name of which is passed as a parameter by the ScriptTagProxy.
5917  * Below is a Java example for a servlet which returns data for either a ScriptTagProxy, or an HttpProxy
5918  * depending on whether the callback name was passed:
5919  * <p>
5920  * <pre><code>
5921 boolean scriptTag = false;
5922 String cb = request.getParameter("callback");
5923 if (cb != null) {
5924     scriptTag = true;
5925     response.setContentType("text/javascript");
5926 } else {
5927     response.setContentType("application/x-json");
5928 }
5929 Writer out = response.getWriter();
5930 if (scriptTag) {
5931     out.write(cb + "(");
5932 }
5933 out.print(dataBlock.toJsonString());
5934 if (scriptTag) {
5935     out.write(");");
5936 }
5937 </pre></code>
5938  *
5939  * @constructor
5940  * @param {Object} config A configuration object.
5941  */
5942 Roo.data.ScriptTagProxy = function(config){
5943     Roo.data.ScriptTagProxy.superclass.constructor.call(this);
5944     Roo.apply(this, config);
5945     this.head = document.getElementsByTagName("head")[0];
5946 };
5947
5948 Roo.data.ScriptTagProxy.TRANS_ID = 1000;
5949
5950 Roo.extend(Roo.data.ScriptTagProxy, Roo.data.DataProxy, {
5951     /**
5952      * @cfg {String} url The URL from which to request the data object.
5953      */
5954     /**
5955      * @cfg {Number} timeout (Optional) The number of milliseconds to wait for a response. Defaults to 30 seconds.
5956      */
5957     timeout : 30000,
5958     /**
5959      * @cfg {String} callbackParam (Optional) The name of the parameter to pass to the server which tells
5960      * the server the name of the callback function set up by the load call to process the returned data object.
5961      * Defaults to "callback".<p>The server-side processing must read this parameter value, and generate
5962      * javascript output which calls this named function passing the data object as its only parameter.
5963      */
5964     callbackParam : "callback",
5965     /**
5966      *  @cfg {Boolean} nocache (Optional) Defaults to true. Disable cacheing by adding a unique parameter
5967      * name to the request.
5968      */
5969     nocache : true,
5970
5971     /**
5972      * Load data from the configured URL, read the data object into
5973      * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
5974      * process that block using the passed callback.
5975      * @param {Object} params An object containing properties which are to be used as HTTP parameters
5976      * for the request to the remote server.
5977      * @param {Roo.data.DataReader} reader The Reader object which converts the data
5978      * object into a block of Roo.data.Records.
5979      * @param {Function} callback The function into which to pass the block of Roo.data.Records.
5980      * The function must be passed <ul>
5981      * <li>The Record block object</li>
5982      * <li>The "arg" argument from the load function</li>
5983      * <li>A boolean success indicator</li>
5984      * </ul>
5985      * @param {Object} scope The scope in which to call the callback
5986      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
5987      */
5988     load : function(params, reader, callback, scope, arg){
5989         if(this.fireEvent("beforeload", this, params) !== false){
5990
5991             var p = Roo.urlEncode(Roo.apply(params, this.extraParams));
5992
5993             var url = this.url;
5994             url += (url.indexOf("?") != -1 ? "&" : "?") + p;
5995             if(this.nocache){
5996                 url += "&_dc=" + (new Date().getTime());
5997             }
5998             var transId = ++Roo.data.ScriptTagProxy.TRANS_ID;
5999             var trans = {
6000                 id : transId,
6001                 cb : "stcCallback"+transId,
6002                 scriptId : "stcScript"+transId,
6003                 params : params,
6004                 arg : arg,
6005                 url : url,
6006                 callback : callback,
6007                 scope : scope,
6008                 reader : reader
6009             };
6010             var conn = this;
6011
6012             window[trans.cb] = function(o){
6013                 conn.handleResponse(o, trans);
6014             };
6015
6016             url += String.format("&{0}={1}", this.callbackParam, trans.cb);
6017
6018             if(this.autoAbort !== false){
6019                 this.abort();
6020             }
6021
6022             trans.timeoutId = this.handleFailure.defer(this.timeout, this, [trans]);
6023
6024             var script = document.createElement("script");
6025             script.setAttribute("src", url);
6026             script.setAttribute("type", "text/javascript");
6027             script.setAttribute("id", trans.scriptId);
6028             this.head.appendChild(script);
6029
6030             this.trans = trans;
6031         }else{
6032             callback.call(scope||this, null, arg, false);
6033         }
6034     },
6035
6036     // private
6037     isLoading : function(){
6038         return this.trans ? true : false;
6039     },
6040
6041     /**
6042      * Abort the current server request.
6043      */
6044     abort : function(){
6045         if(this.isLoading()){
6046             this.destroyTrans(this.trans);
6047         }
6048     },
6049
6050     // private
6051     destroyTrans : function(trans, isLoaded){
6052         this.head.removeChild(document.getElementById(trans.scriptId));
6053         clearTimeout(trans.timeoutId);
6054         if(isLoaded){
6055             window[trans.cb] = undefined;
6056             try{
6057                 delete window[trans.cb];
6058             }catch(e){}
6059         }else{
6060             // if hasn't been loaded, wait for load to remove it to prevent script error
6061             window[trans.cb] = function(){
6062                 window[trans.cb] = undefined;
6063                 try{
6064                     delete window[trans.cb];
6065                 }catch(e){}
6066             };
6067         }
6068     },
6069
6070     // private
6071     handleResponse : function(o, trans){
6072         this.trans = false;
6073         this.destroyTrans(trans, true);
6074         var result;
6075         try {
6076             result = trans.reader.readRecords(o);
6077         }catch(e){
6078             this.fireEvent("loadexception", this, o, trans.arg, e);
6079             trans.callback.call(trans.scope||window, null, trans.arg, false);
6080             return;
6081         }
6082         this.fireEvent("load", this, o, trans.arg);
6083         trans.callback.call(trans.scope||window, result, trans.arg, true);
6084     },
6085
6086     // private
6087     handleFailure : function(trans){
6088         this.trans = false;
6089         this.destroyTrans(trans, false);
6090         this.fireEvent("loadexception", this, null, trans.arg);
6091         trans.callback.call(trans.scope||window, null, trans.arg, false);
6092     }
6093 });/*
6094  * Based on:
6095  * Ext JS Library 1.1.1
6096  * Copyright(c) 2006-2007, Ext JS, LLC.
6097  *
6098  * Originally Released Under LGPL - original licence link has changed is not relivant.
6099  *
6100  * Fork - LGPL
6101  * <script type="text/javascript">
6102  */
6103
6104 /**
6105  * @class Roo.data.JsonReader
6106  * @extends Roo.data.DataReader
6107  * Data reader class to create an Array of Roo.data.Record objects from a JSON response
6108  * based on mappings in a provided Roo.data.Record constructor.
6109  * 
6110  * The default behaviour of a store is to send ?_requestMeta=1, unless the class has recieved 'metaData' property
6111  * in the reply previously. 
6112  * 
6113  * <p>
6114  * Example code:
6115  * <pre><code>
6116 var RecordDef = Roo.data.Record.create([
6117     {name: 'name', mapping: 'name'},     // "mapping" property not needed if it's the same as "name"
6118     {name: 'occupation'}                 // This field will use "occupation" as the mapping.
6119 ]);
6120 var myReader = new Roo.data.JsonReader({
6121     totalProperty: "results",    // The property which contains the total dataset size (optional)
6122     root: "rows",                // The property which contains an Array of row objects
6123     id: "id"                     // The property within each row object that provides an ID for the record (optional)
6124 }, RecordDef);
6125 </code></pre>
6126  * <p>
6127  * This would consume a JSON file like this:
6128  * <pre><code>
6129 { 'results': 2, 'rows': [
6130     { 'id': 1, 'name': 'Bill', occupation: 'Gardener' },
6131     { 'id': 2, 'name': 'Ben', occupation: 'Horticulturalist' } ]
6132 }
6133 </code></pre>
6134  * @cfg {String} totalProperty Name of the property from which to retrieve the total number of records
6135  * in the dataset. This is only needed if the whole dataset is not passed in one go, but is being
6136  * paged from the remote server.
6137  * @cfg {String} successProperty Name of the property from which to retrieve the success attribute used by forms.
6138  * @cfg {String} root name of the property which contains the Array of row objects.
6139  * @cfg {String} id Name of the property within a row object that contains a record identifier value.
6140  * @constructor
6141  * Create a new JsonReader
6142  * @param {Object} meta Metadata configuration options
6143  * @param {Object} recordType Either an Array of field definition objects,
6144  * or an {@link Roo.data.Record} object created using {@link Roo.data.Record#create}.
6145  */
6146 Roo.data.JsonReader = function(meta, recordType){
6147     
6148     meta = meta || {};
6149     // set some defaults:
6150     Roo.applyIf(meta, {
6151         totalProperty: 'total',
6152         successProperty : 'success',
6153         root : 'data',
6154         id : 'id'
6155     });
6156     
6157     Roo.data.JsonReader.superclass.constructor.call(this, meta, recordType||meta.fields);
6158 };
6159 Roo.extend(Roo.data.JsonReader, Roo.data.DataReader, {
6160     
6161     /**
6162      * @prop {Boolean} metaFromRemote  - if the meta data was loaded from the remote source.
6163      * Used by Store query builder to append _requestMeta to params.
6164      * 
6165      */
6166     metaFromRemote : false,
6167     /**
6168      * This method is only used by a DataProxy which has retrieved data from a remote server.
6169      * @param {Object} response The XHR object which contains the JSON data in its responseText.
6170      * @return {Object} data A data block which is used by an Roo.data.Store object as
6171      * a cache of Roo.data.Records.
6172      */
6173     read : function(response){
6174         var json = response.responseText;
6175        
6176         var o = /* eval:var:o */ eval("("+json+")");
6177         if(!o) {
6178             throw {message: "JsonReader.read: Json object not found"};
6179         }
6180         
6181         if(o.metaData){
6182             
6183             delete this.ef;
6184             this.metaFromRemote = true;
6185             this.meta = o.metaData;
6186             this.recordType = Roo.data.Record.create(o.metaData.fields);
6187             this.onMetaChange(this.meta, this.recordType, o);
6188         }
6189         return this.readRecords(o);
6190     },
6191
6192     // private function a store will implement
6193     onMetaChange : function(meta, recordType, o){
6194
6195     },
6196
6197     /**
6198          * @ignore
6199          */
6200     simpleAccess: function(obj, subsc) {
6201         return obj[subsc];
6202     },
6203
6204         /**
6205          * @ignore
6206          */
6207     getJsonAccessor: function(){
6208         var re = /[\[\.]/;
6209         return function(expr) {
6210             try {
6211                 return(re.test(expr))
6212                     ? new Function("obj", "return obj." + expr)
6213                     : function(obj){
6214                         return obj[expr];
6215                     };
6216             } catch(e){}
6217             return Roo.emptyFn;
6218         };
6219     }(),
6220
6221     /**
6222      * Create a data block containing Roo.data.Records from an XML document.
6223      * @param {Object} o An object which contains an Array of row objects in the property specified
6224      * in the config as 'root, and optionally a property, specified in the config as 'totalProperty'
6225      * which contains the total size of the dataset.
6226      * @return {Object} data A data block which is used by an Roo.data.Store object as
6227      * a cache of Roo.data.Records.
6228      */
6229     readRecords : function(o){
6230         /**
6231          * After any data loads, the raw JSON data is available for further custom processing.
6232          * @type Object
6233          */
6234         this.o = o;
6235         var s = this.meta, Record = this.recordType,
6236             f = Record.prototype.fields, fi = f.items, fl = f.length;
6237
6238 //      Generate extraction functions for the totalProperty, the root, the id, and for each field
6239         if (!this.ef) {
6240             if(s.totalProperty) {
6241                     this.getTotal = this.getJsonAccessor(s.totalProperty);
6242                 }
6243                 if(s.successProperty) {
6244                     this.getSuccess = this.getJsonAccessor(s.successProperty);
6245                 }
6246                 this.getRoot = s.root ? this.getJsonAccessor(s.root) : function(p){return p;};
6247                 if (s.id) {
6248                         var g = this.getJsonAccessor(s.id);
6249                         this.getId = function(rec) {
6250                                 var r = g(rec);
6251                                 return (r === undefined || r === "") ? null : r;
6252                         };
6253                 } else {
6254                         this.getId = function(){return null;};
6255                 }
6256             this.ef = [];
6257             for(var jj = 0; jj < fl; jj++){
6258                 f = fi[jj];
6259                 var map = (f.mapping !== undefined && f.mapping !== null) ? f.mapping : f.name;
6260                 this.ef[jj] = this.getJsonAccessor(map);
6261             }
6262         }
6263
6264         var root = this.getRoot(o), c = root.length, totalRecords = c, success = true;
6265         if(s.totalProperty){
6266             var vt = parseInt(this.getTotal(o), 10);
6267             if(!isNaN(vt)){
6268                 totalRecords = vt;
6269             }
6270         }
6271         if(s.successProperty){
6272             var vs = this.getSuccess(o);
6273             if(vs === false || vs === 'false'){
6274                 success = false;
6275             }
6276         }
6277         var records = [];
6278             for(var i = 0; i < c; i++){
6279                     var n = root[i];
6280                 var values = {};
6281                 var id = this.getId(n);
6282                 for(var j = 0; j < fl; j++){
6283                     f = fi[j];
6284                 var v = this.ef[j](n);
6285                 if (!f.convert) {
6286                     Roo.log('missing convert for ' + f.name);
6287                     Roo.log(f);
6288                     continue;
6289                 }
6290                 values[f.name] = f.convert((v !== undefined) ? v : f.defaultValue);
6291                 }
6292                 var record = new Record(values, id);
6293                 record.json = n;
6294                 records[i] = record;
6295             }
6296             return {
6297             raw : o,
6298                 success : success,
6299                 records : records,
6300                 totalRecords : totalRecords
6301             };
6302     }
6303 });/*
6304  * Based on:
6305  * Ext JS Library 1.1.1
6306  * Copyright(c) 2006-2007, Ext JS, LLC.
6307  *
6308  * Originally Released Under LGPL - original licence link has changed is not relivant.
6309  *
6310  * Fork - LGPL
6311  * <script type="text/javascript">
6312  */
6313
6314 /**
6315  * @class Roo.data.XmlReader
6316  * @extends Roo.data.DataReader
6317  * Data reader class to create an Array of {@link Roo.data.Record} objects from an XML document
6318  * based on mappings in a provided Roo.data.Record constructor.<br><br>
6319  * <p>
6320  * <em>Note that in order for the browser to parse a returned XML document, the Content-Type
6321  * header in the HTTP response must be set to "text/xml".</em>
6322  * <p>
6323  * Example code:
6324  * <pre><code>
6325 var RecordDef = Roo.data.Record.create([
6326    {name: 'name', mapping: 'name'},     // "mapping" property not needed if it's the same as "name"
6327    {name: 'occupation'}                 // This field will use "occupation" as the mapping.
6328 ]);
6329 var myReader = new Roo.data.XmlReader({
6330    totalRecords: "results", // The element which contains the total dataset size (optional)
6331    record: "row",           // The repeated element which contains row information
6332    id: "id"                 // The element within the row that provides an ID for the record (optional)
6333 }, RecordDef);
6334 </code></pre>
6335  * <p>
6336  * This would consume an XML file like this:
6337  * <pre><code>
6338 &lt;?xml?>
6339 &lt;dataset>
6340  &lt;results>2&lt;/results>
6341  &lt;row>
6342    &lt;id>1&lt;/id>
6343    &lt;name>Bill&lt;/name>
6344    &lt;occupation>Gardener&lt;/occupation>
6345  &lt;/row>
6346  &lt;row>
6347    &lt;id>2&lt;/id>
6348    &lt;name>Ben&lt;/name>
6349    &lt;occupation>Horticulturalist&lt;/occupation>
6350  &lt;/row>
6351 &lt;/dataset>
6352 </code></pre>
6353  * @cfg {String} totalRecords The DomQuery path from which to retrieve the total number of records
6354  * in the dataset. This is only needed if the whole dataset is not passed in one go, but is being
6355  * paged from the remote server.
6356  * @cfg {String} record The DomQuery path to the repeated element which contains record information.
6357  * @cfg {String} success The DomQuery path to the success attribute used by forms.
6358  * @cfg {String} id The DomQuery path relative from the record element to the element that contains
6359  * a record identifier value.
6360  * @constructor
6361  * Create a new XmlReader
6362  * @param {Object} meta Metadata configuration options
6363  * @param {Mixed} recordType The definition of the data record type to produce.  This can be either a valid
6364  * Record subclass created with {@link Roo.data.Record#create}, or an array of objects with which to call
6365  * Roo.data.Record.create.  See the {@link Roo.data.Record} class for more details.
6366  */
6367 Roo.data.XmlReader = function(meta, recordType){
6368     meta = meta || {};
6369     Roo.data.XmlReader.superclass.constructor.call(this, meta, recordType||meta.fields);
6370 };
6371 Roo.extend(Roo.data.XmlReader, Roo.data.DataReader, {
6372     /**
6373      * This method is only used by a DataProxy which has retrieved data from a remote server.
6374          * @param {Object} response The XHR object which contains the parsed XML document.  The response is expected
6375          * to contain a method called 'responseXML' that returns an XML document object.
6376      * @return {Object} records A data block which is used by an {@link Roo.data.Store} as
6377      * a cache of Roo.data.Records.
6378      */
6379     read : function(response){
6380         var doc = response.responseXML;
6381         if(!doc) {
6382             throw {message: "XmlReader.read: XML Document not available"};
6383         }
6384         return this.readRecords(doc);
6385     },
6386
6387     /**
6388      * Create a data block containing Roo.data.Records from an XML document.
6389          * @param {Object} doc A parsed XML document.
6390      * @return {Object} records A data block which is used by an {@link Roo.data.Store} as
6391      * a cache of Roo.data.Records.
6392      */
6393     readRecords : function(doc){
6394         /**
6395          * After any data loads/reads, the raw XML Document is available for further custom processing.
6396          * @type XMLDocument
6397          */
6398         this.xmlData = doc;
6399         var root = doc.documentElement || doc;
6400         var q = Roo.DomQuery;
6401         var recordType = this.recordType, fields = recordType.prototype.fields;
6402         var sid = this.meta.id;
6403         var totalRecords = 0, success = true;
6404         if(this.meta.totalRecords){
6405             totalRecords = q.selectNumber(this.meta.totalRecords, root, 0);
6406         }
6407         
6408         if(this.meta.success){
6409             var sv = q.selectValue(this.meta.success, root, true);
6410             success = sv !== false && sv !== 'false';
6411         }
6412         var records = [];
6413         var ns = q.select(this.meta.record, root);
6414         for(var i = 0, len = ns.length; i < len; i++) {
6415                 var n = ns[i];
6416                 var values = {};
6417                 var id = sid ? q.selectValue(sid, n) : undefined;
6418                 for(var j = 0, jlen = fields.length; j < jlen; j++){
6419                     var f = fields.items[j];
6420                 var v = q.selectValue(f.mapping || f.name, n, f.defaultValue);
6421                     v = f.convert(v);
6422                     values[f.name] = v;
6423                 }
6424                 var record = new recordType(values, id);
6425                 record.node = n;
6426                 records[records.length] = record;
6427             }
6428
6429             return {
6430                 success : success,
6431                 records : records,
6432                 totalRecords : totalRecords || records.length
6433             };
6434     }
6435 });/*
6436  * Based on:
6437  * Ext JS Library 1.1.1
6438  * Copyright(c) 2006-2007, Ext JS, LLC.
6439  *
6440  * Originally Released Under LGPL - original licence link has changed is not relivant.
6441  *
6442  * Fork - LGPL
6443  * <script type="text/javascript">
6444  */
6445
6446 /**
6447  * @class Roo.data.ArrayReader
6448  * @extends Roo.data.DataReader
6449  * Data reader class to create an Array of Roo.data.Record objects from an Array.
6450  * Each element of that Array represents a row of data fields. The
6451  * fields are pulled into a Record object using as a subscript, the <em>mapping</em> property
6452  * of the field definition if it exists, or the field's ordinal position in the definition.<br>
6453  * <p>
6454  * Example code:.
6455  * <pre><code>
6456 var RecordDef = Roo.data.Record.create([
6457     {name: 'name', mapping: 1},         // "mapping" only needed if an "id" field is present which
6458     {name: 'occupation', mapping: 2}    // precludes using the ordinal position as the index.
6459 ]);
6460 var myReader = new Roo.data.ArrayReader({
6461     id: 0                     // The subscript within row Array that provides an ID for the Record (optional)
6462 }, RecordDef);
6463 </code></pre>
6464  * <p>
6465  * This would consume an Array like this:
6466  * <pre><code>
6467 [ [1, 'Bill', 'Gardener'], [2, 'Ben', 'Horticulturalist'] ]
6468   </code></pre>
6469  * @cfg {String} id (optional) The subscript within row Array that provides an ID for the Record
6470  * @constructor
6471  * Create a new JsonReader
6472  * @param {Object} meta Metadata configuration options.
6473  * @param {Object} recordType Either an Array of field definition objects
6474  * as specified to {@link Roo.data.Record#create},
6475  * or an {@link Roo.data.Record} object
6476  * created using {@link Roo.data.Record#create}.
6477  */
6478 Roo.data.ArrayReader = function(meta, recordType){
6479     Roo.data.ArrayReader.superclass.constructor.call(this, meta, recordType);
6480 };
6481
6482 Roo.extend(Roo.data.ArrayReader, Roo.data.JsonReader, {
6483     /**
6484      * Create a data block containing Roo.data.Records from an XML document.
6485      * @param {Object} o An Array of row objects which represents the dataset.
6486      * @return {Object} data A data block which is used by an Roo.data.Store object as
6487      * a cache of Roo.data.Records.
6488      */
6489     readRecords : function(o){
6490         var sid = this.meta ? this.meta.id : null;
6491         var recordType = this.recordType, fields = recordType.prototype.fields;
6492         var records = [];
6493         var root = o;
6494             for(var i = 0; i < root.length; i++){
6495                     var n = root[i];
6496                 var values = {};
6497                 var id = ((sid || sid === 0) && n[sid] !== undefined && n[sid] !== "" ? n[sid] : null);
6498                 for(var j = 0, jlen = fields.length; j < jlen; j++){
6499                 var f = fields.items[j];
6500                 var k = f.mapping !== undefined && f.mapping !== null ? f.mapping : j;
6501                 var v = n[k] !== undefined ? n[k] : f.defaultValue;
6502                 v = f.convert(v);
6503                 values[f.name] = v;
6504             }
6505                 var record = new recordType(values, id);
6506                 record.json = n;
6507                 records[records.length] = record;
6508             }
6509             return {
6510                 records : records,
6511                 totalRecords : records.length
6512             };
6513     }
6514 });/*
6515  * Based on:
6516  * Ext JS Library 1.1.1
6517  * Copyright(c) 2006-2007, Ext JS, LLC.
6518  *
6519  * Originally Released Under LGPL - original licence link has changed is not relivant.
6520  *
6521  * Fork - LGPL
6522  * <script type="text/javascript">
6523  */
6524
6525
6526 /**
6527  * @class Roo.data.Tree
6528  * @extends Roo.util.Observable
6529  * Represents a tree data structure and bubbles all the events for its nodes. The nodes
6530  * in the tree have most standard DOM functionality.
6531  * @constructor
6532  * @param {Node} root (optional) The root node
6533  */
6534 Roo.data.Tree = function(root){
6535    this.nodeHash = {};
6536    /**
6537     * The root node for this tree
6538     * @type Node
6539     */
6540    this.root = null;
6541    if(root){
6542        this.setRootNode(root);
6543    }
6544    this.addEvents({
6545        /**
6546         * @event append
6547         * Fires when a new child node is appended to a node in this tree.
6548         * @param {Tree} tree The owner tree
6549         * @param {Node} parent The parent node
6550         * @param {Node} node The newly appended node
6551         * @param {Number} index The index of the newly appended node
6552         */
6553        "append" : true,
6554        /**
6555         * @event remove
6556         * Fires when a child node is removed from a node in this tree.
6557         * @param {Tree} tree The owner tree
6558         * @param {Node} parent The parent node
6559         * @param {Node} node The child node removed
6560         */
6561        "remove" : true,
6562        /**
6563         * @event move
6564         * Fires when a node is moved to a new location in the tree
6565         * @param {Tree} tree The owner tree
6566         * @param {Node} node The node moved
6567         * @param {Node} oldParent The old parent of this node
6568         * @param {Node} newParent The new parent of this node
6569         * @param {Number} index The index it was moved to
6570         */
6571        "move" : true,
6572        /**
6573         * @event insert
6574         * Fires when a new child node is inserted in a node in this tree.
6575         * @param {Tree} tree The owner tree
6576         * @param {Node} parent The parent node
6577         * @param {Node} node The child node inserted
6578         * @param {Node} refNode The child node the node was inserted before
6579         */
6580        "insert" : true,
6581        /**
6582         * @event beforeappend
6583         * Fires before a new child is appended to a node in this tree, return false to cancel the append.
6584         * @param {Tree} tree The owner tree
6585         * @param {Node} parent The parent node
6586         * @param {Node} node The child node to be appended
6587         */
6588        "beforeappend" : true,
6589        /**
6590         * @event beforeremove
6591         * Fires before a child is removed from a node in this tree, return false to cancel the remove.
6592         * @param {Tree} tree The owner tree
6593         * @param {Node} parent The parent node
6594         * @param {Node} node The child node to be removed
6595         */
6596        "beforeremove" : true,
6597        /**
6598         * @event beforemove
6599         * Fires before a node is moved to a new location in the tree. Return false to cancel the move.
6600         * @param {Tree} tree The owner tree
6601         * @param {Node} node The node being moved
6602         * @param {Node} oldParent The parent of the node
6603         * @param {Node} newParent The new parent the node is moving to
6604         * @param {Number} index The index it is being moved to
6605         */
6606        "beforemove" : true,
6607        /**
6608         * @event beforeinsert
6609         * Fires before a new child is inserted in a node in this tree, return false to cancel the insert.
6610         * @param {Tree} tree The owner tree
6611         * @param {Node} parent The parent node
6612         * @param {Node} node The child node to be inserted
6613         * @param {Node} refNode The child node the node is being inserted before
6614         */
6615        "beforeinsert" : true
6616    });
6617
6618     Roo.data.Tree.superclass.constructor.call(this);
6619 };
6620
6621 Roo.extend(Roo.data.Tree, Roo.util.Observable, {
6622     pathSeparator: "/",
6623
6624     proxyNodeEvent : function(){
6625         return this.fireEvent.apply(this, arguments);
6626     },
6627
6628     /**
6629      * Returns the root node for this tree.
6630      * @return {Node}
6631      */
6632     getRootNode : function(){
6633         return this.root;
6634     },
6635
6636     /**
6637      * Sets the root node for this tree.
6638      * @param {Node} node
6639      * @return {Node}
6640      */
6641     setRootNode : function(node){
6642         this.root = node;
6643         node.ownerTree = this;
6644         node.isRoot = true;
6645         this.registerNode(node);
6646         return node;
6647     },
6648
6649     /**
6650      * Gets a node in this tree by its id.
6651      * @param {String} id
6652      * @return {Node}
6653      */
6654     getNodeById : function(id){
6655         return this.nodeHash[id];
6656     },
6657
6658     registerNode : function(node){
6659         this.nodeHash[node.id] = node;
6660     },
6661
6662     unregisterNode : function(node){
6663         delete this.nodeHash[node.id];
6664     },
6665
6666     toString : function(){
6667         return "[Tree"+(this.id?" "+this.id:"")+"]";
6668     }
6669 });
6670
6671 /**
6672  * @class Roo.data.Node
6673  * @extends Roo.util.Observable
6674  * @cfg {Boolean} leaf true if this node is a leaf and does not have children
6675  * @cfg {String} id The id for this node. If one is not specified, one is generated.
6676  * @constructor
6677  * @param {Object} attributes The attributes/config for the node
6678  */
6679 Roo.data.Node = function(attributes){
6680     /**
6681      * The attributes supplied for the node. You can use this property to access any custom attributes you supplied.
6682      * @type {Object}
6683      */
6684     this.attributes = attributes || {};
6685     this.leaf = this.attributes.leaf;
6686     /**
6687      * The node id. @type String
6688      */
6689     this.id = this.attributes.id;
6690     if(!this.id){
6691         this.id = Roo.id(null, "ynode-");
6692         this.attributes.id = this.id;
6693     }
6694      
6695     
6696     /**
6697      * All child nodes of this node. @type Array
6698      */
6699     this.childNodes = [];
6700     if(!this.childNodes.indexOf){ // indexOf is a must
6701         this.childNodes.indexOf = function(o){
6702             for(var i = 0, len = this.length; i < len; i++){
6703                 if(this[i] == o) {
6704                     return i;
6705                 }
6706             }
6707             return -1;
6708         };
6709     }
6710     /**
6711      * The parent node for this node. @type Node
6712      */
6713     this.parentNode = null;
6714     /**
6715      * The first direct child node of this node, or null if this node has no child nodes. @type Node
6716      */
6717     this.firstChild = null;
6718     /**
6719      * The last direct child node of this node, or null if this node has no child nodes. @type Node
6720      */
6721     this.lastChild = null;
6722     /**
6723      * The node immediately preceding this node in the tree, or null if there is no sibling node. @type Node
6724      */
6725     this.previousSibling = null;
6726     /**
6727      * The node immediately following this node in the tree, or null if there is no sibling node. @type Node
6728      */
6729     this.nextSibling = null;
6730
6731     this.addEvents({
6732        /**
6733         * @event append
6734         * Fires when a new child node is appended
6735         * @param {Tree} tree The owner tree
6736         * @param {Node} this This node
6737         * @param {Node} node The newly appended node
6738         * @param {Number} index The index of the newly appended node
6739         */
6740        "append" : true,
6741        /**
6742         * @event remove
6743         * Fires when a child node is removed
6744         * @param {Tree} tree The owner tree
6745         * @param {Node} this This node
6746         * @param {Node} node The removed node
6747         */
6748        "remove" : true,
6749        /**
6750         * @event move
6751         * Fires when this node is moved to a new location in the tree
6752         * @param {Tree} tree The owner tree
6753         * @param {Node} this This node
6754         * @param {Node} oldParent The old parent of this node
6755         * @param {Node} newParent The new parent of this node
6756         * @param {Number} index The index it was moved to
6757         */
6758        "move" : true,
6759        /**
6760         * @event insert
6761         * Fires when a new child node is inserted.
6762         * @param {Tree} tree The owner tree
6763         * @param {Node} this This node
6764         * @param {Node} node The child node inserted
6765         * @param {Node} refNode The child node the node was inserted before
6766         */
6767        "insert" : true,
6768        /**
6769         * @event beforeappend
6770         * Fires before a new child is appended, return false to cancel the append.
6771         * @param {Tree} tree The owner tree
6772         * @param {Node} this This node
6773         * @param {Node} node The child node to be appended
6774         */
6775        "beforeappend" : true,
6776        /**
6777         * @event beforeremove
6778         * Fires before a child is removed, return false to cancel the remove.
6779         * @param {Tree} tree The owner tree
6780         * @param {Node} this This node
6781         * @param {Node} node The child node to be removed
6782         */
6783        "beforeremove" : true,
6784        /**
6785         * @event beforemove
6786         * Fires before this node is moved to a new location in the tree. Return false to cancel the move.
6787         * @param {Tree} tree The owner tree
6788         * @param {Node} this This node
6789         * @param {Node} oldParent The parent of this node
6790         * @param {Node} newParent The new parent this node is moving to
6791         * @param {Number} index The index it is being moved to
6792         */
6793        "beforemove" : true,
6794        /**
6795         * @event beforeinsert
6796         * Fires before a new child is inserted, return false to cancel the insert.
6797         * @param {Tree} tree The owner tree
6798         * @param {Node} this This node
6799         * @param {Node} node The child node to be inserted
6800         * @param {Node} refNode The child node the node is being inserted before
6801         */
6802        "beforeinsert" : true
6803    });
6804     this.listeners = this.attributes.listeners;
6805     Roo.data.Node.superclass.constructor.call(this);
6806 };
6807
6808 Roo.extend(Roo.data.Node, Roo.util.Observable, {
6809     fireEvent : function(evtName){
6810         // first do standard event for this node
6811         if(Roo.data.Node.superclass.fireEvent.apply(this, arguments) === false){
6812             return false;
6813         }
6814         // then bubble it up to the tree if the event wasn't cancelled
6815         var ot = this.getOwnerTree();
6816         if(ot){
6817             if(ot.proxyNodeEvent.apply(ot, arguments) === false){
6818                 return false;
6819             }
6820         }
6821         return true;
6822     },
6823
6824     /**
6825      * Returns true if this node is a leaf
6826      * @return {Boolean}
6827      */
6828     isLeaf : function(){
6829         return this.leaf === true;
6830     },
6831
6832     // private
6833     setFirstChild : function(node){
6834         this.firstChild = node;
6835     },
6836
6837     //private
6838     setLastChild : function(node){
6839         this.lastChild = node;
6840     },
6841
6842
6843     /**
6844      * Returns true if this node is the last child of its parent
6845      * @return {Boolean}
6846      */
6847     isLast : function(){
6848        return (!this.parentNode ? true : this.parentNode.lastChild == this);
6849     },
6850
6851     /**
6852      * Returns true if this node is the first child of its parent
6853      * @return {Boolean}
6854      */
6855     isFirst : function(){
6856        return (!this.parentNode ? true : this.parentNode.firstChild == this);
6857     },
6858
6859     hasChildNodes : function(){
6860         return !this.isLeaf() && this.childNodes.length > 0;
6861     },
6862
6863     /**
6864      * Insert node(s) as the last child node of this node.
6865      * @param {Node/Array} node The node or Array of nodes to append
6866      * @return {Node} The appended node if single append, or null if an array was passed
6867      */
6868     appendChild : function(node){
6869         var multi = false;
6870         if(node instanceof Array){
6871             multi = node;
6872         }else if(arguments.length > 1){
6873             multi = arguments;
6874         }
6875         // if passed an array or multiple args do them one by one
6876         if(multi){
6877             for(var i = 0, len = multi.length; i < len; i++) {
6878                 this.appendChild(multi[i]);
6879             }
6880         }else{
6881             if(this.fireEvent("beforeappend", this.ownerTree, this, node) === false){
6882                 return false;
6883             }
6884             var index = this.childNodes.length;
6885             var oldParent = node.parentNode;
6886             // it's a move, make sure we move it cleanly
6887             if(oldParent){
6888                 if(node.fireEvent("beforemove", node.getOwnerTree(), node, oldParent, this, index) === false){
6889                     return false;
6890                 }
6891                 oldParent.removeChild(node);
6892             }
6893             index = this.childNodes.length;
6894             if(index == 0){
6895                 this.setFirstChild(node);
6896             }
6897             this.childNodes.push(node);
6898             node.parentNode = this;
6899             var ps = this.childNodes[index-1];
6900             if(ps){
6901                 node.previousSibling = ps;
6902                 ps.nextSibling = node;
6903             }else{
6904                 node.previousSibling = null;
6905             }
6906             node.nextSibling = null;
6907             this.setLastChild(node);
6908             node.setOwnerTree(this.getOwnerTree());
6909             this.fireEvent("append", this.ownerTree, this, node, index);
6910             if(oldParent){
6911                 node.fireEvent("move", this.ownerTree, node, oldParent, this, index);
6912             }
6913             return node;
6914         }
6915     },
6916
6917     /**
6918      * Removes a child node from this node.
6919      * @param {Node} node The node to remove
6920      * @return {Node} The removed node
6921      */
6922     removeChild : function(node){
6923         var index = this.childNodes.indexOf(node);
6924         if(index == -1){
6925             return false;
6926         }
6927         if(this.fireEvent("beforeremove", this.ownerTree, this, node) === false){
6928             return false;
6929         }
6930
6931         // remove it from childNodes collection
6932         this.childNodes.splice(index, 1);
6933
6934         // update siblings
6935         if(node.previousSibling){
6936             node.previousSibling.nextSibling = node.nextSibling;
6937         }
6938         if(node.nextSibling){
6939             node.nextSibling.previousSibling = node.previousSibling;
6940         }
6941
6942         // update child refs
6943         if(this.firstChild == node){
6944             this.setFirstChild(node.nextSibling);
6945         }
6946         if(this.lastChild == node){
6947             this.setLastChild(node.previousSibling);
6948         }
6949
6950         node.setOwnerTree(null);
6951         // clear any references from the node
6952         node.parentNode = null;
6953         node.previousSibling = null;
6954         node.nextSibling = null;
6955         this.fireEvent("remove", this.ownerTree, this, node);
6956         return node;
6957     },
6958
6959     /**
6960      * Inserts the first node before the second node in this nodes childNodes collection.
6961      * @param {Node} node The node to insert
6962      * @param {Node} refNode The node to insert before (if null the node is appended)
6963      * @return {Node} The inserted node
6964      */
6965     insertBefore : function(node, refNode){
6966         if(!refNode){ // like standard Dom, refNode can be null for append
6967             return this.appendChild(node);
6968         }
6969         // nothing to do
6970         if(node == refNode){
6971             return false;
6972         }
6973
6974         if(this.fireEvent("beforeinsert", this.ownerTree, this, node, refNode) === false){
6975             return false;
6976         }
6977         var index = this.childNodes.indexOf(refNode);
6978         var oldParent = node.parentNode;
6979         var refIndex = index;
6980
6981         // when moving internally, indexes will change after remove
6982         if(oldParent == this && this.childNodes.indexOf(node) < index){
6983             refIndex--;
6984         }
6985
6986         // it's a move, make sure we move it cleanly
6987         if(oldParent){
6988             if(node.fireEvent("beforemove", node.getOwnerTree(), node, oldParent, this, index, refNode) === false){
6989                 return false;
6990             }
6991             oldParent.removeChild(node);
6992         }
6993         if(refIndex == 0){
6994             this.setFirstChild(node);
6995         }
6996         this.childNodes.splice(refIndex, 0, node);
6997         node.parentNode = this;
6998         var ps = this.childNodes[refIndex-1];
6999         if(ps){
7000             node.previousSibling = ps;
7001             ps.nextSibling = node;
7002         }else{
7003             node.previousSibling = null;
7004         }
7005         node.nextSibling = refNode;
7006         refNode.previousSibling = node;
7007         node.setOwnerTree(this.getOwnerTree());
7008         this.fireEvent("insert", this.ownerTree, this, node, refNode);
7009         if(oldParent){
7010             node.fireEvent("move", this.ownerTree, node, oldParent, this, refIndex, refNode);
7011         }
7012         return node;
7013     },
7014
7015     /**
7016      * Returns the child node at the specified index.
7017      * @param {Number} index
7018      * @return {Node}
7019      */
7020     item : function(index){
7021         return this.childNodes[index];
7022     },
7023
7024     /**
7025      * Replaces one child node in this node with another.
7026      * @param {Node} newChild The replacement node
7027      * @param {Node} oldChild The node to replace
7028      * @return {Node} The replaced node
7029      */
7030     replaceChild : function(newChild, oldChild){
7031         this.insertBefore(newChild, oldChild);
7032         this.removeChild(oldChild);
7033         return oldChild;
7034     },
7035
7036     /**
7037      * Returns the index of a child node
7038      * @param {Node} node
7039      * @return {Number} The index of the node or -1 if it was not found
7040      */
7041     indexOf : function(child){
7042         return this.childNodes.indexOf(child);
7043     },
7044
7045     /**
7046      * Returns the tree this node is in.
7047      * @return {Tree}
7048      */
7049     getOwnerTree : function(){
7050         // if it doesn't have one, look for one
7051         if(!this.ownerTree){
7052             var p = this;
7053             while(p){
7054                 if(p.ownerTree){
7055                     this.ownerTree = p.ownerTree;
7056                     break;
7057                 }
7058                 p = p.parentNode;
7059             }
7060         }
7061         return this.ownerTree;
7062     },
7063
7064     /**
7065      * Returns depth of this node (the root node has a depth of 0)
7066      * @return {Number}
7067      */
7068     getDepth : function(){
7069         var depth = 0;
7070         var p = this;
7071         while(p.parentNode){
7072             ++depth;
7073             p = p.parentNode;
7074         }
7075         return depth;
7076     },
7077
7078     // private
7079     setOwnerTree : function(tree){
7080         // if it's move, we need to update everyone
7081         if(tree != this.ownerTree){
7082             if(this.ownerTree){
7083                 this.ownerTree.unregisterNode(this);
7084             }
7085             this.ownerTree = tree;
7086             var cs = this.childNodes;
7087             for(var i = 0, len = cs.length; i < len; i++) {
7088                 cs[i].setOwnerTree(tree);
7089             }
7090             if(tree){
7091                 tree.registerNode(this);
7092             }
7093         }
7094     },
7095
7096     /**
7097      * Returns the path for this node. The path can be used to expand or select this node programmatically.
7098      * @param {String} attr (optional) The attr to use for the path (defaults to the node's id)
7099      * @return {String} The path
7100      */
7101     getPath : function(attr){
7102         attr = attr || "id";
7103         var p = this.parentNode;
7104         var b = [this.attributes[attr]];
7105         while(p){
7106             b.unshift(p.attributes[attr]);
7107             p = p.parentNode;
7108         }
7109         var sep = this.getOwnerTree().pathSeparator;
7110         return sep + b.join(sep);
7111     },
7112
7113     /**
7114      * Bubbles up the tree from this node, calling the specified function with each node. The scope (<i>this</i>) of
7115      * function call will be the scope provided or the current node. The arguments to the function
7116      * will be the args provided or the current node. If the function returns false at any point,
7117      * the bubble is stopped.
7118      * @param {Function} fn The function to call
7119      * @param {Object} scope (optional) The scope of the function (defaults to current node)
7120      * @param {Array} args (optional) The args to call the function with (default to passing the current node)
7121      */
7122     bubble : function(fn, scope, args){
7123         var p = this;
7124         while(p){
7125             if(fn.call(scope || p, args || p) === false){
7126                 break;
7127             }
7128             p = p.parentNode;
7129         }
7130     },
7131
7132     /**
7133      * Cascades down the tree from this node, calling the specified function with each node. The scope (<i>this</i>) of
7134      * function call will be the scope provided or the current node. The arguments to the function
7135      * will be the args provided or the current node. If the function returns false at any point,
7136      * the cascade is stopped on that branch.
7137      * @param {Function} fn The function to call
7138      * @param {Object} scope (optional) The scope of the function (defaults to current node)
7139      * @param {Array} args (optional) The args to call the function with (default to passing the current node)
7140      */
7141     cascade : function(fn, scope, args){
7142         if(fn.call(scope || this, args || this) !== false){
7143             var cs = this.childNodes;
7144             for(var i = 0, len = cs.length; i < len; i++) {
7145                 cs[i].cascade(fn, scope, args);
7146             }
7147         }
7148     },
7149
7150     /**
7151      * Interates the child nodes of this node, calling the specified function with each node. The scope (<i>this</i>) of
7152      * function call will be the scope provided or the current node. The arguments to the function
7153      * will be the args provided or the current node. If the function returns false at any point,
7154      * the iteration stops.
7155      * @param {Function} fn The function to call
7156      * @param {Object} scope (optional) The scope of the function (defaults to current node)
7157      * @param {Array} args (optional) The args to call the function with (default to passing the current node)
7158      */
7159     eachChild : function(fn, scope, args){
7160         var cs = this.childNodes;
7161         for(var i = 0, len = cs.length; i < len; i++) {
7162                 if(fn.call(scope || this, args || cs[i]) === false){
7163                     break;
7164                 }
7165         }
7166     },
7167
7168     /**
7169      * Finds the first child that has the attribute with the specified value.
7170      * @param {String} attribute The attribute name
7171      * @param {Mixed} value The value to search for
7172      * @return {Node} The found child or null if none was found
7173      */
7174     findChild : function(attribute, value){
7175         var cs = this.childNodes;
7176         for(var i = 0, len = cs.length; i < len; i++) {
7177                 if(cs[i].attributes[attribute] == value){
7178                     return cs[i];
7179                 }
7180         }
7181         return null;
7182     },
7183
7184     /**
7185      * Finds the first child by a custom function. The child matches if the function passed
7186      * returns true.
7187      * @param {Function} fn
7188      * @param {Object} scope (optional)
7189      * @return {Node} The found child or null if none was found
7190      */
7191     findChildBy : function(fn, scope){
7192         var cs = this.childNodes;
7193         for(var i = 0, len = cs.length; i < len; i++) {
7194                 if(fn.call(scope||cs[i], cs[i]) === true){
7195                     return cs[i];
7196                 }
7197         }
7198         return null;
7199     },
7200
7201     /**
7202      * Sorts this nodes children using the supplied sort function
7203      * @param {Function} fn
7204      * @param {Object} scope (optional)
7205      */
7206     sort : function(fn, scope){
7207         var cs = this.childNodes;
7208         var len = cs.length;
7209         if(len > 0){
7210             var sortFn = scope ? function(){fn.apply(scope, arguments);} : fn;
7211             cs.sort(sortFn);
7212             for(var i = 0; i < len; i++){
7213                 var n = cs[i];
7214                 n.previousSibling = cs[i-1];
7215                 n.nextSibling = cs[i+1];
7216                 if(i == 0){
7217                     this.setFirstChild(n);
7218                 }
7219                 if(i == len-1){
7220                     this.setLastChild(n);
7221                 }
7222             }
7223         }
7224     },
7225
7226     /**
7227      * Returns true if this node is an ancestor (at any point) of the passed node.
7228      * @param {Node} node
7229      * @return {Boolean}
7230      */
7231     contains : function(node){
7232         return node.isAncestor(this);
7233     },
7234
7235     /**
7236      * Returns true if the passed node is an ancestor (at any point) of this node.
7237      * @param {Node} node
7238      * @return {Boolean}
7239      */
7240     isAncestor : function(node){
7241         var p = this.parentNode;
7242         while(p){
7243             if(p == node){
7244                 return true;
7245             }
7246             p = p.parentNode;
7247         }
7248         return false;
7249     },
7250
7251     toString : function(){
7252         return "[Node"+(this.id?" "+this.id:"")+"]";
7253     }
7254 });/*
7255  * Based on:
7256  * Ext JS Library 1.1.1
7257  * Copyright(c) 2006-2007, Ext JS, LLC.
7258  *
7259  * Originally Released Under LGPL - original licence link has changed is not relivant.
7260  *
7261  * Fork - LGPL
7262  * <script type="text/javascript">
7263  */
7264  
7265
7266 /**
7267  * @class Roo.ComponentMgr
7268  * Provides a common registry of all components on a page so that they can be easily accessed by component id (see {@link Roo.getCmp}).
7269  * @singleton
7270  */
7271 Roo.ComponentMgr = function(){
7272     var all = new Roo.util.MixedCollection();
7273
7274     return {
7275         /**
7276          * Registers a component.
7277          * @param {Roo.Component} c The component
7278          */
7279         register : function(c){
7280             all.add(c);
7281         },
7282
7283         /**
7284          * Unregisters a component.
7285          * @param {Roo.Component} c The component
7286          */
7287         unregister : function(c){
7288             all.remove(c);
7289         },
7290
7291         /**
7292          * Returns a component by id
7293          * @param {String} id The component id
7294          */
7295         get : function(id){
7296             return all.get(id);
7297         },
7298
7299         /**
7300          * Registers a function that will be called when a specified component is added to ComponentMgr
7301          * @param {String} id The component id
7302          * @param {Funtction} fn The callback function
7303          * @param {Object} scope The scope of the callback
7304          */
7305         onAvailable : function(id, fn, scope){
7306             all.on("add", function(index, o){
7307                 if(o.id == id){
7308                     fn.call(scope || o, o);
7309                     all.un("add", fn, scope);
7310                 }
7311             });
7312         }
7313     };
7314 }();/*
7315  * Based on:
7316  * Ext JS Library 1.1.1
7317  * Copyright(c) 2006-2007, Ext JS, LLC.
7318  *
7319  * Originally Released Under LGPL - original licence link has changed is not relivant.
7320  *
7321  * Fork - LGPL
7322  * <script type="text/javascript">
7323  */
7324  
7325 /**
7326  * @class Roo.Component
7327  * @extends Roo.util.Observable
7328  * Base class for all major Roo components.  All subclasses of Component can automatically participate in the standard
7329  * Roo component lifecycle of creation, rendering and destruction.  They also have automatic support for basic hide/show
7330  * and enable/disable behavior.  Component allows any subclass to be lazy-rendered into any {@link Roo.Container} and
7331  * to be automatically registered with the {@link Roo.ComponentMgr} so that it can be referenced at any time via {@link Roo.getCmp}.
7332  * All visual components (widgets) that require rendering into a layout should subclass Component.
7333  * @constructor
7334  * @param {Roo.Element/String/Object} config The configuration options.  If an element is passed, it is set as the internal
7335  * 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
7336  * and is used as the component id.  Otherwise, it is assumed to be a standard config object and is applied to the component.
7337  */
7338 Roo.Component = function(config){
7339     config = config || {};
7340     if(config.tagName || config.dom || typeof config == "string"){ // element object
7341         config = {el: config, id: config.id || config};
7342     }
7343     this.initialConfig = config;
7344
7345     Roo.apply(this, config);
7346     this.addEvents({
7347         /**
7348          * @event disable
7349          * Fires after the component is disabled.
7350              * @param {Roo.Component} this
7351              */
7352         disable : true,
7353         /**
7354          * @event enable
7355          * Fires after the component is enabled.
7356              * @param {Roo.Component} this
7357              */
7358         enable : true,
7359         /**
7360          * @event beforeshow
7361          * Fires before the component is shown.  Return false to stop the show.
7362              * @param {Roo.Component} this
7363              */
7364         beforeshow : true,
7365         /**
7366          * @event show
7367          * Fires after the component is shown.
7368              * @param {Roo.Component} this
7369              */
7370         show : true,
7371         /**
7372          * @event beforehide
7373          * Fires before the component is hidden. Return false to stop the hide.
7374              * @param {Roo.Component} this
7375              */
7376         beforehide : true,
7377         /**
7378          * @event hide
7379          * Fires after the component is hidden.
7380              * @param {Roo.Component} this
7381              */
7382         hide : true,
7383         /**
7384          * @event beforerender
7385          * Fires before the component is rendered. Return false to stop the render.
7386              * @param {Roo.Component} this
7387              */
7388         beforerender : true,
7389         /**
7390          * @event render
7391          * Fires after the component is rendered.
7392              * @param {Roo.Component} this
7393              */
7394         render : true,
7395         /**
7396          * @event beforedestroy
7397          * Fires before the component is destroyed. Return false to stop the destroy.
7398              * @param {Roo.Component} this
7399              */
7400         beforedestroy : true,
7401         /**
7402          * @event destroy
7403          * Fires after the component is destroyed.
7404              * @param {Roo.Component} this
7405              */
7406         destroy : true
7407     });
7408     if(!this.id){
7409         this.id = "ext-comp-" + (++Roo.Component.AUTO_ID);
7410     }
7411     Roo.ComponentMgr.register(this);
7412     Roo.Component.superclass.constructor.call(this);
7413     this.initComponent();
7414     if(this.renderTo){ // not supported by all components yet. use at your own risk!
7415         this.render(this.renderTo);
7416         delete this.renderTo;
7417     }
7418 };
7419
7420 /** @private */
7421 Roo.Component.AUTO_ID = 1000;
7422
7423 Roo.extend(Roo.Component, Roo.util.Observable, {
7424     /**
7425      * @scope Roo.Component.prototype
7426      * @type {Boolean}
7427      * true if this component is hidden. Read-only.
7428      */
7429     hidden : false,
7430     /**
7431      * @type {Boolean}
7432      * true if this component is disabled. Read-only.
7433      */
7434     disabled : false,
7435     /**
7436      * @type {Boolean}
7437      * true if this component has been rendered. Read-only.
7438      */
7439     rendered : false,
7440     
7441     /** @cfg {String} disableClass
7442      * CSS class added to the component when it is disabled (defaults to "x-item-disabled").
7443      */
7444     disabledClass : "x-item-disabled",
7445         /** @cfg {Boolean} allowDomMove
7446          * Whether the component can move the Dom node when rendering (defaults to true).
7447          */
7448     allowDomMove : true,
7449     /** @cfg {String} hideMode
7450      * How this component should hidden. Supported values are
7451      * "visibility" (css visibility), "offsets" (negative offset position) and
7452      * "display" (css display) - defaults to "display".
7453      */
7454     hideMode: 'display',
7455
7456     /** @private */
7457     ctype : "Roo.Component",
7458
7459     /**
7460      * @cfg {String} actionMode 
7461      * which property holds the element that used for  hide() / show() / disable() / enable()
7462      * default is 'el' 
7463      */
7464     actionMode : "el",
7465
7466     /** @private */
7467     getActionEl : function(){
7468         return this[this.actionMode];
7469     },
7470
7471     initComponent : Roo.emptyFn,
7472     /**
7473      * If this is a lazy rendering component, render it to its container element.
7474      * @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.
7475      */
7476     render : function(container, position){
7477         if(!this.rendered && this.fireEvent("beforerender", this) !== false){
7478             if(!container && this.el){
7479                 this.el = Roo.get(this.el);
7480                 container = this.el.dom.parentNode;
7481                 this.allowDomMove = false;
7482             }
7483             this.container = Roo.get(container);
7484             this.rendered = true;
7485             if(position !== undefined){
7486                 if(typeof position == 'number'){
7487                     position = this.container.dom.childNodes[position];
7488                 }else{
7489                     position = Roo.getDom(position);
7490                 }
7491             }
7492             this.onRender(this.container, position || null);
7493             if(this.cls){
7494                 this.el.addClass(this.cls);
7495                 delete this.cls;
7496             }
7497             if(this.style){
7498                 this.el.applyStyles(this.style);
7499                 delete this.style;
7500             }
7501             this.fireEvent("render", this);
7502             this.afterRender(this.container);
7503             if(this.hidden){
7504                 this.hide();
7505             }
7506             if(this.disabled){
7507                 this.disable();
7508             }
7509         }
7510         return this;
7511     },
7512
7513     /** @private */
7514     // default function is not really useful
7515     onRender : function(ct, position){
7516         if(this.el){
7517             this.el = Roo.get(this.el);
7518             if(this.allowDomMove !== false){
7519                 ct.dom.insertBefore(this.el.dom, position);
7520             }
7521         }
7522     },
7523
7524     /** @private */
7525     getAutoCreate : function(){
7526         var cfg = typeof this.autoCreate == "object" ?
7527                       this.autoCreate : Roo.apply({}, this.defaultAutoCreate);
7528         if(this.id && !cfg.id){
7529             cfg.id = this.id;
7530         }
7531         return cfg;
7532     },
7533
7534     /** @private */
7535     afterRender : Roo.emptyFn,
7536
7537     /**
7538      * Destroys this component by purging any event listeners, removing the component's element from the DOM,
7539      * removing the component from its {@link Roo.Container} (if applicable) and unregistering it from {@link Roo.ComponentMgr}.
7540      */
7541     destroy : function(){
7542         if(this.fireEvent("beforedestroy", this) !== false){
7543             this.purgeListeners();
7544             this.beforeDestroy();
7545             if(this.rendered){
7546                 this.el.removeAllListeners();
7547                 this.el.remove();
7548                 if(this.actionMode == "container"){
7549                     this.container.remove();
7550                 }
7551             }
7552             this.onDestroy();
7553             Roo.ComponentMgr.unregister(this);
7554             this.fireEvent("destroy", this);
7555         }
7556     },
7557
7558         /** @private */
7559     beforeDestroy : function(){
7560
7561     },
7562
7563         /** @private */
7564         onDestroy : function(){
7565
7566     },
7567
7568     /**
7569      * Returns the underlying {@link Roo.Element}.
7570      * @return {Roo.Element} The element
7571      */
7572     getEl : function(){
7573         return this.el;
7574     },
7575
7576     /**
7577      * Returns the id of this component.
7578      * @return {String}
7579      */
7580     getId : function(){
7581         return this.id;
7582     },
7583
7584     /**
7585      * Try to focus this component.
7586      * @param {Boolean} selectText True to also select the text in this component (if applicable)
7587      * @return {Roo.Component} this
7588      */
7589     focus : function(selectText){
7590         if(this.rendered){
7591             this.el.focus();
7592             if(selectText === true){
7593                 this.el.dom.select();
7594             }
7595         }
7596         return this;
7597     },
7598
7599     /** @private */
7600     blur : function(){
7601         if(this.rendered){
7602             this.el.blur();
7603         }
7604         return this;
7605     },
7606
7607     /**
7608      * Disable this component.
7609      * @return {Roo.Component} this
7610      */
7611     disable : function(){
7612         if(this.rendered){
7613             this.onDisable();
7614         }
7615         this.disabled = true;
7616         this.fireEvent("disable", this);
7617         return this;
7618     },
7619
7620         // private
7621     onDisable : function(){
7622         this.getActionEl().addClass(this.disabledClass);
7623         this.el.dom.disabled = true;
7624     },
7625
7626     /**
7627      * Enable this component.
7628      * @return {Roo.Component} this
7629      */
7630     enable : function(){
7631         if(this.rendered){
7632             this.onEnable();
7633         }
7634         this.disabled = false;
7635         this.fireEvent("enable", this);
7636         return this;
7637     },
7638
7639         // private
7640     onEnable : function(){
7641         this.getActionEl().removeClass(this.disabledClass);
7642         this.el.dom.disabled = false;
7643     },
7644
7645     /**
7646      * Convenience function for setting disabled/enabled by boolean.
7647      * @param {Boolean} disabled
7648      */
7649     setDisabled : function(disabled){
7650         this[disabled ? "disable" : "enable"]();
7651     },
7652
7653     /**
7654      * Show this component.
7655      * @return {Roo.Component} this
7656      */
7657     show: function(){
7658         if(this.fireEvent("beforeshow", this) !== false){
7659             this.hidden = false;
7660             if(this.rendered){
7661                 this.onShow();
7662             }
7663             this.fireEvent("show", this);
7664         }
7665         return this;
7666     },
7667
7668     // private
7669     onShow : function(){
7670         var ae = this.getActionEl();
7671         if(this.hideMode == 'visibility'){
7672             ae.dom.style.visibility = "visible";
7673         }else if(this.hideMode == 'offsets'){
7674             ae.removeClass('x-hidden');
7675         }else{
7676             ae.dom.style.display = "";
7677         }
7678     },
7679
7680     /**
7681      * Hide this component.
7682      * @return {Roo.Component} this
7683      */
7684     hide: function(){
7685         if(this.fireEvent("beforehide", this) !== false){
7686             this.hidden = true;
7687             if(this.rendered){
7688                 this.onHide();
7689             }
7690             this.fireEvent("hide", this);
7691         }
7692         return this;
7693     },
7694
7695     // private
7696     onHide : function(){
7697         var ae = this.getActionEl();
7698         if(this.hideMode == 'visibility'){
7699             ae.dom.style.visibility = "hidden";
7700         }else if(this.hideMode == 'offsets'){
7701             ae.addClass('x-hidden');
7702         }else{
7703             ae.dom.style.display = "none";
7704         }
7705     },
7706
7707     /**
7708      * Convenience function to hide or show this component by boolean.
7709      * @param {Boolean} visible True to show, false to hide
7710      * @return {Roo.Component} this
7711      */
7712     setVisible: function(visible){
7713         if(visible) {
7714             this.show();
7715         }else{
7716             this.hide();
7717         }
7718         return this;
7719     },
7720
7721     /**
7722      * Returns true if this component is visible.
7723      */
7724     isVisible : function(){
7725         return this.getActionEl().isVisible();
7726     },
7727
7728     cloneConfig : function(overrides){
7729         overrides = overrides || {};
7730         var id = overrides.id || Roo.id();
7731         var cfg = Roo.applyIf(overrides, this.initialConfig);
7732         cfg.id = id; // prevent dup id
7733         return new this.constructor(cfg);
7734     }
7735 });/*
7736  * Based on:
7737  * Ext JS Library 1.1.1
7738  * Copyright(c) 2006-2007, Ext JS, LLC.
7739  *
7740  * Originally Released Under LGPL - original licence link has changed is not relivant.
7741  *
7742  * Fork - LGPL
7743  * <script type="text/javascript">
7744  */
7745  (function(){ 
7746 /**
7747  * @class Roo.Layer
7748  * @extends Roo.Element
7749  * An extended {@link Roo.Element} object that supports a shadow and shim, constrain to viewport and
7750  * automatic maintaining of shadow/shim positions.
7751  * @cfg {Boolean} shim False to disable the iframe shim in browsers which need one (defaults to true)
7752  * @cfg {String/Boolean} shadow True to create a shadow element with default class "x-layer-shadow", or
7753  * you can pass a string with a CSS class name. False turns off the shadow.
7754  * @cfg {Object} dh DomHelper object config to create element with (defaults to {tag: "div", cls: "x-layer"}).
7755  * @cfg {Boolean} constrain False to disable constrain to viewport (defaults to true)
7756  * @cfg {String} cls CSS class to add to the element
7757  * @cfg {Number} zindex Starting z-index (defaults to 11000)
7758  * @cfg {Number} shadowOffset Number of pixels to offset the shadow (defaults to 3)
7759  * @constructor
7760  * @param {Object} config An object with config options.
7761  * @param {String/HTMLElement} existingEl (optional) Uses an existing DOM element. If the element is not found it creates it.
7762  */
7763
7764 Roo.Layer = function(config, existingEl){
7765     config = config || {};
7766     var dh = Roo.DomHelper;
7767     var cp = config.parentEl, pel = cp ? Roo.getDom(cp) : document.body;
7768     if(existingEl){
7769         this.dom = Roo.getDom(existingEl);
7770     }
7771     if(!this.dom){
7772         var o = config.dh || {tag: "div", cls: "x-layer"};
7773         this.dom = dh.append(pel, o);
7774     }
7775     if(config.cls){
7776         this.addClass(config.cls);
7777     }
7778     this.constrain = config.constrain !== false;
7779     this.visibilityMode = Roo.Element.VISIBILITY;
7780     if(config.id){
7781         this.id = this.dom.id = config.id;
7782     }else{
7783         this.id = Roo.id(this.dom);
7784     }
7785     this.zindex = config.zindex || this.getZIndex();
7786     this.position("absolute", this.zindex);
7787     if(config.shadow){
7788         this.shadowOffset = config.shadowOffset || 4;
7789         this.shadow = new Roo.Shadow({
7790             offset : this.shadowOffset,
7791             mode : config.shadow
7792         });
7793     }else{
7794         this.shadowOffset = 0;
7795     }
7796     this.useShim = config.shim !== false && Roo.useShims;
7797     this.useDisplay = config.useDisplay;
7798     this.hide();
7799 };
7800
7801 var supr = Roo.Element.prototype;
7802
7803 // shims are shared among layer to keep from having 100 iframes
7804 var shims = [];
7805
7806 Roo.extend(Roo.Layer, Roo.Element, {
7807
7808     getZIndex : function(){
7809         return this.zindex || parseInt(this.getStyle("z-index"), 10) || 11000;
7810     },
7811
7812     getShim : function(){
7813         if(!this.useShim){
7814             return null;
7815         }
7816         if(this.shim){
7817             return this.shim;
7818         }
7819         var shim = shims.shift();
7820         if(!shim){
7821             shim = this.createShim();
7822             shim.enableDisplayMode('block');
7823             shim.dom.style.display = 'none';
7824             shim.dom.style.visibility = 'visible';
7825         }
7826         var pn = this.dom.parentNode;
7827         if(shim.dom.parentNode != pn){
7828             pn.insertBefore(shim.dom, this.dom);
7829         }
7830         shim.setStyle('z-index', this.getZIndex()-2);
7831         this.shim = shim;
7832         return shim;
7833     },
7834
7835     hideShim : function(){
7836         if(this.shim){
7837             this.shim.setDisplayed(false);
7838             shims.push(this.shim);
7839             delete this.shim;
7840         }
7841     },
7842
7843     disableShadow : function(){
7844         if(this.shadow){
7845             this.shadowDisabled = true;
7846             this.shadow.hide();
7847             this.lastShadowOffset = this.shadowOffset;
7848             this.shadowOffset = 0;
7849         }
7850     },
7851
7852     enableShadow : function(show){
7853         if(this.shadow){
7854             this.shadowDisabled = false;
7855             this.shadowOffset = this.lastShadowOffset;
7856             delete this.lastShadowOffset;
7857             if(show){
7858                 this.sync(true);
7859             }
7860         }
7861     },
7862
7863     // private
7864     // this code can execute repeatedly in milliseconds (i.e. during a drag) so
7865     // code size was sacrificed for effeciency (e.g. no getBox/setBox, no XY calls)
7866     sync : function(doShow){
7867         var sw = this.shadow;
7868         if(!this.updating && this.isVisible() && (sw || this.useShim)){
7869             var sh = this.getShim();
7870
7871             var w = this.getWidth(),
7872                 h = this.getHeight();
7873
7874             var l = this.getLeft(true),
7875                 t = this.getTop(true);
7876
7877             if(sw && !this.shadowDisabled){
7878                 if(doShow && !sw.isVisible()){
7879                     sw.show(this);
7880                 }else{
7881                     sw.realign(l, t, w, h);
7882                 }
7883                 if(sh){
7884                     if(doShow){
7885                        sh.show();
7886                     }
7887                     // fit the shim behind the shadow, so it is shimmed too
7888                     var a = sw.adjusts, s = sh.dom.style;
7889                     s.left = (Math.min(l, l+a.l))+"px";
7890                     s.top = (Math.min(t, t+a.t))+"px";
7891                     s.width = (w+a.w)+"px";
7892                     s.height = (h+a.h)+"px";
7893                 }
7894             }else if(sh){
7895                 if(doShow){
7896                    sh.show();
7897                 }
7898                 sh.setSize(w, h);
7899                 sh.setLeftTop(l, t);
7900             }
7901             
7902         }
7903     },
7904
7905     // private
7906     destroy : function(){
7907         this.hideShim();
7908         if(this.shadow){
7909             this.shadow.hide();
7910         }
7911         this.removeAllListeners();
7912         var pn = this.dom.parentNode;
7913         if(pn){
7914             pn.removeChild(this.dom);
7915         }
7916         Roo.Element.uncache(this.id);
7917     },
7918
7919     remove : function(){
7920         this.destroy();
7921     },
7922
7923     // private
7924     beginUpdate : function(){
7925         this.updating = true;
7926     },
7927
7928     // private
7929     endUpdate : function(){
7930         this.updating = false;
7931         this.sync(true);
7932     },
7933
7934     // private
7935     hideUnders : function(negOffset){
7936         if(this.shadow){
7937             this.shadow.hide();
7938         }
7939         this.hideShim();
7940     },
7941
7942     // private
7943     constrainXY : function(){
7944         if(this.constrain){
7945             var vw = Roo.lib.Dom.getViewWidth(),
7946                 vh = Roo.lib.Dom.getViewHeight();
7947             var s = Roo.get(document).getScroll();
7948
7949             var xy = this.getXY();
7950             var x = xy[0], y = xy[1];   
7951             var w = this.dom.offsetWidth+this.shadowOffset, h = this.dom.offsetHeight+this.shadowOffset;
7952             // only move it if it needs it
7953             var moved = false;
7954             // first validate right/bottom
7955             if((x + w) > vw+s.left){
7956                 x = vw - w - this.shadowOffset;
7957                 moved = true;
7958             }
7959             if((y + h) > vh+s.top){
7960                 y = vh - h - this.shadowOffset;
7961                 moved = true;
7962             }
7963             // then make sure top/left isn't negative
7964             if(x < s.left){
7965                 x = s.left;
7966                 moved = true;
7967             }
7968             if(y < s.top){
7969                 y = s.top;
7970                 moved = true;
7971             }
7972             if(moved){
7973                 if(this.avoidY){
7974                     var ay = this.avoidY;
7975                     if(y <= ay && (y+h) >= ay){
7976                         y = ay-h-5;   
7977                     }
7978                 }
7979                 xy = [x, y];
7980                 this.storeXY(xy);
7981                 supr.setXY.call(this, xy);
7982                 this.sync();
7983             }
7984         }
7985     },
7986
7987     isVisible : function(){
7988         return this.visible;    
7989     },
7990
7991     // private
7992     showAction : function(){
7993         this.visible = true; // track visibility to prevent getStyle calls
7994         if(this.useDisplay === true){
7995             this.setDisplayed("");
7996         }else if(this.lastXY){
7997             supr.setXY.call(this, this.lastXY);
7998         }else if(this.lastLT){
7999             supr.setLeftTop.call(this, this.lastLT[0], this.lastLT[1]);
8000         }
8001     },
8002
8003     // private
8004     hideAction : function(){
8005         this.visible = false;
8006         if(this.useDisplay === true){
8007             this.setDisplayed(false);
8008         }else{
8009             this.setLeftTop(-10000,-10000);
8010         }
8011     },
8012
8013     // overridden Element method
8014     setVisible : function(v, a, d, c, e){
8015         if(v){
8016             this.showAction();
8017         }
8018         if(a && v){
8019             var cb = function(){
8020                 this.sync(true);
8021                 if(c){
8022                     c();
8023                 }
8024             }.createDelegate(this);
8025             supr.setVisible.call(this, true, true, d, cb, e);
8026         }else{
8027             if(!v){
8028                 this.hideUnders(true);
8029             }
8030             var cb = c;
8031             if(a){
8032                 cb = function(){
8033                     this.hideAction();
8034                     if(c){
8035                         c();
8036                     }
8037                 }.createDelegate(this);
8038             }
8039             supr.setVisible.call(this, v, a, d, cb, e);
8040             if(v){
8041                 this.sync(true);
8042             }else if(!a){
8043                 this.hideAction();
8044             }
8045         }
8046     },
8047
8048     storeXY : function(xy){
8049         delete this.lastLT;
8050         this.lastXY = xy;
8051     },
8052
8053     storeLeftTop : function(left, top){
8054         delete this.lastXY;
8055         this.lastLT = [left, top];
8056     },
8057
8058     // private
8059     beforeFx : function(){
8060         this.beforeAction();
8061         return Roo.Layer.superclass.beforeFx.apply(this, arguments);
8062     },
8063
8064     // private
8065     afterFx : function(){
8066         Roo.Layer.superclass.afterFx.apply(this, arguments);
8067         this.sync(this.isVisible());
8068     },
8069
8070     // private
8071     beforeAction : function(){
8072         if(!this.updating && this.shadow){
8073             this.shadow.hide();
8074         }
8075     },
8076
8077     // overridden Element method
8078     setLeft : function(left){
8079         this.storeLeftTop(left, this.getTop(true));
8080         supr.setLeft.apply(this, arguments);
8081         this.sync();
8082     },
8083
8084     setTop : function(top){
8085         this.storeLeftTop(this.getLeft(true), top);
8086         supr.setTop.apply(this, arguments);
8087         this.sync();
8088     },
8089
8090     setLeftTop : function(left, top){
8091         this.storeLeftTop(left, top);
8092         supr.setLeftTop.apply(this, arguments);
8093         this.sync();
8094     },
8095
8096     setXY : function(xy, a, d, c, e){
8097         this.fixDisplay();
8098         this.beforeAction();
8099         this.storeXY(xy);
8100         var cb = this.createCB(c);
8101         supr.setXY.call(this, xy, a, d, cb, e);
8102         if(!a){
8103             cb();
8104         }
8105     },
8106
8107     // private
8108     createCB : function(c){
8109         var el = this;
8110         return function(){
8111             el.constrainXY();
8112             el.sync(true);
8113             if(c){
8114                 c();
8115             }
8116         };
8117     },
8118
8119     // overridden Element method
8120     setX : function(x, a, d, c, e){
8121         this.setXY([x, this.getY()], a, d, c, e);
8122     },
8123
8124     // overridden Element method
8125     setY : function(y, a, d, c, e){
8126         this.setXY([this.getX(), y], a, d, c, e);
8127     },
8128
8129     // overridden Element method
8130     setSize : function(w, h, a, d, c, e){
8131         this.beforeAction();
8132         var cb = this.createCB(c);
8133         supr.setSize.call(this, w, h, a, d, cb, e);
8134         if(!a){
8135             cb();
8136         }
8137     },
8138
8139     // overridden Element method
8140     setWidth : function(w, a, d, c, e){
8141         this.beforeAction();
8142         var cb = this.createCB(c);
8143         supr.setWidth.call(this, w, a, d, cb, e);
8144         if(!a){
8145             cb();
8146         }
8147     },
8148
8149     // overridden Element method
8150     setHeight : function(h, a, d, c, e){
8151         this.beforeAction();
8152         var cb = this.createCB(c);
8153         supr.setHeight.call(this, h, a, d, cb, e);
8154         if(!a){
8155             cb();
8156         }
8157     },
8158
8159     // overridden Element method
8160     setBounds : function(x, y, w, h, a, d, c, e){
8161         this.beforeAction();
8162         var cb = this.createCB(c);
8163         if(!a){
8164             this.storeXY([x, y]);
8165             supr.setXY.call(this, [x, y]);
8166             supr.setSize.call(this, w, h, a, d, cb, e);
8167             cb();
8168         }else{
8169             supr.setBounds.call(this, x, y, w, h, a, d, cb, e);
8170         }
8171         return this;
8172     },
8173     
8174     /**
8175      * Sets the z-index of this layer and adjusts any shadow and shim z-indexes. The layer z-index is automatically
8176      * incremented by two more than the value passed in so that it always shows above any shadow or shim (the shadow
8177      * element, if any, will be assigned z-index + 1, and the shim element, if any, will be assigned the unmodified z-index).
8178      * @param {Number} zindex The new z-index to set
8179      * @return {this} The Layer
8180      */
8181     setZIndex : function(zindex){
8182         this.zindex = zindex;
8183         this.setStyle("z-index", zindex + 2);
8184         if(this.shadow){
8185             this.shadow.setZIndex(zindex + 1);
8186         }
8187         if(this.shim){
8188             this.shim.setStyle("z-index", zindex);
8189         }
8190     }
8191 });
8192 })();/*
8193  * Based on:
8194  * Ext JS Library 1.1.1
8195  * Copyright(c) 2006-2007, Ext JS, LLC.
8196  *
8197  * Originally Released Under LGPL - original licence link has changed is not relivant.
8198  *
8199  * Fork - LGPL
8200  * <script type="text/javascript">
8201  */
8202
8203
8204 /**
8205  * @class Roo.Shadow
8206  * Simple class that can provide a shadow effect for any element.  Note that the element MUST be absolutely positioned,
8207  * and the shadow does not provide any shimming.  This should be used only in simple cases -- for more advanced
8208  * functionality that can also provide the same shadow effect, see the {@link Roo.Layer} class.
8209  * @constructor
8210  * Create a new Shadow
8211  * @param {Object} config The config object
8212  */
8213 Roo.Shadow = function(config){
8214     Roo.apply(this, config);
8215     if(typeof this.mode != "string"){
8216         this.mode = this.defaultMode;
8217     }
8218     var o = this.offset, a = {h: 0};
8219     var rad = Math.floor(this.offset/2);
8220     switch(this.mode.toLowerCase()){ // all this hideous nonsense calculates the various offsets for shadows
8221         case "drop":
8222             a.w = 0;
8223             a.l = a.t = o;
8224             a.t -= 1;
8225             if(Roo.isIE){
8226                 a.l -= this.offset + rad;
8227                 a.t -= this.offset + rad;
8228                 a.w -= rad;
8229                 a.h -= rad;
8230                 a.t += 1;
8231             }
8232         break;
8233         case "sides":
8234             a.w = (o*2);
8235             a.l = -o;
8236             a.t = o-1;
8237             if(Roo.isIE){
8238                 a.l -= (this.offset - rad);
8239                 a.t -= this.offset + rad;
8240                 a.l += 1;
8241                 a.w -= (this.offset - rad)*2;
8242                 a.w -= rad + 1;
8243                 a.h -= 1;
8244             }
8245         break;
8246         case "frame":
8247             a.w = a.h = (o*2);
8248             a.l = a.t = -o;
8249             a.t += 1;
8250             a.h -= 2;
8251             if(Roo.isIE){
8252                 a.l -= (this.offset - rad);
8253                 a.t -= (this.offset - rad);
8254                 a.l += 1;
8255                 a.w -= (this.offset + rad + 1);
8256                 a.h -= (this.offset + rad);
8257                 a.h += 1;
8258             }
8259         break;
8260     };
8261
8262     this.adjusts = a;
8263 };
8264
8265 Roo.Shadow.prototype = {
8266     /**
8267      * @cfg {String} mode
8268      * The shadow display mode.  Supports the following options:<br />
8269      * sides: Shadow displays on both sides and bottom only<br />
8270      * frame: Shadow displays equally on all four sides<br />
8271      * drop: Traditional bottom-right drop shadow (default)
8272      */
8273     /**
8274      * @cfg {String} offset
8275      * The number of pixels to offset the shadow from the element (defaults to 4)
8276      */
8277     offset: 4,
8278
8279     // private
8280     defaultMode: "drop",
8281
8282     /**
8283      * Displays the shadow under the target element
8284      * @param {String/HTMLElement/Element} targetEl The id or element under which the shadow should display
8285      */
8286     show : function(target){
8287         target = Roo.get(target);
8288         if(!this.el){
8289             this.el = Roo.Shadow.Pool.pull();
8290             if(this.el.dom.nextSibling != target.dom){
8291                 this.el.insertBefore(target);
8292             }
8293         }
8294         this.el.setStyle("z-index", this.zIndex || parseInt(target.getStyle("z-index"), 10)-1);
8295         if(Roo.isIE){
8296             this.el.dom.style.filter="progid:DXImageTransform.Microsoft.alpha(opacity=50) progid:DXImageTransform.Microsoft.Blur(pixelradius="+(this.offset)+")";
8297         }
8298         this.realign(
8299             target.getLeft(true),
8300             target.getTop(true),
8301             target.getWidth(),
8302             target.getHeight()
8303         );
8304         this.el.dom.style.display = "block";
8305     },
8306
8307     /**
8308      * Returns true if the shadow is visible, else false
8309      */
8310     isVisible : function(){
8311         return this.el ? true : false;  
8312     },
8313
8314     /**
8315      * Direct alignment when values are already available. Show must be called at least once before
8316      * calling this method to ensure it is initialized.
8317      * @param {Number} left The target element left position
8318      * @param {Number} top The target element top position
8319      * @param {Number} width The target element width
8320      * @param {Number} height The target element height
8321      */
8322     realign : function(l, t, w, h){
8323         if(!this.el){
8324             return;
8325         }
8326         var a = this.adjusts, d = this.el.dom, s = d.style;
8327         var iea = 0;
8328         s.left = (l+a.l)+"px";
8329         s.top = (t+a.t)+"px";
8330         var sw = (w+a.w), sh = (h+a.h), sws = sw +"px", shs = sh + "px";
8331  
8332         if(s.width != sws || s.height != shs){
8333             s.width = sws;
8334             s.height = shs;
8335             if(!Roo.isIE){
8336                 var cn = d.childNodes;
8337                 var sww = Math.max(0, (sw-12))+"px";
8338                 cn[0].childNodes[1].style.width = sww;
8339                 cn[1].childNodes[1].style.width = sww;
8340                 cn[2].childNodes[1].style.width = sww;
8341                 cn[1].style.height = Math.max(0, (sh-12))+"px";
8342             }
8343         }
8344     },
8345
8346     /**
8347      * Hides this shadow
8348      */
8349     hide : function(){
8350         if(this.el){
8351             this.el.dom.style.display = "none";
8352             Roo.Shadow.Pool.push(this.el);
8353             delete this.el;
8354         }
8355     },
8356
8357     /**
8358      * Adjust the z-index of this shadow
8359      * @param {Number} zindex The new z-index
8360      */
8361     setZIndex : function(z){
8362         this.zIndex = z;
8363         if(this.el){
8364             this.el.setStyle("z-index", z);
8365         }
8366     }
8367 };
8368
8369 // Private utility class that manages the internal Shadow cache
8370 Roo.Shadow.Pool = function(){
8371     var p = [];
8372     var markup = Roo.isIE ?
8373                  '<div class="x-ie-shadow"></div>' :
8374                  '<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>';
8375     return {
8376         pull : function(){
8377             var sh = p.shift();
8378             if(!sh){
8379                 sh = Roo.get(Roo.DomHelper.insertHtml("beforeBegin", document.body.firstChild, markup));
8380                 sh.autoBoxAdjust = false;
8381             }
8382             return sh;
8383         },
8384
8385         push : function(sh){
8386             p.push(sh);
8387         }
8388     };
8389 }();/*
8390  * Based on:
8391  * Ext JS Library 1.1.1
8392  * Copyright(c) 2006-2007, Ext JS, LLC.
8393  *
8394  * Originally Released Under LGPL - original licence link has changed is not relivant.
8395  *
8396  * Fork - LGPL
8397  * <script type="text/javascript">
8398  */
8399
8400 /**
8401  * @class Roo.BoxComponent
8402  * @extends Roo.Component
8403  * Base class for any visual {@link Roo.Component} that uses a box container.  BoxComponent provides automatic box
8404  * model adjustments for sizing and positioning and will work correctly withnin the Component rendering model.  All
8405  * container classes should subclass BoxComponent so that they will work consistently when nested within other Ext
8406  * layout containers.
8407  * @constructor
8408  * @param {Roo.Element/String/Object} config The configuration options.
8409  */
8410 Roo.BoxComponent = function(config){
8411     Roo.Component.call(this, config);
8412     this.addEvents({
8413         /**
8414          * @event resize
8415          * Fires after the component is resized.
8416              * @param {Roo.Component} this
8417              * @param {Number} adjWidth The box-adjusted width that was set
8418              * @param {Number} adjHeight The box-adjusted height that was set
8419              * @param {Number} rawWidth The width that was originally specified
8420              * @param {Number} rawHeight The height that was originally specified
8421              */
8422         resize : true,
8423         /**
8424          * @event move
8425          * Fires after the component is moved.
8426              * @param {Roo.Component} this
8427              * @param {Number} x The new x position
8428              * @param {Number} y The new y position
8429              */
8430         move : true
8431     });
8432 };
8433
8434 Roo.extend(Roo.BoxComponent, Roo.Component, {
8435     // private, set in afterRender to signify that the component has been rendered
8436     boxReady : false,
8437     // private, used to defer height settings to subclasses
8438     deferHeight: false,
8439     /** @cfg {Number} width
8440      * width (optional) size of component
8441      */
8442      /** @cfg {Number} height
8443      * height (optional) size of component
8444      */
8445      
8446     /**
8447      * Sets the width and height of the component.  This method fires the resize event.  This method can accept
8448      * either width and height as separate numeric arguments, or you can pass a size object like {width:10, height:20}.
8449      * @param {Number/Object} width The new width to set, or a size object in the format {width, height}
8450      * @param {Number} height The new height to set (not required if a size object is passed as the first arg)
8451      * @return {Roo.BoxComponent} this
8452      */
8453     setSize : function(w, h){
8454         // support for standard size objects
8455         if(typeof w == 'object'){
8456             h = w.height;
8457             w = w.width;
8458         }
8459         // not rendered
8460         if(!this.boxReady){
8461             this.width = w;
8462             this.height = h;
8463             return this;
8464         }
8465
8466         // prevent recalcs when not needed
8467         if(this.lastSize && this.lastSize.width == w && this.lastSize.height == h){
8468             return this;
8469         }
8470         this.lastSize = {width: w, height: h};
8471
8472         var adj = this.adjustSize(w, h);
8473         var aw = adj.width, ah = adj.height;
8474         if(aw !== undefined || ah !== undefined){ // this code is nasty but performs better with floaters
8475             var rz = this.getResizeEl();
8476             if(!this.deferHeight && aw !== undefined && ah !== undefined){
8477                 rz.setSize(aw, ah);
8478             }else if(!this.deferHeight && ah !== undefined){
8479                 rz.setHeight(ah);
8480             }else if(aw !== undefined){
8481                 rz.setWidth(aw);
8482             }
8483             this.onResize(aw, ah, w, h);
8484             this.fireEvent('resize', this, aw, ah, w, h);
8485         }
8486         return this;
8487     },
8488
8489     /**
8490      * Gets the current size of the component's underlying element.
8491      * @return {Object} An object containing the element's size {width: (element width), height: (element height)}
8492      */
8493     getSize : function(){
8494         return this.el.getSize();
8495     },
8496
8497     /**
8498      * Gets the current XY position of the component's underlying element.
8499      * @param {Boolean} local (optional) If true the element's left and top are returned instead of page XY (defaults to false)
8500      * @return {Array} The XY position of the element (e.g., [100, 200])
8501      */
8502     getPosition : function(local){
8503         if(local === true){
8504             return [this.el.getLeft(true), this.el.getTop(true)];
8505         }
8506         return this.xy || this.el.getXY();
8507     },
8508
8509     /**
8510      * Gets the current box measurements of the component's underlying element.
8511      * @param {Boolean} local (optional) If true the element's left and top are returned instead of page XY (defaults to false)
8512      * @returns {Object} box An object in the format {x, y, width, height}
8513      */
8514     getBox : function(local){
8515         var s = this.el.getSize();
8516         if(local){
8517             s.x = this.el.getLeft(true);
8518             s.y = this.el.getTop(true);
8519         }else{
8520             var xy = this.xy || this.el.getXY();
8521             s.x = xy[0];
8522             s.y = xy[1];
8523         }
8524         return s;
8525     },
8526
8527     /**
8528      * Sets the current box measurements of the component's underlying element.
8529      * @param {Object} box An object in the format {x, y, width, height}
8530      * @returns {Roo.BoxComponent} this
8531      */
8532     updateBox : function(box){
8533         this.setSize(box.width, box.height);
8534         this.setPagePosition(box.x, box.y);
8535         return this;
8536     },
8537
8538     // protected
8539     getResizeEl : function(){
8540         return this.resizeEl || this.el;
8541     },
8542
8543     // protected
8544     getPositionEl : function(){
8545         return this.positionEl || this.el;
8546     },
8547
8548     /**
8549      * Sets the left and top of the component.  To set the page XY position instead, use {@link #setPagePosition}.
8550      * This method fires the move event.
8551      * @param {Number} left The new left
8552      * @param {Number} top The new top
8553      * @returns {Roo.BoxComponent} this
8554      */
8555     setPosition : function(x, y){
8556         this.x = x;
8557         this.y = y;
8558         if(!this.boxReady){
8559             return this;
8560         }
8561         var adj = this.adjustPosition(x, y);
8562         var ax = adj.x, ay = adj.y;
8563
8564         var el = this.getPositionEl();
8565         if(ax !== undefined || ay !== undefined){
8566             if(ax !== undefined && ay !== undefined){
8567                 el.setLeftTop(ax, ay);
8568             }else if(ax !== undefined){
8569                 el.setLeft(ax);
8570             }else if(ay !== undefined){
8571                 el.setTop(ay);
8572             }
8573             this.onPosition(ax, ay);
8574             this.fireEvent('move', this, ax, ay);
8575         }
8576         return this;
8577     },
8578
8579     /**
8580      * Sets the page XY position of the component.  To set the left and top instead, use {@link #setPosition}.
8581      * This method fires the move event.
8582      * @param {Number} x The new x position
8583      * @param {Number} y The new y position
8584      * @returns {Roo.BoxComponent} this
8585      */
8586     setPagePosition : function(x, y){
8587         this.pageX = x;
8588         this.pageY = y;
8589         if(!this.boxReady){
8590             return;
8591         }
8592         if(x === undefined || y === undefined){ // cannot translate undefined points
8593             return;
8594         }
8595         var p = this.el.translatePoints(x, y);
8596         this.setPosition(p.left, p.top);
8597         return this;
8598     },
8599
8600     // private
8601     onRender : function(ct, position){
8602         Roo.BoxComponent.superclass.onRender.call(this, ct, position);
8603         if(this.resizeEl){
8604             this.resizeEl = Roo.get(this.resizeEl);
8605         }
8606         if(this.positionEl){
8607             this.positionEl = Roo.get(this.positionEl);
8608         }
8609     },
8610
8611     // private
8612     afterRender : function(){
8613         Roo.BoxComponent.superclass.afterRender.call(this);
8614         this.boxReady = true;
8615         this.setSize(this.width, this.height);
8616         if(this.x || this.y){
8617             this.setPosition(this.x, this.y);
8618         }
8619         if(this.pageX || this.pageY){
8620             this.setPagePosition(this.pageX, this.pageY);
8621         }
8622     },
8623
8624     /**
8625      * Force the component's size to recalculate based on the underlying element's current height and width.
8626      * @returns {Roo.BoxComponent} this
8627      */
8628     syncSize : function(){
8629         delete this.lastSize;
8630         this.setSize(this.el.getWidth(), this.el.getHeight());
8631         return this;
8632     },
8633
8634     /**
8635      * Called after the component is resized, this method is empty by default but can be implemented by any
8636      * subclass that needs to perform custom logic after a resize occurs.
8637      * @param {Number} adjWidth The box-adjusted width that was set
8638      * @param {Number} adjHeight The box-adjusted height that was set
8639      * @param {Number} rawWidth The width that was originally specified
8640      * @param {Number} rawHeight The height that was originally specified
8641      */
8642     onResize : function(adjWidth, adjHeight, rawWidth, rawHeight){
8643
8644     },
8645
8646     /**
8647      * Called after the component is moved, this method is empty by default but can be implemented by any
8648      * subclass that needs to perform custom logic after a move occurs.
8649      * @param {Number} x The new x position
8650      * @param {Number} y The new y position
8651      */
8652     onPosition : function(x, y){
8653
8654     },
8655
8656     // private
8657     adjustSize : function(w, h){
8658         if(this.autoWidth){
8659             w = 'auto';
8660         }
8661         if(this.autoHeight){
8662             h = 'auto';
8663         }
8664         return {width : w, height: h};
8665     },
8666
8667     // private
8668     adjustPosition : function(x, y){
8669         return {x : x, y: y};
8670     }
8671 });/*
8672  * Based on:
8673  * Ext JS Library 1.1.1
8674  * Copyright(c) 2006-2007, Ext JS, LLC.
8675  *
8676  * Originally Released Under LGPL - original licence link has changed is not relivant.
8677  *
8678  * Fork - LGPL
8679  * <script type="text/javascript">
8680  */
8681
8682
8683 /**
8684  * @class Roo.SplitBar
8685  * @extends Roo.util.Observable
8686  * Creates draggable splitter bar functionality from two elements (element to be dragged and element to be resized).
8687  * <br><br>
8688  * Usage:
8689  * <pre><code>
8690 var split = new Roo.SplitBar("elementToDrag", "elementToSize",
8691                    Roo.SplitBar.HORIZONTAL, Roo.SplitBar.LEFT);
8692 split.setAdapter(new Roo.SplitBar.AbsoluteLayoutAdapter("container"));
8693 split.minSize = 100;
8694 split.maxSize = 600;
8695 split.animate = true;
8696 split.on('moved', splitterMoved);
8697 </code></pre>
8698  * @constructor
8699  * Create a new SplitBar
8700  * @param {String/HTMLElement/Roo.Element} dragElement The element to be dragged and act as the SplitBar. 
8701  * @param {String/HTMLElement/Roo.Element} resizingElement The element to be resized based on where the SplitBar element is dragged 
8702  * @param {Number} orientation (optional) Either Roo.SplitBar.HORIZONTAL or Roo.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
8703  * @param {Number} placement (optional) Either Roo.SplitBar.LEFT or Roo.SplitBar.RIGHT for horizontal or  
8704                         Roo.SplitBar.TOP or Roo.SplitBar.BOTTOM for vertical. (By default, this is determined automatically by the initial
8705                         position of the SplitBar).
8706  */
8707 Roo.SplitBar = function(dragElement, resizingElement, orientation, placement, existingProxy){
8708     
8709     /** @private */
8710     this.el = Roo.get(dragElement, true);
8711     this.el.dom.unselectable = "on";
8712     /** @private */
8713     this.resizingEl = Roo.get(resizingElement, true);
8714
8715     /**
8716      * @private
8717      * The orientation of the split. Either Roo.SplitBar.HORIZONTAL or Roo.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
8718      * Note: If this is changed after creating the SplitBar, the placement property must be manually updated
8719      * @type Number
8720      */
8721     this.orientation = orientation || Roo.SplitBar.HORIZONTAL;
8722     
8723     /**
8724      * The minimum size of the resizing element. (Defaults to 0)
8725      * @type Number
8726      */
8727     this.minSize = 0;
8728     
8729     /**
8730      * The maximum size of the resizing element. (Defaults to 2000)
8731      * @type Number
8732      */
8733     this.maxSize = 2000;
8734     
8735     /**
8736      * Whether to animate the transition to the new size
8737      * @type Boolean
8738      */
8739     this.animate = false;
8740     
8741     /**
8742      * Whether to create a transparent shim that overlays the page when dragging, enables dragging across iframes.
8743      * @type Boolean
8744      */
8745     this.useShim = false;
8746     
8747     /** @private */
8748     this.shim = null;
8749     
8750     if(!existingProxy){
8751         /** @private */
8752         this.proxy = Roo.SplitBar.createProxy(this.orientation);
8753     }else{
8754         this.proxy = Roo.get(existingProxy).dom;
8755     }
8756     /** @private */
8757     this.dd = new Roo.dd.DDProxy(this.el.dom.id, "XSplitBars", {dragElId : this.proxy.id});
8758     
8759     /** @private */
8760     this.dd.b4StartDrag = this.onStartProxyDrag.createDelegate(this);
8761     
8762     /** @private */
8763     this.dd.endDrag = this.onEndProxyDrag.createDelegate(this);
8764     
8765     /** @private */
8766     this.dragSpecs = {};
8767     
8768     /**
8769      * @private The adapter to use to positon and resize elements
8770      */
8771     this.adapter = new Roo.SplitBar.BasicLayoutAdapter();
8772     this.adapter.init(this);
8773     
8774     if(this.orientation == Roo.SplitBar.HORIZONTAL){
8775         /** @private */
8776         this.placement = placement || (this.el.getX() > this.resizingEl.getX() ? Roo.SplitBar.LEFT : Roo.SplitBar.RIGHT);
8777         this.el.addClass("x-splitbar-h");
8778     }else{
8779         /** @private */
8780         this.placement = placement || (this.el.getY() > this.resizingEl.getY() ? Roo.SplitBar.TOP : Roo.SplitBar.BOTTOM);
8781         this.el.addClass("x-splitbar-v");
8782     }
8783     
8784     this.addEvents({
8785         /**
8786          * @event resize
8787          * Fires when the splitter is moved (alias for {@link #event-moved})
8788          * @param {Roo.SplitBar} this
8789          * @param {Number} newSize the new width or height
8790          */
8791         "resize" : true,
8792         /**
8793          * @event moved
8794          * Fires when the splitter is moved
8795          * @param {Roo.SplitBar} this
8796          * @param {Number} newSize the new width or height
8797          */
8798         "moved" : true,
8799         /**
8800          * @event beforeresize
8801          * Fires before the splitter is dragged
8802          * @param {Roo.SplitBar} this
8803          */
8804         "beforeresize" : true,
8805
8806         "beforeapply" : true
8807     });
8808
8809     Roo.util.Observable.call(this);
8810 };
8811
8812 Roo.extend(Roo.SplitBar, Roo.util.Observable, {
8813     onStartProxyDrag : function(x, y){
8814         this.fireEvent("beforeresize", this);
8815         if(!this.overlay){
8816             var o = Roo.DomHelper.insertFirst(document.body,  {cls: "x-drag-overlay", html: "&#160;"}, true);
8817             o.unselectable();
8818             o.enableDisplayMode("block");
8819             // all splitbars share the same overlay
8820             Roo.SplitBar.prototype.overlay = o;
8821         }
8822         this.overlay.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
8823         this.overlay.show();
8824         Roo.get(this.proxy).setDisplayed("block");
8825         var size = this.adapter.getElementSize(this);
8826         this.activeMinSize = this.getMinimumSize();;
8827         this.activeMaxSize = this.getMaximumSize();;
8828         var c1 = size - this.activeMinSize;
8829         var c2 = Math.max(this.activeMaxSize - size, 0);
8830         if(this.orientation == Roo.SplitBar.HORIZONTAL){
8831             this.dd.resetConstraints();
8832             this.dd.setXConstraint(
8833                 this.placement == Roo.SplitBar.LEFT ? c1 : c2, 
8834                 this.placement == Roo.SplitBar.LEFT ? c2 : c1
8835             );
8836             this.dd.setYConstraint(0, 0);
8837         }else{
8838             this.dd.resetConstraints();
8839             this.dd.setXConstraint(0, 0);
8840             this.dd.setYConstraint(
8841                 this.placement == Roo.SplitBar.TOP ? c1 : c2, 
8842                 this.placement == Roo.SplitBar.TOP ? c2 : c1
8843             );
8844          }
8845         this.dragSpecs.startSize = size;
8846         this.dragSpecs.startPoint = [x, y];
8847         Roo.dd.DDProxy.prototype.b4StartDrag.call(this.dd, x, y);
8848     },
8849     
8850     /** 
8851      * @private Called after the drag operation by the DDProxy
8852      */
8853     onEndProxyDrag : function(e){
8854         Roo.get(this.proxy).setDisplayed(false);
8855         var endPoint = Roo.lib.Event.getXY(e);
8856         if(this.overlay){
8857             this.overlay.hide();
8858         }
8859         var newSize;
8860         if(this.orientation == Roo.SplitBar.HORIZONTAL){
8861             newSize = this.dragSpecs.startSize + 
8862                 (this.placement == Roo.SplitBar.LEFT ?
8863                     endPoint[0] - this.dragSpecs.startPoint[0] :
8864                     this.dragSpecs.startPoint[0] - endPoint[0]
8865                 );
8866         }else{
8867             newSize = this.dragSpecs.startSize + 
8868                 (this.placement == Roo.SplitBar.TOP ?
8869                     endPoint[1] - this.dragSpecs.startPoint[1] :
8870                     this.dragSpecs.startPoint[1] - endPoint[1]
8871                 );
8872         }
8873         newSize = Math.min(Math.max(newSize, this.activeMinSize), this.activeMaxSize);
8874         if(newSize != this.dragSpecs.startSize){
8875             if(this.fireEvent('beforeapply', this, newSize) !== false){
8876                 this.adapter.setElementSize(this, newSize);
8877                 this.fireEvent("moved", this, newSize);
8878                 this.fireEvent("resize", this, newSize);
8879             }
8880         }
8881     },
8882     
8883     /**
8884      * Get the adapter this SplitBar uses
8885      * @return The adapter object
8886      */
8887     getAdapter : function(){
8888         return this.adapter;
8889     },
8890     
8891     /**
8892      * Set the adapter this SplitBar uses
8893      * @param {Object} adapter A SplitBar adapter object
8894      */
8895     setAdapter : function(adapter){
8896         this.adapter = adapter;
8897         this.adapter.init(this);
8898     },
8899     
8900     /**
8901      * Gets the minimum size for the resizing element
8902      * @return {Number} The minimum size
8903      */
8904     getMinimumSize : function(){
8905         return this.minSize;
8906     },
8907     
8908     /**
8909      * Sets the minimum size for the resizing element
8910      * @param {Number} minSize The minimum size
8911      */
8912     setMinimumSize : function(minSize){
8913         this.minSize = minSize;
8914     },
8915     
8916     /**
8917      * Gets the maximum size for the resizing element
8918      * @return {Number} The maximum size
8919      */
8920     getMaximumSize : function(){
8921         return this.maxSize;
8922     },
8923     
8924     /**
8925      * Sets the maximum size for the resizing element
8926      * @param {Number} maxSize The maximum size
8927      */
8928     setMaximumSize : function(maxSize){
8929         this.maxSize = maxSize;
8930     },
8931     
8932     /**
8933      * Sets the initialize size for the resizing element
8934      * @param {Number} size The initial size
8935      */
8936     setCurrentSize : function(size){
8937         var oldAnimate = this.animate;
8938         this.animate = false;
8939         this.adapter.setElementSize(this, size);
8940         this.animate = oldAnimate;
8941     },
8942     
8943     /**
8944      * Destroy this splitbar. 
8945      * @param {Boolean} removeEl True to remove the element
8946      */
8947     destroy : function(removeEl){
8948         if(this.shim){
8949             this.shim.remove();
8950         }
8951         this.dd.unreg();
8952         this.proxy.parentNode.removeChild(this.proxy);
8953         if(removeEl){
8954             this.el.remove();
8955         }
8956     }
8957 });
8958
8959 /**
8960  * @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.
8961  */
8962 Roo.SplitBar.createProxy = function(dir){
8963     var proxy = new Roo.Element(document.createElement("div"));
8964     proxy.unselectable();
8965     var cls = 'x-splitbar-proxy';
8966     proxy.addClass(cls + ' ' + (dir == Roo.SplitBar.HORIZONTAL ? cls +'-h' : cls + '-v'));
8967     document.body.appendChild(proxy.dom);
8968     return proxy.dom;
8969 };
8970
8971 /** 
8972  * @class Roo.SplitBar.BasicLayoutAdapter
8973  * Default Adapter. It assumes the splitter and resizing element are not positioned
8974  * elements and only gets/sets the width of the element. Generally used for table based layouts.
8975  */
8976 Roo.SplitBar.BasicLayoutAdapter = function(){
8977 };
8978
8979 Roo.SplitBar.BasicLayoutAdapter.prototype = {
8980     // do nothing for now
8981     init : function(s){
8982     
8983     },
8984     /**
8985      * Called before drag operations to get the current size of the resizing element. 
8986      * @param {Roo.SplitBar} s The SplitBar using this adapter
8987      */
8988      getElementSize : function(s){
8989         if(s.orientation == Roo.SplitBar.HORIZONTAL){
8990             return s.resizingEl.getWidth();
8991         }else{
8992             return s.resizingEl.getHeight();
8993         }
8994     },
8995     
8996     /**
8997      * Called after drag operations to set the size of the resizing element.
8998      * @param {Roo.SplitBar} s The SplitBar using this adapter
8999      * @param {Number} newSize The new size to set
9000      * @param {Function} onComplete A function to be invoked when resizing is complete
9001      */
9002     setElementSize : function(s, newSize, onComplete){
9003         if(s.orientation == Roo.SplitBar.HORIZONTAL){
9004             if(!s.animate){
9005                 s.resizingEl.setWidth(newSize);
9006                 if(onComplete){
9007                     onComplete(s, newSize);
9008                 }
9009             }else{
9010                 s.resizingEl.setWidth(newSize, true, .1, onComplete, 'easeOut');
9011             }
9012         }else{
9013             
9014             if(!s.animate){
9015                 s.resizingEl.setHeight(newSize);
9016                 if(onComplete){
9017                     onComplete(s, newSize);
9018                 }
9019             }else{
9020                 s.resizingEl.setHeight(newSize, true, .1, onComplete, 'easeOut');
9021             }
9022         }
9023     }
9024 };
9025
9026 /** 
9027  *@class Roo.SplitBar.AbsoluteLayoutAdapter
9028  * @extends Roo.SplitBar.BasicLayoutAdapter
9029  * Adapter that  moves the splitter element to align with the resized sizing element. 
9030  * Used with an absolute positioned SplitBar.
9031  * @param {String/HTMLElement/Roo.Element} container The container that wraps around the absolute positioned content. If it's
9032  * document.body, make sure you assign an id to the body element.
9033  */
9034 Roo.SplitBar.AbsoluteLayoutAdapter = function(container){
9035     this.basic = new Roo.SplitBar.BasicLayoutAdapter();
9036     this.container = Roo.get(container);
9037 };
9038
9039 Roo.SplitBar.AbsoluteLayoutAdapter.prototype = {
9040     init : function(s){
9041         this.basic.init(s);
9042     },
9043     
9044     getElementSize : function(s){
9045         return this.basic.getElementSize(s);
9046     },
9047     
9048     setElementSize : function(s, newSize, onComplete){
9049         this.basic.setElementSize(s, newSize, this.moveSplitter.createDelegate(this, [s]));
9050     },
9051     
9052     moveSplitter : function(s){
9053         var yes = Roo.SplitBar;
9054         switch(s.placement){
9055             case yes.LEFT:
9056                 s.el.setX(s.resizingEl.getRight());
9057                 break;
9058             case yes.RIGHT:
9059                 s.el.setStyle("right", (this.container.getWidth() - s.resizingEl.getLeft()) + "px");
9060                 break;
9061             case yes.TOP:
9062                 s.el.setY(s.resizingEl.getBottom());
9063                 break;
9064             case yes.BOTTOM:
9065                 s.el.setY(s.resizingEl.getTop() - s.el.getHeight());
9066                 break;
9067         }
9068     }
9069 };
9070
9071 /**
9072  * Orientation constant - Create a vertical SplitBar
9073  * @static
9074  * @type Number
9075  */
9076 Roo.SplitBar.VERTICAL = 1;
9077
9078 /**
9079  * Orientation constant - Create a horizontal SplitBar
9080  * @static
9081  * @type Number
9082  */
9083 Roo.SplitBar.HORIZONTAL = 2;
9084
9085 /**
9086  * Placement constant - The resizing element is to the left of the splitter element
9087  * @static
9088  * @type Number
9089  */
9090 Roo.SplitBar.LEFT = 1;
9091
9092 /**
9093  * Placement constant - The resizing element is to the right of the splitter element
9094  * @static
9095  * @type Number
9096  */
9097 Roo.SplitBar.RIGHT = 2;
9098
9099 /**
9100  * Placement constant - The resizing element is positioned above the splitter element
9101  * @static
9102  * @type Number
9103  */
9104 Roo.SplitBar.TOP = 3;
9105
9106 /**
9107  * Placement constant - The resizing element is positioned under splitter element
9108  * @static
9109  * @type Number
9110  */
9111 Roo.SplitBar.BOTTOM = 4;
9112 /*
9113  * Based on:
9114  * Ext JS Library 1.1.1
9115  * Copyright(c) 2006-2007, Ext JS, LLC.
9116  *
9117  * Originally Released Under LGPL - original licence link has changed is not relivant.
9118  *
9119  * Fork - LGPL
9120  * <script type="text/javascript">
9121  */
9122
9123 /**
9124  * @class Roo.View
9125  * @extends Roo.util.Observable
9126  * Create a "View" for an element based on a data model or UpdateManager and the supplied DomHelper template. 
9127  * This class also supports single and multi selection modes. <br>
9128  * Create a data model bound view:
9129  <pre><code>
9130  var store = new Roo.data.Store(...);
9131
9132  var view = new Roo.View({
9133     el : "my-element",
9134     tpl : '&lt;div id="{0}"&gt;{2} - {1}&lt;/div&gt;', // auto create template
9135  
9136     singleSelect: true,
9137     selectedClass: "ydataview-selected",
9138     store: store
9139  });
9140
9141  // listen for node click?
9142  view.on("click", function(vw, index, node, e){
9143  alert('Node "' + node.id + '" at index: ' + index + " was clicked.");
9144  });
9145
9146  // load XML data
9147  dataModel.load("foobar.xml");
9148  </code></pre>
9149  For an example of creating a JSON/UpdateManager view, see {@link Roo.JsonView}.
9150  * <br><br>
9151  * <b>Note: The root of your template must be a single node. Table/row implementations may work but are not supported due to
9152  * IE"s limited insertion support with tables and Opera"s faulty event bubbling.</b>
9153  * 
9154  * Note: old style constructor is still suported (container, template, config)
9155  * 
9156  * @constructor
9157  * Create a new View
9158  * @param {Object} config The config object
9159  * 
9160  */
9161 Roo.View = function(config, depreciated_tpl, depreciated_config){
9162     
9163     if (typeof(depreciated_tpl) == 'undefined') {
9164         // new way.. - universal constructor.
9165         Roo.apply(this, config);
9166         this.el  = Roo.get(this.el);
9167     } else {
9168         // old format..
9169         this.el  = Roo.get(config);
9170         this.tpl = depreciated_tpl;
9171         Roo.apply(this, depreciated_config);
9172     }
9173      
9174     
9175     if(typeof(this.tpl) == "string"){
9176         this.tpl = new Roo.Template(this.tpl);
9177     } else {
9178         // support xtype ctors..
9179         this.tpl = new Roo.factory(this.tpl, Roo);
9180     }
9181     
9182     
9183     this.tpl.compile();
9184    
9185
9186      
9187     /** @private */
9188     this.addEvents({
9189         /**
9190          * @event beforeclick
9191          * Fires before a click is processed. Returns false to cancel the default action.
9192          * @param {Roo.View} this
9193          * @param {Number} index The index of the target node
9194          * @param {HTMLElement} node The target node
9195          * @param {Roo.EventObject} e The raw event object
9196          */
9197             "beforeclick" : true,
9198         /**
9199          * @event click
9200          * Fires when a template node is clicked.
9201          * @param {Roo.View} this
9202          * @param {Number} index The index of the target node
9203          * @param {HTMLElement} node The target node
9204          * @param {Roo.EventObject} e The raw event object
9205          */
9206             "click" : true,
9207         /**
9208          * @event dblclick
9209          * Fires when a template node is double clicked.
9210          * @param {Roo.View} this
9211          * @param {Number} index The index of the target node
9212          * @param {HTMLElement} node The target node
9213          * @param {Roo.EventObject} e The raw event object
9214          */
9215             "dblclick" : true,
9216         /**
9217          * @event contextmenu
9218          * Fires when a template node is right clicked.
9219          * @param {Roo.View} this
9220          * @param {Number} index The index of the target node
9221          * @param {HTMLElement} node The target node
9222          * @param {Roo.EventObject} e The raw event object
9223          */
9224             "contextmenu" : true,
9225         /**
9226          * @event selectionchange
9227          * Fires when the selected nodes change.
9228          * @param {Roo.View} this
9229          * @param {Array} selections Array of the selected nodes
9230          */
9231             "selectionchange" : true,
9232     
9233         /**
9234          * @event beforeselect
9235          * Fires before a selection is made. If any handlers return false, the selection is cancelled.
9236          * @param {Roo.View} this
9237          * @param {HTMLElement} node The node to be selected
9238          * @param {Array} selections Array of currently selected nodes
9239          */
9240             "beforeselect" : true,
9241         /**
9242          * @event preparedata
9243          * Fires on every row to render, to allow you to change the data.
9244          * @param {Roo.View} this
9245          * @param {Object} data to be rendered (change this)
9246          */
9247           "preparedata" : true
9248         });
9249
9250     this.el.on({
9251         "click": this.onClick,
9252         "dblclick": this.onDblClick,
9253         "contextmenu": this.onContextMenu,
9254         scope:this
9255     });
9256
9257     this.selections = [];
9258     this.nodes = [];
9259     this.cmp = new Roo.CompositeElementLite([]);
9260     if(this.store){
9261         this.store = Roo.factory(this.store, Roo.data);
9262         this.setStore(this.store, true);
9263     }
9264     Roo.View.superclass.constructor.call(this);
9265     
9266     
9267      
9268     
9269 };
9270
9271 Roo.extend(Roo.View, Roo.util.Observable, {
9272     
9273      /**
9274      * @cfg {Roo.data.Store} store Data store to load data from.
9275      */
9276     store : false,
9277     
9278     /**
9279      * @cfg {String|Roo.Element} el The container element.
9280      */
9281     el : '',
9282     
9283     /**
9284      * @cfg {String|Roo.Template} tpl The template used by this View 
9285      */
9286     tpl : false,
9287     /**
9288      * @cfg {String} dataName the named area of the template to use as the data area
9289      *                          Works with domtemplates roo-name="name"
9290      */
9291     dataName: false,
9292     /**
9293      * @cfg {String} selectedClass The css class to add to selected nodes
9294      */
9295     selectedClass : "x-view-selected",
9296      /**
9297      * @cfg {String} emptyText The empty text to show when nothing is loaded.
9298      */
9299     emptyText : "",
9300     
9301     /**
9302      * @cfg {String} text to display on mask (default Loading)
9303      */
9304     mask : false,
9305     /**
9306      * @cfg {Boolean} multiSelect Allow multiple selection
9307      */
9308     multiSelect : false,
9309     /**
9310      * @cfg {Boolean} singleSelect Allow single selection
9311      */
9312     singleSelect:  false,
9313     
9314     /**
9315      * @cfg {Boolean} toggleSelect - selecting 
9316      */
9317     toggleSelect : false,
9318     
9319     /**
9320      * Returns the element this view is bound to.
9321      * @return {Roo.Element}
9322      */
9323     getEl : function(){
9324         return this.el;
9325     },
9326     
9327     render : function()
9328     {
9329         if (this.footer && this.footer.xtype) {
9330          
9331          
9332             Roo.log("this.el.parentNode()");
9333             Roo.log(this.el.dom );
9334             if (!this.wrapEl) {
9335                 this.wrapEl = this.el.wrap();
9336             }
9337             this.footer.dataSource = this.store
9338             this.footer.container = this.wrapEl;
9339             this.footer = Roo.factory(this.footer, Roo);
9340         }
9341
9342     }
9343     
9344
9345     /**
9346      * Refreshes the view. - called by datachanged on the store. - do not call directly.
9347      */
9348     refresh : function(){
9349         var t = this.tpl;
9350         
9351         // if we are using something like 'domtemplate', then
9352         // the what gets used is:
9353         // t.applySubtemplate(NAME, data, wrapping data..)
9354         // the outer template then get' applied with
9355         //     the store 'extra data'
9356         // and the body get's added to the
9357         //      roo-name="data" node?
9358         //      <span class='roo-tpl-{name}'></span> ?????
9359         
9360         
9361         
9362         this.clearSelections();
9363         this.el.update("");
9364         var html = [];
9365         var records = this.store.getRange();
9366         if(records.length < 1) {
9367             
9368             // is this valid??  = should it render a template??
9369             
9370             this.el.update(this.emptyText);
9371             return;
9372         }
9373         var el = this.el;
9374         if (this.dataName) {
9375             this.el.update(t.apply(this.store.meta)); //????
9376             el = this.el.child('.roo-tpl-' + this.dataName);
9377         }
9378         
9379         for(var i = 0, len = records.length; i < len; i++){
9380             var data = this.prepareData(records[i].data, i, records[i]);
9381             this.fireEvent("preparedata", this, data, i, records[i]);
9382             html[html.length] = Roo.util.Format.trim(
9383                 this.dataName ?
9384                     t.applySubtemplate(this.dataName, data, this.store.meta) :
9385                     t.apply(data)
9386             );
9387         }
9388         
9389         
9390         
9391         el.update(html.join(""));
9392         this.nodes = el.dom.childNodes;
9393         this.updateIndexes(0);
9394     },
9395
9396     /**
9397      * Function to override to reformat the data that is sent to
9398      * the template for each node.
9399      * DEPRICATED - use the preparedata event handler.
9400      * @param {Array/Object} data The raw data (array of colData for a data model bound view or
9401      * a JSON object for an UpdateManager bound view).
9402      */
9403     prepareData : function(data, index, record)
9404     {
9405         this.fireEvent("preparedata", this, data, index, record);
9406         return data;
9407     },
9408
9409     onUpdate : function(ds, record){
9410         this.clearSelections();
9411         var index = this.store.indexOf(record);
9412         var n = this.nodes[index];
9413         this.tpl.insertBefore(n, this.prepareData(record.data, index, record));
9414         n.parentNode.removeChild(n);
9415         this.updateIndexes(index, index);
9416     },
9417
9418     
9419     
9420 // --------- FIXME     
9421     onAdd : function(ds, records, index)
9422     {
9423         this.clearSelections();
9424         if(this.nodes.length == 0){
9425             this.refresh();
9426             return;
9427         }
9428         var n = this.nodes[index];
9429         for(var i = 0, len = records.length; i < len; i++){
9430             var d = this.prepareData(records[i].data, i, records[i]);
9431             if(n){
9432                 this.tpl.insertBefore(n, d);
9433             }else{
9434                 
9435                 this.tpl.append(this.el, d);
9436             }
9437         }
9438         this.updateIndexes(index);
9439     },
9440
9441     onRemove : function(ds, record, index){
9442         this.clearSelections();
9443         var el = this.dataName  ?
9444             this.el.child('.roo-tpl-' + this.dataName) :
9445             this.el; 
9446         el.dom.removeChild(this.nodes[index]);
9447         this.updateIndexes(index);
9448     },
9449
9450     /**
9451      * Refresh an individual node.
9452      * @param {Number} index
9453      */
9454     refreshNode : function(index){
9455         this.onUpdate(this.store, this.store.getAt(index));
9456     },
9457
9458     updateIndexes : function(startIndex, endIndex){
9459         var ns = this.nodes;
9460         startIndex = startIndex || 0;
9461         endIndex = endIndex || ns.length - 1;
9462         for(var i = startIndex; i <= endIndex; i++){
9463             ns[i].nodeIndex = i;
9464         }
9465     },
9466
9467     /**
9468      * Changes the data store this view uses and refresh the view.
9469      * @param {Store} store
9470      */
9471     setStore : function(store, initial){
9472         if(!initial && this.store){
9473             this.store.un("datachanged", this.refresh);
9474             this.store.un("add", this.onAdd);
9475             this.store.un("remove", this.onRemove);
9476             this.store.un("update", this.onUpdate);
9477             this.store.un("clear", this.refresh);
9478             this.store.un("beforeload", this.onBeforeLoad);
9479             this.store.un("load", this.onLoad);
9480             this.store.un("loadexception", this.onLoad);
9481         }
9482         if(store){
9483           
9484             store.on("datachanged", this.refresh, this);
9485             store.on("add", this.onAdd, this);
9486             store.on("remove", this.onRemove, this);
9487             store.on("update", this.onUpdate, this);
9488             store.on("clear", this.refresh, this);
9489             store.on("beforeload", this.onBeforeLoad, this);
9490             store.on("load", this.onLoad, this);
9491             store.on("loadexception", this.onLoad, this);
9492         }
9493         
9494         if(store){
9495             this.refresh();
9496         }
9497     },
9498     /**
9499      * onbeforeLoad - masks the loading area.
9500      *
9501      */
9502     onBeforeLoad : function()
9503     {
9504         this.el.update("");
9505         this.el.mask(this.mask ? this.mask : "Loading" ); 
9506     },
9507     onLoad : function ()
9508     {
9509         this.el.unmask();
9510     },
9511     
9512
9513     /**
9514      * Returns the template node the passed child belongs to or null if it doesn't belong to one.
9515      * @param {HTMLElement} node
9516      * @return {HTMLElement} The template node
9517      */
9518     findItemFromChild : function(node){
9519         var el = this.dataName  ?
9520             this.el.child('.roo-tpl-' + this.dataName,true) :
9521             this.el.dom; 
9522         
9523         if(!node || node.parentNode == el){
9524                     return node;
9525             }
9526             var p = node.parentNode;
9527             while(p && p != el){
9528             if(p.parentNode == el){
9529                 return p;
9530             }
9531             p = p.parentNode;
9532         }
9533             return null;
9534     },
9535
9536     /** @ignore */
9537     onClick : function(e){
9538         var item = this.findItemFromChild(e.getTarget());
9539         if(item){
9540             var index = this.indexOf(item);
9541             if(this.onItemClick(item, index, e) !== false){
9542                 this.fireEvent("click", this, index, item, e);
9543             }
9544         }else{
9545             this.clearSelections();
9546         }
9547     },
9548
9549     /** @ignore */
9550     onContextMenu : function(e){
9551         var item = this.findItemFromChild(e.getTarget());
9552         if(item){
9553             this.fireEvent("contextmenu", this, this.indexOf(item), item, e);
9554         }
9555     },
9556
9557     /** @ignore */
9558     onDblClick : function(e){
9559         var item = this.findItemFromChild(e.getTarget());
9560         if(item){
9561             this.fireEvent("dblclick", this, this.indexOf(item), item, e);
9562         }
9563     },
9564
9565     onItemClick : function(item, index, e)
9566     {
9567         if(this.fireEvent("beforeclick", this, index, item, e) === false){
9568             return false;
9569         }
9570         if (this.toggleSelect) {
9571             var m = this.isSelected(item) ? 'unselect' : 'select';
9572             Roo.log(m);
9573             var _t = this;
9574             _t[m](item, true, false);
9575             return true;
9576         }
9577         if(this.multiSelect || this.singleSelect){
9578             if(this.multiSelect && e.shiftKey && this.lastSelection){
9579                 this.select(this.getNodes(this.indexOf(this.lastSelection), index), false);
9580             }else{
9581                 this.select(item, this.multiSelect && e.ctrlKey);
9582                 this.lastSelection = item;
9583             }
9584             e.preventDefault();
9585         }
9586         return true;
9587     },
9588
9589     /**
9590      * Get the number of selected nodes.
9591      * @return {Number}
9592      */
9593     getSelectionCount : function(){
9594         return this.selections.length;
9595     },
9596
9597     /**
9598      * Get the currently selected nodes.
9599      * @return {Array} An array of HTMLElements
9600      */
9601     getSelectedNodes : function(){
9602         return this.selections;
9603     },
9604
9605     /**
9606      * Get the indexes of the selected nodes.
9607      * @return {Array}
9608      */
9609     getSelectedIndexes : function(){
9610         var indexes = [], s = this.selections;
9611         for(var i = 0, len = s.length; i < len; i++){
9612             indexes.push(s[i].nodeIndex);
9613         }
9614         return indexes;
9615     },
9616
9617     /**
9618      * Clear all selections
9619      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange event
9620      */
9621     clearSelections : function(suppressEvent){
9622         if(this.nodes && (this.multiSelect || this.singleSelect) && this.selections.length > 0){
9623             this.cmp.elements = this.selections;
9624             this.cmp.removeClass(this.selectedClass);
9625             this.selections = [];
9626             if(!suppressEvent){
9627                 this.fireEvent("selectionchange", this, this.selections);
9628             }
9629         }
9630     },
9631
9632     /**
9633      * Returns true if the passed node is selected
9634      * @param {HTMLElement/Number} node The node or node index
9635      * @return {Boolean}
9636      */
9637     isSelected : function(node){
9638         var s = this.selections;
9639         if(s.length < 1){
9640             return false;
9641         }
9642         node = this.getNode(node);
9643         return s.indexOf(node) !== -1;
9644     },
9645
9646     /**
9647      * Selects nodes.
9648      * @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
9649      * @param {Boolean} keepExisting (optional) true to keep existing selections
9650      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
9651      */
9652     select : function(nodeInfo, keepExisting, suppressEvent){
9653         if(nodeInfo instanceof Array){
9654             if(!keepExisting){
9655                 this.clearSelections(true);
9656             }
9657             for(var i = 0, len = nodeInfo.length; i < len; i++){
9658                 this.select(nodeInfo[i], true, true);
9659             }
9660             return;
9661         } 
9662         var node = this.getNode(nodeInfo);
9663         if(!node || this.isSelected(node)){
9664             return; // already selected.
9665         }
9666         if(!keepExisting){
9667             this.clearSelections(true);
9668         }
9669         if(this.fireEvent("beforeselect", this, node, this.selections) !== false){
9670             Roo.fly(node).addClass(this.selectedClass);
9671             this.selections.push(node);
9672             if(!suppressEvent){
9673                 this.fireEvent("selectionchange", this, this.selections);
9674             }
9675         }
9676         
9677         
9678     },
9679       /**
9680      * Unselects nodes.
9681      * @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
9682      * @param {Boolean} keepExisting (optional) true IGNORED (for campatibility with select)
9683      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
9684      */
9685     unselect : function(nodeInfo, keepExisting, suppressEvent)
9686     {
9687         if(nodeInfo instanceof Array){
9688             Roo.each(this.selections, function(s) {
9689                 this.unselect(s, nodeInfo);
9690             }, this);
9691             return;
9692         }
9693         var node = this.getNode(nodeInfo);
9694         if(!node || !this.isSelected(node)){
9695             Roo.log("not selected");
9696             return; // not selected.
9697         }
9698         // fireevent???
9699         var ns = [];
9700         Roo.each(this.selections, function(s) {
9701             if (s == node ) {
9702                 Roo.fly(node).removeClass(this.selectedClass);
9703
9704                 return;
9705             }
9706             ns.push(s);
9707         },this);
9708         
9709         this.selections= ns;
9710         this.fireEvent("selectionchange", this, this.selections);
9711     },
9712
9713     /**
9714      * Gets a template node.
9715      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
9716      * @return {HTMLElement} The node or null if it wasn't found
9717      */
9718     getNode : function(nodeInfo){
9719         if(typeof nodeInfo == "string"){
9720             return document.getElementById(nodeInfo);
9721         }else if(typeof nodeInfo == "number"){
9722             return this.nodes[nodeInfo];
9723         }
9724         return nodeInfo;
9725     },
9726
9727     /**
9728      * Gets a range template nodes.
9729      * @param {Number} startIndex
9730      * @param {Number} endIndex
9731      * @return {Array} An array of nodes
9732      */
9733     getNodes : function(start, end){
9734         var ns = this.nodes;
9735         start = start || 0;
9736         end = typeof end == "undefined" ? ns.length - 1 : end;
9737         var nodes = [];
9738         if(start <= end){
9739             for(var i = start; i <= end; i++){
9740                 nodes.push(ns[i]);
9741             }
9742         } else{
9743             for(var i = start; i >= end; i--){
9744                 nodes.push(ns[i]);
9745             }
9746         }
9747         return nodes;
9748     },
9749
9750     /**
9751      * Finds the index of the passed node
9752      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
9753      * @return {Number} The index of the node or -1
9754      */
9755     indexOf : function(node){
9756         node = this.getNode(node);
9757         if(typeof node.nodeIndex == "number"){
9758             return node.nodeIndex;
9759         }
9760         var ns = this.nodes;
9761         for(var i = 0, len = ns.length; i < len; i++){
9762             if(ns[i] == node){
9763                 return i;
9764             }
9765         }
9766         return -1;
9767     }
9768 });
9769 /*
9770  * Based on:
9771  * Ext JS Library 1.1.1
9772  * Copyright(c) 2006-2007, Ext JS, LLC.
9773  *
9774  * Originally Released Under LGPL - original licence link has changed is not relivant.
9775  *
9776  * Fork - LGPL
9777  * <script type="text/javascript">
9778  */
9779
9780 /**
9781  * @class Roo.JsonView
9782  * @extends Roo.View
9783  * Shortcut class to create a JSON + {@link Roo.UpdateManager} template view. Usage:
9784 <pre><code>
9785 var view = new Roo.JsonView({
9786     container: "my-element",
9787     tpl: '&lt;div id="{id}"&gt;{foo} - {bar}&lt;/div&gt;', // auto create template
9788     multiSelect: true, 
9789     jsonRoot: "data" 
9790 });
9791
9792 // listen for node click?
9793 view.on("click", function(vw, index, node, e){
9794     alert('Node "' + node.id + '" at index: ' + index + " was clicked.");
9795 });
9796
9797 // direct load of JSON data
9798 view.load("foobar.php");
9799
9800 // Example from my blog list
9801 var tpl = new Roo.Template(
9802     '&lt;div class="entry"&gt;' +
9803     '&lt;a class="entry-title" href="{link}"&gt;{title}&lt;/a&gt;' +
9804     "&lt;h4&gt;{date} by {author} | {comments} Comments&lt;/h4&gt;{description}" +
9805     "&lt;/div&gt;&lt;hr /&gt;"
9806 );
9807
9808 var moreView = new Roo.JsonView({
9809     container :  "entry-list", 
9810     template : tpl,
9811     jsonRoot: "posts"
9812 });
9813 moreView.on("beforerender", this.sortEntries, this);
9814 moreView.load({
9815     url: "/blog/get-posts.php",
9816     params: "allposts=true",
9817     text: "Loading Blog Entries..."
9818 });
9819 </code></pre>
9820
9821 * Note: old code is supported with arguments : (container, template, config)
9822
9823
9824  * @constructor
9825  * Create a new JsonView
9826  * 
9827  * @param {Object} config The config object
9828  * 
9829  */
9830 Roo.JsonView = function(config, depreciated_tpl, depreciated_config){
9831     
9832     
9833     Roo.JsonView.superclass.constructor.call(this, config, depreciated_tpl, depreciated_config);
9834
9835     var um = this.el.getUpdateManager();
9836     um.setRenderer(this);
9837     um.on("update", this.onLoad, this);
9838     um.on("failure", this.onLoadException, this);
9839
9840     /**
9841      * @event beforerender
9842      * Fires before rendering of the downloaded JSON data.
9843      * @param {Roo.JsonView} this
9844      * @param {Object} data The JSON data loaded
9845      */
9846     /**
9847      * @event load
9848      * Fires when data is loaded.
9849      * @param {Roo.JsonView} this
9850      * @param {Object} data The JSON data loaded
9851      * @param {Object} response The raw Connect response object
9852      */
9853     /**
9854      * @event loadexception
9855      * Fires when loading fails.
9856      * @param {Roo.JsonView} this
9857      * @param {Object} response The raw Connect response object
9858      */
9859     this.addEvents({
9860         'beforerender' : true,
9861         'load' : true,
9862         'loadexception' : true
9863     });
9864 };
9865 Roo.extend(Roo.JsonView, Roo.View, {
9866     /**
9867      * @type {String} The root property in the loaded JSON object that contains the data
9868      */
9869     jsonRoot : "",
9870
9871     /**
9872      * Refreshes the view.
9873      */
9874     refresh : function(){
9875         this.clearSelections();
9876         this.el.update("");
9877         var html = [];
9878         var o = this.jsonData;
9879         if(o && o.length > 0){
9880             for(var i = 0, len = o.length; i < len; i++){
9881                 var data = this.prepareData(o[i], i, o);
9882                 html[html.length] = this.tpl.apply(data);
9883             }
9884         }else{
9885             html.push(this.emptyText);
9886         }
9887         this.el.update(html.join(""));
9888         this.nodes = this.el.dom.childNodes;
9889         this.updateIndexes(0);
9890     },
9891
9892     /**
9893      * 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.
9894      * @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:
9895      <pre><code>
9896      view.load({
9897          url: "your-url.php",
9898          params: {param1: "foo", param2: "bar"}, // or a URL encoded string
9899          callback: yourFunction,
9900          scope: yourObject, //(optional scope)
9901          discardUrl: false,
9902          nocache: false,
9903          text: "Loading...",
9904          timeout: 30,
9905          scripts: false
9906      });
9907      </code></pre>
9908      * The only required property is <i>url</i>. The optional properties <i>nocache</i>, <i>text</i> and <i>scripts</i>
9909      * 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.
9910      * @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}
9911      * @param {Function} callback (optional) Callback when transaction is complete - called with signature (oElement, bSuccess)
9912      * @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.
9913      */
9914     load : function(){
9915         var um = this.el.getUpdateManager();
9916         um.update.apply(um, arguments);
9917     },
9918
9919     render : function(el, response){
9920         this.clearSelections();
9921         this.el.update("");
9922         var o;
9923         try{
9924             o = Roo.util.JSON.decode(response.responseText);
9925             if(this.jsonRoot){
9926                 
9927                 o = o[this.jsonRoot];
9928             }
9929         } catch(e){
9930         }
9931         /**
9932          * The current JSON data or null
9933          */
9934         this.jsonData = o;
9935         this.beforeRender();
9936         this.refresh();
9937     },
9938
9939 /**
9940  * Get the number of records in the current JSON dataset
9941  * @return {Number}
9942  */
9943     getCount : function(){
9944         return this.jsonData ? this.jsonData.length : 0;
9945     },
9946
9947 /**
9948  * Returns the JSON object for the specified node(s)
9949  * @param {HTMLElement/Array} node The node or an array of nodes
9950  * @return {Object/Array} If you pass in an array, you get an array back, otherwise
9951  * you get the JSON object for the node
9952  */
9953     getNodeData : function(node){
9954         if(node instanceof Array){
9955             var data = [];
9956             for(var i = 0, len = node.length; i < len; i++){
9957                 data.push(this.getNodeData(node[i]));
9958             }
9959             return data;
9960         }
9961         return this.jsonData[this.indexOf(node)] || null;
9962     },
9963
9964     beforeRender : function(){
9965         this.snapshot = this.jsonData;
9966         if(this.sortInfo){
9967             this.sort.apply(this, this.sortInfo);
9968         }
9969         this.fireEvent("beforerender", this, this.jsonData);
9970     },
9971
9972     onLoad : function(el, o){
9973         this.fireEvent("load", this, this.jsonData, o);
9974     },
9975
9976     onLoadException : function(el, o){
9977         this.fireEvent("loadexception", this, o);
9978     },
9979
9980 /**
9981  * Filter the data by a specific property.
9982  * @param {String} property A property on your JSON objects
9983  * @param {String/RegExp} value Either string that the property values
9984  * should start with, or a RegExp to test against the property
9985  */
9986     filter : function(property, value){
9987         if(this.jsonData){
9988             var data = [];
9989             var ss = this.snapshot;
9990             if(typeof value == "string"){
9991                 var vlen = value.length;
9992                 if(vlen == 0){
9993                     this.clearFilter();
9994                     return;
9995                 }
9996                 value = value.toLowerCase();
9997                 for(var i = 0, len = ss.length; i < len; i++){
9998                     var o = ss[i];
9999                     if(o[property].substr(0, vlen).toLowerCase() == value){
10000                         data.push(o);
10001                     }
10002                 }
10003             } else if(value.exec){ // regex?
10004                 for(var i = 0, len = ss.length; i < len; i++){
10005                     var o = ss[i];
10006                     if(value.test(o[property])){
10007                         data.push(o);
10008                     }
10009                 }
10010             } else{
10011                 return;
10012             }
10013             this.jsonData = data;
10014             this.refresh();
10015         }
10016     },
10017
10018 /**
10019  * Filter by a function. The passed function will be called with each
10020  * object in the current dataset. If the function returns true the value is kept,
10021  * otherwise it is filtered.
10022  * @param {Function} fn
10023  * @param {Object} scope (optional) The scope of the function (defaults to this JsonView)
10024  */
10025     filterBy : function(fn, scope){
10026         if(this.jsonData){
10027             var data = [];
10028             var ss = this.snapshot;
10029             for(var i = 0, len = ss.length; i < len; i++){
10030                 var o = ss[i];
10031                 if(fn.call(scope || this, o)){
10032                     data.push(o);
10033                 }
10034             }
10035             this.jsonData = data;
10036             this.refresh();
10037         }
10038     },
10039
10040 /**
10041  * Clears the current filter.
10042  */
10043     clearFilter : function(){
10044         if(this.snapshot && this.jsonData != this.snapshot){
10045             this.jsonData = this.snapshot;
10046             this.refresh();
10047         }
10048     },
10049
10050
10051 /**
10052  * Sorts the data for this view and refreshes it.
10053  * @param {String} property A property on your JSON objects to sort on
10054  * @param {String} direction (optional) "desc" or "asc" (defaults to "asc")
10055  * @param {Function} sortType (optional) A function to call to convert the data to a sortable value.
10056  */
10057     sort : function(property, dir, sortType){
10058         this.sortInfo = Array.prototype.slice.call(arguments, 0);
10059         if(this.jsonData){
10060             var p = property;
10061             var dsc = dir && dir.toLowerCase() == "desc";
10062             var f = function(o1, o2){
10063                 var v1 = sortType ? sortType(o1[p]) : o1[p];
10064                 var v2 = sortType ? sortType(o2[p]) : o2[p];
10065                 ;
10066                 if(v1 < v2){
10067                     return dsc ? +1 : -1;
10068                 } else if(v1 > v2){
10069                     return dsc ? -1 : +1;
10070                 } else{
10071                     return 0;
10072                 }
10073             };
10074             this.jsonData.sort(f);
10075             this.refresh();
10076             if(this.jsonData != this.snapshot){
10077                 this.snapshot.sort(f);
10078             }
10079         }
10080     }
10081 });/*
10082  * Based on:
10083  * Ext JS Library 1.1.1
10084  * Copyright(c) 2006-2007, Ext JS, LLC.
10085  *
10086  * Originally Released Under LGPL - original licence link has changed is not relivant.
10087  *
10088  * Fork - LGPL
10089  * <script type="text/javascript">
10090  */
10091  
10092
10093 /**
10094  * @class Roo.ColorPalette
10095  * @extends Roo.Component
10096  * Simple color palette class for choosing colors.  The palette can be rendered to any container.<br />
10097  * Here's an example of typical usage:
10098  * <pre><code>
10099 var cp = new Roo.ColorPalette({value:'993300'});  // initial selected color
10100 cp.render('my-div');
10101
10102 cp.on('select', function(palette, selColor){
10103     // do something with selColor
10104 });
10105 </code></pre>
10106  * @constructor
10107  * Create a new ColorPalette
10108  * @param {Object} config The config object
10109  */
10110 Roo.ColorPalette = function(config){
10111     Roo.ColorPalette.superclass.constructor.call(this, config);
10112     this.addEvents({
10113         /**
10114              * @event select
10115              * Fires when a color is selected
10116              * @param {ColorPalette} this
10117              * @param {String} color The 6-digit color hex code (without the # symbol)
10118              */
10119         select: true
10120     });
10121
10122     if(this.handler){
10123         this.on("select", this.handler, this.scope, true);
10124     }
10125 };
10126 Roo.extend(Roo.ColorPalette, Roo.Component, {
10127     /**
10128      * @cfg {String} itemCls
10129      * The CSS class to apply to the containing element (defaults to "x-color-palette")
10130      */
10131     itemCls : "x-color-palette",
10132     /**
10133      * @cfg {String} value
10134      * The initial color to highlight (should be a valid 6-digit color hex code without the # symbol).  Note that
10135      * the hex codes are case-sensitive.
10136      */
10137     value : null,
10138     clickEvent:'click',
10139     // private
10140     ctype: "Roo.ColorPalette",
10141
10142     /**
10143      * @cfg {Boolean} allowReselect If set to true then reselecting a color that is already selected fires the selection event
10144      */
10145     allowReselect : false,
10146
10147     /**
10148      * <p>An array of 6-digit color hex code strings (without the # symbol).  This array can contain any number
10149      * of colors, and each hex code should be unique.  The width of the palette is controlled via CSS by adjusting
10150      * the width property of the 'x-color-palette' class (or assigning a custom class), so you can balance the number
10151      * of colors with the width setting until the box is symmetrical.</p>
10152      * <p>You can override individual colors if needed:</p>
10153      * <pre><code>
10154 var cp = new Roo.ColorPalette();
10155 cp.colors[0] = "FF0000";  // change the first box to red
10156 </code></pre>
10157
10158 Or you can provide a custom array of your own for complete control:
10159 <pre><code>
10160 var cp = new Roo.ColorPalette();
10161 cp.colors = ["000000", "993300", "333300"];
10162 </code></pre>
10163      * @type Array
10164      */
10165     colors : [
10166         "000000", "993300", "333300", "003300", "003366", "000080", "333399", "333333",
10167         "800000", "FF6600", "808000", "008000", "008080", "0000FF", "666699", "808080",
10168         "FF0000", "FF9900", "99CC00", "339966", "33CCCC", "3366FF", "800080", "969696",
10169         "FF00FF", "FFCC00", "FFFF00", "00FF00", "00FFFF", "00CCFF", "993366", "C0C0C0",
10170         "FF99CC", "FFCC99", "FFFF99", "CCFFCC", "CCFFFF", "99CCFF", "CC99FF", "FFFFFF"
10171     ],
10172
10173     // private
10174     onRender : function(container, position){
10175         var t = new Roo.MasterTemplate(
10176             '<tpl><a href="#" class="color-{0}" hidefocus="on"><em><span style="background:#{0}" unselectable="on">&#160;</span></em></a></tpl>'
10177         );
10178         var c = this.colors;
10179         for(var i = 0, len = c.length; i < len; i++){
10180             t.add([c[i]]);
10181         }
10182         var el = document.createElement("div");
10183         el.className = this.itemCls;
10184         t.overwrite(el);
10185         container.dom.insertBefore(el, position);
10186         this.el = Roo.get(el);
10187         this.el.on(this.clickEvent, this.handleClick,  this, {delegate: "a"});
10188         if(this.clickEvent != 'click'){
10189             this.el.on('click', Roo.emptyFn,  this, {delegate: "a", preventDefault:true});
10190         }
10191     },
10192
10193     // private
10194     afterRender : function(){
10195         Roo.ColorPalette.superclass.afterRender.call(this);
10196         if(this.value){
10197             var s = this.value;
10198             this.value = null;
10199             this.select(s);
10200         }
10201     },
10202
10203     // private
10204     handleClick : function(e, t){
10205         e.preventDefault();
10206         if(!this.disabled){
10207             var c = t.className.match(/(?:^|\s)color-(.{6})(?:\s|$)/)[1];
10208             this.select(c.toUpperCase());
10209         }
10210     },
10211
10212     /**
10213      * Selects the specified color in the palette (fires the select event)
10214      * @param {String} color A valid 6-digit color hex code (# will be stripped if included)
10215      */
10216     select : function(color){
10217         color = color.replace("#", "");
10218         if(color != this.value || this.allowReselect){
10219             var el = this.el;
10220             if(this.value){
10221                 el.child("a.color-"+this.value).removeClass("x-color-palette-sel");
10222             }
10223             el.child("a.color-"+color).addClass("x-color-palette-sel");
10224             this.value = color;
10225             this.fireEvent("select", this, color);
10226         }
10227     }
10228 });/*
10229  * Based on:
10230  * Ext JS Library 1.1.1
10231  * Copyright(c) 2006-2007, Ext JS, LLC.
10232  *
10233  * Originally Released Under LGPL - original licence link has changed is not relivant.
10234  *
10235  * Fork - LGPL
10236  * <script type="text/javascript">
10237  */
10238  
10239 /**
10240  * @class Roo.DatePicker
10241  * @extends Roo.Component
10242  * Simple date picker class.
10243  * @constructor
10244  * Create a new DatePicker
10245  * @param {Object} config The config object
10246  */
10247 Roo.DatePicker = function(config){
10248     Roo.DatePicker.superclass.constructor.call(this, config);
10249
10250     this.value = config && config.value ?
10251                  config.value.clearTime() : new Date().clearTime();
10252
10253     this.addEvents({
10254         /**
10255              * @event select
10256              * Fires when a date is selected
10257              * @param {DatePicker} this
10258              * @param {Date} date The selected date
10259              */
10260         'select': true,
10261         /**
10262              * @event monthchange
10263              * Fires when the displayed month changes 
10264              * @param {DatePicker} this
10265              * @param {Date} date The selected month
10266              */
10267         'monthchange': true
10268     });
10269
10270     if(this.handler){
10271         this.on("select", this.handler,  this.scope || this);
10272     }
10273     // build the disabledDatesRE
10274     if(!this.disabledDatesRE && this.disabledDates){
10275         var dd = this.disabledDates;
10276         var re = "(?:";
10277         for(var i = 0; i < dd.length; i++){
10278             re += dd[i];
10279             if(i != dd.length-1) re += "|";
10280         }
10281         this.disabledDatesRE = new RegExp(re + ")");
10282     }
10283 };
10284
10285 Roo.extend(Roo.DatePicker, Roo.Component, {
10286     /**
10287      * @cfg {String} todayText
10288      * The text to display on the button that selects the current date (defaults to "Today")
10289      */
10290     todayText : "Today",
10291     /**
10292      * @cfg {String} okText
10293      * The text to display on the ok button
10294      */
10295     okText : "&#160;OK&#160;", // &#160; to give the user extra clicking room
10296     /**
10297      * @cfg {String} cancelText
10298      * The text to display on the cancel button
10299      */
10300     cancelText : "Cancel",
10301     /**
10302      * @cfg {String} todayTip
10303      * The tooltip to display for the button that selects the current date (defaults to "{current date} (Spacebar)")
10304      */
10305     todayTip : "{0} (Spacebar)",
10306     /**
10307      * @cfg {Date} minDate
10308      * Minimum allowable date (JavaScript date object, defaults to null)
10309      */
10310     minDate : null,
10311     /**
10312      * @cfg {Date} maxDate
10313      * Maximum allowable date (JavaScript date object, defaults to null)
10314      */
10315     maxDate : null,
10316     /**
10317      * @cfg {String} minText
10318      * The error text to display if the minDate validation fails (defaults to "This date is before the minimum date")
10319      */
10320     minText : "This date is before the minimum date",
10321     /**
10322      * @cfg {String} maxText
10323      * The error text to display if the maxDate validation fails (defaults to "This date is after the maximum date")
10324      */
10325     maxText : "This date is after the maximum date",
10326     /**
10327      * @cfg {String} format
10328      * The default date format string which can be overriden for localization support.  The format must be
10329      * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
10330      */
10331     format : "m/d/y",
10332     /**
10333      * @cfg {Array} disabledDays
10334      * An array of days to disable, 0-based. For example, [0, 6] disables Sunday and Saturday (defaults to null).
10335      */
10336     disabledDays : null,
10337     /**
10338      * @cfg {String} disabledDaysText
10339      * The tooltip to display when the date falls on a disabled day (defaults to "")
10340      */
10341     disabledDaysText : "",
10342     /**
10343      * @cfg {RegExp} disabledDatesRE
10344      * JavaScript regular expression used to disable a pattern of dates (defaults to null)
10345      */
10346     disabledDatesRE : null,
10347     /**
10348      * @cfg {String} disabledDatesText
10349      * The tooltip text to display when the date falls on a disabled date (defaults to "")
10350      */
10351     disabledDatesText : "",
10352     /**
10353      * @cfg {Boolean} constrainToViewport
10354      * True to constrain the date picker to the viewport (defaults to true)
10355      */
10356     constrainToViewport : true,
10357     /**
10358      * @cfg {Array} monthNames
10359      * An array of textual month names which can be overriden for localization support (defaults to Date.monthNames)
10360      */
10361     monthNames : Date.monthNames,
10362     /**
10363      * @cfg {Array} dayNames
10364      * An array of textual day names which can be overriden for localization support (defaults to Date.dayNames)
10365      */
10366     dayNames : Date.dayNames,
10367     /**
10368      * @cfg {String} nextText
10369      * The next month navigation button tooltip (defaults to 'Next Month (Control+Right)')
10370      */
10371     nextText: 'Next Month (Control+Right)',
10372     /**
10373      * @cfg {String} prevText
10374      * The previous month navigation button tooltip (defaults to 'Previous Month (Control+Left)')
10375      */
10376     prevText: 'Previous Month (Control+Left)',
10377     /**
10378      * @cfg {String} monthYearText
10379      * The header month selector tooltip (defaults to 'Choose a month (Control+Up/Down to move years)')
10380      */
10381     monthYearText: 'Choose a month (Control+Up/Down to move years)',
10382     /**
10383      * @cfg {Number} startDay
10384      * Day index at which the week should begin, 0-based (defaults to 0, which is Sunday)
10385      */
10386     startDay : 0,
10387     /**
10388      * @cfg {Bool} showClear
10389      * Show a clear button (usefull for date form elements that can be blank.)
10390      */
10391     
10392     showClear: false,
10393     
10394     /**
10395      * Sets the value of the date field
10396      * @param {Date} value The date to set
10397      */
10398     setValue : function(value){
10399         var old = this.value;
10400         
10401         if (typeof(value) == 'string') {
10402          
10403             value = Date.parseDate(value, this.format);
10404         }
10405         if (!value) {
10406             value = new Date();
10407         }
10408         
10409         this.value = value.clearTime(true);
10410         if(this.el){
10411             this.update(this.value);
10412         }
10413     },
10414
10415     /**
10416      * Gets the current selected value of the date field
10417      * @return {Date} The selected date
10418      */
10419     getValue : function(){
10420         return this.value;
10421     },
10422
10423     // private
10424     focus : function(){
10425         if(this.el){
10426             this.update(this.activeDate);
10427         }
10428     },
10429
10430     // privateval
10431     onRender : function(container, position){
10432         
10433         var m = [
10434              '<table cellspacing="0">',
10435                 '<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>',
10436                 '<tr><td colspan="3"><table class="x-date-inner" cellspacing="0"><thead><tr>'];
10437         var dn = this.dayNames;
10438         for(var i = 0; i < 7; i++){
10439             var d = this.startDay+i;
10440             if(d > 6){
10441                 d = d-7;
10442             }
10443             m.push("<th><span>", dn[d].substr(0,1), "</span></th>");
10444         }
10445         m[m.length] = "</tr></thead><tbody><tr>";
10446         for(var i = 0; i < 42; i++) {
10447             if(i % 7 == 0 && i != 0){
10448                 m[m.length] = "</tr><tr>";
10449             }
10450             m[m.length] = '<td><a href="#" hidefocus="on" class="x-date-date" tabIndex="1"><em><span></span></em></a></td>';
10451         }
10452         m[m.length] = '</tr></tbody></table></td></tr><tr>'+
10453             '<td colspan="3" class="x-date-bottom" align="center"></td></tr></table><div class="x-date-mp"></div>';
10454
10455         var el = document.createElement("div");
10456         el.className = "x-date-picker";
10457         el.innerHTML = m.join("");
10458
10459         container.dom.insertBefore(el, position);
10460
10461         this.el = Roo.get(el);
10462         this.eventEl = Roo.get(el.firstChild);
10463
10464         new Roo.util.ClickRepeater(this.el.child("td.x-date-left a"), {
10465             handler: this.showPrevMonth,
10466             scope: this,
10467             preventDefault:true,
10468             stopDefault:true
10469         });
10470
10471         new Roo.util.ClickRepeater(this.el.child("td.x-date-right a"), {
10472             handler: this.showNextMonth,
10473             scope: this,
10474             preventDefault:true,
10475             stopDefault:true
10476         });
10477
10478         this.eventEl.on("mousewheel", this.handleMouseWheel,  this);
10479
10480         this.monthPicker = this.el.down('div.x-date-mp');
10481         this.monthPicker.enableDisplayMode('block');
10482         
10483         var kn = new Roo.KeyNav(this.eventEl, {
10484             "left" : function(e){
10485                 e.ctrlKey ?
10486                     this.showPrevMonth() :
10487                     this.update(this.activeDate.add("d", -1));
10488             },
10489
10490             "right" : function(e){
10491                 e.ctrlKey ?
10492                     this.showNextMonth() :
10493                     this.update(this.activeDate.add("d", 1));
10494             },
10495
10496             "up" : function(e){
10497                 e.ctrlKey ?
10498                     this.showNextYear() :
10499                     this.update(this.activeDate.add("d", -7));
10500             },
10501
10502             "down" : function(e){
10503                 e.ctrlKey ?
10504                     this.showPrevYear() :
10505                     this.update(this.activeDate.add("d", 7));
10506             },
10507
10508             "pageUp" : function(e){
10509                 this.showNextMonth();
10510             },
10511
10512             "pageDown" : function(e){
10513                 this.showPrevMonth();
10514             },
10515
10516             "enter" : function(e){
10517                 e.stopPropagation();
10518                 return true;
10519             },
10520
10521             scope : this
10522         });
10523
10524         this.eventEl.on("click", this.handleDateClick,  this, {delegate: "a.x-date-date"});
10525
10526         this.eventEl.addKeyListener(Roo.EventObject.SPACE, this.selectToday,  this);
10527
10528         this.el.unselectable();
10529         
10530         this.cells = this.el.select("table.x-date-inner tbody td");
10531         this.textNodes = this.el.query("table.x-date-inner tbody span");
10532
10533         this.mbtn = new Roo.Button(this.el.child("td.x-date-middle", true), {
10534             text: "&#160;",
10535             tooltip: this.monthYearText
10536         });
10537
10538         this.mbtn.on('click', this.showMonthPicker, this);
10539         this.mbtn.el.child(this.mbtn.menuClassTarget).addClass("x-btn-with-menu");
10540
10541
10542         var today = (new Date()).dateFormat(this.format);
10543         
10544         var baseTb = new Roo.Toolbar(this.el.child("td.x-date-bottom", true));
10545         if (this.showClear) {
10546             baseTb.add( new Roo.Toolbar.Fill());
10547         }
10548         baseTb.add({
10549             text: String.format(this.todayText, today),
10550             tooltip: String.format(this.todayTip, today),
10551             handler: this.selectToday,
10552             scope: this
10553         });
10554         
10555         //var todayBtn = new Roo.Button(this.el.child("td.x-date-bottom", true), {
10556             
10557         //});
10558         if (this.showClear) {
10559             
10560             baseTb.add( new Roo.Toolbar.Fill());
10561             baseTb.add({
10562                 text: '&#160;',
10563                 cls: 'x-btn-icon x-btn-clear',
10564                 handler: function() {
10565                     //this.value = '';
10566                     this.fireEvent("select", this, '');
10567                 },
10568                 scope: this
10569             });
10570         }
10571         
10572         
10573         if(Roo.isIE){
10574             this.el.repaint();
10575         }
10576         this.update(this.value);
10577     },
10578
10579     createMonthPicker : function(){
10580         if(!this.monthPicker.dom.firstChild){
10581             var buf = ['<table border="0" cellspacing="0">'];
10582             for(var i = 0; i < 6; i++){
10583                 buf.push(
10584                     '<tr><td class="x-date-mp-month"><a href="#">', this.monthNames[i].substr(0, 3), '</a></td>',
10585                     '<td class="x-date-mp-month x-date-mp-sep"><a href="#">', this.monthNames[i+6].substr(0, 3), '</a></td>',
10586                     i == 0 ?
10587                     '<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>' :
10588                     '<td class="x-date-mp-year"><a href="#"></a></td><td class="x-date-mp-year"><a href="#"></a></td></tr>'
10589                 );
10590             }
10591             buf.push(
10592                 '<tr class="x-date-mp-btns"><td colspan="4"><button type="button" class="x-date-mp-ok">',
10593                     this.okText,
10594                     '</button><button type="button" class="x-date-mp-cancel">',
10595                     this.cancelText,
10596                     '</button></td></tr>',
10597                 '</table>'
10598             );
10599             this.monthPicker.update(buf.join(''));
10600             this.monthPicker.on('click', this.onMonthClick, this);
10601             this.monthPicker.on('dblclick', this.onMonthDblClick, this);
10602
10603             this.mpMonths = this.monthPicker.select('td.x-date-mp-month');
10604             this.mpYears = this.monthPicker.select('td.x-date-mp-year');
10605
10606             this.mpMonths.each(function(m, a, i){
10607                 i += 1;
10608                 if((i%2) == 0){
10609                     m.dom.xmonth = 5 + Math.round(i * .5);
10610                 }else{
10611                     m.dom.xmonth = Math.round((i-1) * .5);
10612                 }
10613             });
10614         }
10615     },
10616
10617     showMonthPicker : function(){
10618         this.createMonthPicker();
10619         var size = this.el.getSize();
10620         this.monthPicker.setSize(size);
10621         this.monthPicker.child('table').setSize(size);
10622
10623         this.mpSelMonth = (this.activeDate || this.value).getMonth();
10624         this.updateMPMonth(this.mpSelMonth);
10625         this.mpSelYear = (this.activeDate || this.value).getFullYear();
10626         this.updateMPYear(this.mpSelYear);
10627
10628         this.monthPicker.slideIn('t', {duration:.2});
10629     },
10630
10631     updateMPYear : function(y){
10632         this.mpyear = y;
10633         var ys = this.mpYears.elements;
10634         for(var i = 1; i <= 10; i++){
10635             var td = ys[i-1], y2;
10636             if((i%2) == 0){
10637                 y2 = y + Math.round(i * .5);
10638                 td.firstChild.innerHTML = y2;
10639                 td.xyear = y2;
10640             }else{
10641                 y2 = y - (5-Math.round(i * .5));
10642                 td.firstChild.innerHTML = y2;
10643                 td.xyear = y2;
10644             }
10645             this.mpYears.item(i-1)[y2 == this.mpSelYear ? 'addClass' : 'removeClass']('x-date-mp-sel');
10646         }
10647     },
10648
10649     updateMPMonth : function(sm){
10650         this.mpMonths.each(function(m, a, i){
10651             m[m.dom.xmonth == sm ? 'addClass' : 'removeClass']('x-date-mp-sel');
10652         });
10653     },
10654
10655     selectMPMonth: function(m){
10656         
10657     },
10658
10659     onMonthClick : function(e, t){
10660         e.stopEvent();
10661         var el = new Roo.Element(t), pn;
10662         if(el.is('button.x-date-mp-cancel')){
10663             this.hideMonthPicker();
10664         }
10665         else if(el.is('button.x-date-mp-ok')){
10666             this.update(new Date(this.mpSelYear, this.mpSelMonth, (this.activeDate || this.value).getDate()));
10667             this.hideMonthPicker();
10668         }
10669         else if(pn = el.up('td.x-date-mp-month', 2)){
10670             this.mpMonths.removeClass('x-date-mp-sel');
10671             pn.addClass('x-date-mp-sel');
10672             this.mpSelMonth = pn.dom.xmonth;
10673         }
10674         else if(pn = el.up('td.x-date-mp-year', 2)){
10675             this.mpYears.removeClass('x-date-mp-sel');
10676             pn.addClass('x-date-mp-sel');
10677             this.mpSelYear = pn.dom.xyear;
10678         }
10679         else if(el.is('a.x-date-mp-prev')){
10680             this.updateMPYear(this.mpyear-10);
10681         }
10682         else if(el.is('a.x-date-mp-next')){
10683             this.updateMPYear(this.mpyear+10);
10684         }
10685     },
10686
10687     onMonthDblClick : function(e, t){
10688         e.stopEvent();
10689         var el = new Roo.Element(t), pn;
10690         if(pn = el.up('td.x-date-mp-month', 2)){
10691             this.update(new Date(this.mpSelYear, pn.dom.xmonth, (this.activeDate || this.value).getDate()));
10692             this.hideMonthPicker();
10693         }
10694         else if(pn = el.up('td.x-date-mp-year', 2)){
10695             this.update(new Date(pn.dom.xyear, this.mpSelMonth, (this.activeDate || this.value).getDate()));
10696             this.hideMonthPicker();
10697         }
10698     },
10699
10700     hideMonthPicker : function(disableAnim){
10701         if(this.monthPicker){
10702             if(disableAnim === true){
10703                 this.monthPicker.hide();
10704             }else{
10705                 this.monthPicker.slideOut('t', {duration:.2});
10706             }
10707         }
10708     },
10709
10710     // private
10711     showPrevMonth : function(e){
10712         this.update(this.activeDate.add("mo", -1));
10713     },
10714
10715     // private
10716     showNextMonth : function(e){
10717         this.update(this.activeDate.add("mo", 1));
10718     },
10719
10720     // private
10721     showPrevYear : function(){
10722         this.update(this.activeDate.add("y", -1));
10723     },
10724
10725     // private
10726     showNextYear : function(){
10727         this.update(this.activeDate.add("y", 1));
10728     },
10729
10730     // private
10731     handleMouseWheel : function(e){
10732         var delta = e.getWheelDelta();
10733         if(delta > 0){
10734             this.showPrevMonth();
10735             e.stopEvent();
10736         } else if(delta < 0){
10737             this.showNextMonth();
10738             e.stopEvent();
10739         }
10740     },
10741
10742     // private
10743     handleDateClick : function(e, t){
10744         e.stopEvent();
10745         if(t.dateValue && !Roo.fly(t.parentNode).hasClass("x-date-disabled")){
10746             this.setValue(new Date(t.dateValue));
10747             this.fireEvent("select", this, this.value);
10748         }
10749     },
10750
10751     // private
10752     selectToday : function(){
10753         this.setValue(new Date().clearTime());
10754         this.fireEvent("select", this, this.value);
10755     },
10756
10757     // private
10758     update : function(date)
10759     {
10760         var vd = this.activeDate;
10761         this.activeDate = date;
10762         if(vd && this.el){
10763             var t = date.getTime();
10764             if(vd.getMonth() == date.getMonth() && vd.getFullYear() == date.getFullYear()){
10765                 this.cells.removeClass("x-date-selected");
10766                 this.cells.each(function(c){
10767                    if(c.dom.firstChild.dateValue == t){
10768                        c.addClass("x-date-selected");
10769                        setTimeout(function(){
10770                             try{c.dom.firstChild.focus();}catch(e){}
10771                        }, 50);
10772                        return false;
10773                    }
10774                 });
10775                 return;
10776             }
10777         }
10778         
10779         var days = date.getDaysInMonth();
10780         var firstOfMonth = date.getFirstDateOfMonth();
10781         var startingPos = firstOfMonth.getDay()-this.startDay;
10782
10783         if(startingPos <= this.startDay){
10784             startingPos += 7;
10785         }
10786
10787         var pm = date.add("mo", -1);
10788         var prevStart = pm.getDaysInMonth()-startingPos;
10789
10790         var cells = this.cells.elements;
10791         var textEls = this.textNodes;
10792         days += startingPos;
10793
10794         // convert everything to numbers so it's fast
10795         var day = 86400000;
10796         var d = (new Date(pm.getFullYear(), pm.getMonth(), prevStart)).clearTime();
10797         var today = new Date().clearTime().getTime();
10798         var sel = date.clearTime().getTime();
10799         var min = this.minDate ? this.minDate.clearTime() : Number.NEGATIVE_INFINITY;
10800         var max = this.maxDate ? this.maxDate.clearTime() : Number.POSITIVE_INFINITY;
10801         var ddMatch = this.disabledDatesRE;
10802         var ddText = this.disabledDatesText;
10803         var ddays = this.disabledDays ? this.disabledDays.join("") : false;
10804         var ddaysText = this.disabledDaysText;
10805         var format = this.format;
10806
10807         var setCellClass = function(cal, cell){
10808             cell.title = "";
10809             var t = d.getTime();
10810             cell.firstChild.dateValue = t;
10811             if(t == today){
10812                 cell.className += " x-date-today";
10813                 cell.title = cal.todayText;
10814             }
10815             if(t == sel){
10816                 cell.className += " x-date-selected";
10817                 setTimeout(function(){
10818                     try{cell.firstChild.focus();}catch(e){}
10819                 }, 50);
10820             }
10821             // disabling
10822             if(t < min) {
10823                 cell.className = " x-date-disabled";
10824                 cell.title = cal.minText;
10825                 return;
10826             }
10827             if(t > max) {
10828                 cell.className = " x-date-disabled";
10829                 cell.title = cal.maxText;
10830                 return;
10831             }
10832             if(ddays){
10833                 if(ddays.indexOf(d.getDay()) != -1){
10834                     cell.title = ddaysText;
10835                     cell.className = " x-date-disabled";
10836                 }
10837             }
10838             if(ddMatch && format){
10839                 var fvalue = d.dateFormat(format);
10840                 if(ddMatch.test(fvalue)){
10841                     cell.title = ddText.replace("%0", fvalue);
10842                     cell.className = " x-date-disabled";
10843                 }
10844             }
10845         };
10846
10847         var i = 0;
10848         for(; i < startingPos; i++) {
10849             textEls[i].innerHTML = (++prevStart);
10850             d.setDate(d.getDate()+1);
10851             cells[i].className = "x-date-prevday";
10852             setCellClass(this, cells[i]);
10853         }
10854         for(; i < days; i++){
10855             intDay = i - startingPos + 1;
10856             textEls[i].innerHTML = (intDay);
10857             d.setDate(d.getDate()+1);
10858             cells[i].className = "x-date-active";
10859             setCellClass(this, cells[i]);
10860         }
10861         var extraDays = 0;
10862         for(; i < 42; i++) {
10863              textEls[i].innerHTML = (++extraDays);
10864              d.setDate(d.getDate()+1);
10865              cells[i].className = "x-date-nextday";
10866              setCellClass(this, cells[i]);
10867         }
10868
10869         this.mbtn.setText(this.monthNames[date.getMonth()] + " " + date.getFullYear());
10870         this.fireEvent('monthchange', this, date);
10871         
10872         if(!this.internalRender){
10873             var main = this.el.dom.firstChild;
10874             var w = main.offsetWidth;
10875             this.el.setWidth(w + this.el.getBorderWidth("lr"));
10876             Roo.fly(main).setWidth(w);
10877             this.internalRender = true;
10878             // opera does not respect the auto grow header center column
10879             // then, after it gets a width opera refuses to recalculate
10880             // without a second pass
10881             if(Roo.isOpera && !this.secondPass){
10882                 main.rows[0].cells[1].style.width = (w - (main.rows[0].cells[0].offsetWidth+main.rows[0].cells[2].offsetWidth)) + "px";
10883                 this.secondPass = true;
10884                 this.update.defer(10, this, [date]);
10885             }
10886         }
10887         
10888         
10889     }
10890 });        /*
10891  * Based on:
10892  * Ext JS Library 1.1.1
10893  * Copyright(c) 2006-2007, Ext JS, LLC.
10894  *
10895  * Originally Released Under LGPL - original licence link has changed is not relivant.
10896  *
10897  * Fork - LGPL
10898  * <script type="text/javascript">
10899  */
10900 /**
10901  * @class Roo.TabPanel
10902  * @extends Roo.util.Observable
10903  * A lightweight tab container.
10904  * <br><br>
10905  * Usage:
10906  * <pre><code>
10907 // basic tabs 1, built from existing content
10908 var tabs = new Roo.TabPanel("tabs1");
10909 tabs.addTab("script", "View Script");
10910 tabs.addTab("markup", "View Markup");
10911 tabs.activate("script");
10912
10913 // more advanced tabs, built from javascript
10914 var jtabs = new Roo.TabPanel("jtabs");
10915 jtabs.addTab("jtabs-1", "Normal Tab", "My content was added during construction.");
10916
10917 // set up the UpdateManager
10918 var tab2 = jtabs.addTab("jtabs-2", "Ajax Tab 1");
10919 var updater = tab2.getUpdateManager();
10920 updater.setDefaultUrl("ajax1.htm");
10921 tab2.on('activate', updater.refresh, updater, true);
10922
10923 // Use setUrl for Ajax loading
10924 var tab3 = jtabs.addTab("jtabs-3", "Ajax Tab 2");
10925 tab3.setUrl("ajax2.htm", null, true);
10926
10927 // Disabled tab
10928 var tab4 = jtabs.addTab("tabs1-5", "Disabled Tab", "Can't see me cause I'm disabled");
10929 tab4.disable();
10930
10931 jtabs.activate("jtabs-1");
10932  * </code></pre>
10933  * @constructor
10934  * Create a new TabPanel.
10935  * @param {String/HTMLElement/Roo.Element} container The id, DOM element or Roo.Element container where this TabPanel is to be rendered.
10936  * @param {Object/Boolean} config Config object to set any properties for this TabPanel, or true to render the tabs on the bottom.
10937  */
10938 Roo.TabPanel = function(container, config){
10939     /**
10940     * The container element for this TabPanel.
10941     * @type Roo.Element
10942     */
10943     this.el = Roo.get(container, true);
10944     if(config){
10945         if(typeof config == "boolean"){
10946             this.tabPosition = config ? "bottom" : "top";
10947         }else{
10948             Roo.apply(this, config);
10949         }
10950     }
10951     if(this.tabPosition == "bottom"){
10952         this.bodyEl = Roo.get(this.createBody(this.el.dom));
10953         this.el.addClass("x-tabs-bottom");
10954     }
10955     this.stripWrap = Roo.get(this.createStrip(this.el.dom), true);
10956     this.stripEl = Roo.get(this.createStripList(this.stripWrap.dom), true);
10957     this.stripBody = Roo.get(this.stripWrap.dom.firstChild.firstChild, true);
10958     if(Roo.isIE){
10959         Roo.fly(this.stripWrap.dom.firstChild).setStyle("overflow-x", "hidden");
10960     }
10961     if(this.tabPosition != "bottom"){
10962         /** The body element that contains {@link Roo.TabPanelItem} bodies. +
10963          * @type Roo.Element
10964          */
10965         this.bodyEl = Roo.get(this.createBody(this.el.dom));
10966         this.el.addClass("x-tabs-top");
10967     }
10968     this.items = [];
10969
10970     this.bodyEl.setStyle("position", "relative");
10971
10972     this.active = null;
10973     this.activateDelegate = this.activate.createDelegate(this);
10974
10975     this.addEvents({
10976         /**
10977          * @event tabchange
10978          * Fires when the active tab changes
10979          * @param {Roo.TabPanel} this
10980          * @param {Roo.TabPanelItem} activePanel The new active tab
10981          */
10982         "tabchange": true,
10983         /**
10984          * @event beforetabchange
10985          * Fires before the active tab changes, set cancel to true on the "e" parameter to cancel the change
10986          * @param {Roo.TabPanel} this
10987          * @param {Object} e Set cancel to true on this object to cancel the tab change
10988          * @param {Roo.TabPanelItem} tab The tab being changed to
10989          */
10990         "beforetabchange" : true
10991     });
10992
10993     Roo.EventManager.onWindowResize(this.onResize, this);
10994     this.cpad = this.el.getPadding("lr");
10995     this.hiddenCount = 0;
10996
10997
10998     // toolbar on the tabbar support...
10999     if (this.toolbar) {
11000         var tcfg = this.toolbar;
11001         tcfg.container = this.stripEl.child('td.x-tab-strip-toolbar');  
11002         this.toolbar = new Roo.Toolbar(tcfg);
11003         if (Roo.isSafari) {
11004             var tbl = tcfg.container.child('table', true);
11005             tbl.setAttribute('width', '100%');
11006         }
11007         
11008     }
11009    
11010
11011
11012     Roo.TabPanel.superclass.constructor.call(this);
11013 };
11014
11015 Roo.extend(Roo.TabPanel, Roo.util.Observable, {
11016     /*
11017      *@cfg {String} tabPosition "top" or "bottom" (defaults to "top")
11018      */
11019     tabPosition : "top",
11020     /*
11021      *@cfg {Number} currentTabWidth The width of the current tab (defaults to 0)
11022      */
11023     currentTabWidth : 0,
11024     /*
11025      *@cfg {Number} minTabWidth The minimum width of a tab (defaults to 40) (ignored if {@link #resizeTabs} is not true)
11026      */
11027     minTabWidth : 40,
11028     /*
11029      *@cfg {Number} maxTabWidth The maximum width of a tab (defaults to 250) (ignored if {@link #resizeTabs} is not true)
11030      */
11031     maxTabWidth : 250,
11032     /*
11033      *@cfg {Number} preferredTabWidth The preferred (default) width of a tab (defaults to 175) (ignored if {@link #resizeTabs} is not true)
11034      */
11035     preferredTabWidth : 175,
11036     /*
11037      *@cfg {Boolean} resizeTabs True to enable dynamic tab resizing (defaults to false)
11038      */
11039     resizeTabs : false,
11040     /*
11041      *@cfg {Boolean} monitorResize Set this to true to turn on window resize monitoring (ignored if {@link #resizeTabs} is not true) (defaults to true)
11042      */
11043     monitorResize : true,
11044     /*
11045      *@cfg {Object} toolbar xtype description of toolbar to show at the right of the tab bar. 
11046      */
11047     toolbar : false,
11048
11049     /**
11050      * Creates a new {@link Roo.TabPanelItem} by looking for an existing element with the provided id -- if it's not found it creates one.
11051      * @param {String} id The id of the div to use <b>or create</b>
11052      * @param {String} text The text for the tab
11053      * @param {String} content (optional) Content to put in the TabPanelItem body
11054      * @param {Boolean} closable (optional) True to create a close icon on the tab
11055      * @return {Roo.TabPanelItem} The created TabPanelItem
11056      */
11057     addTab : function(id, text, content, closable){
11058         var item = new Roo.TabPanelItem(this, id, text, closable);
11059         this.addTabItem(item);
11060         if(content){
11061             item.setContent(content);
11062         }
11063         return item;
11064     },
11065
11066     /**
11067      * Returns the {@link Roo.TabPanelItem} with the specified id/index
11068      * @param {String/Number} id The id or index of the TabPanelItem to fetch.
11069      * @return {Roo.TabPanelItem}
11070      */
11071     getTab : function(id){
11072         return this.items[id];
11073     },
11074
11075     /**
11076      * Hides the {@link Roo.TabPanelItem} with the specified id/index
11077      * @param {String/Number} id The id or index of the TabPanelItem to hide.
11078      */
11079     hideTab : function(id){
11080         var t = this.items[id];
11081         if(!t.isHidden()){
11082            t.setHidden(true);
11083            this.hiddenCount++;
11084            this.autoSizeTabs();
11085         }
11086     },
11087
11088     /**
11089      * "Unhides" the {@link Roo.TabPanelItem} with the specified id/index.
11090      * @param {String/Number} id The id or index of the TabPanelItem to unhide.
11091      */
11092     unhideTab : function(id){
11093         var t = this.items[id];
11094         if(t.isHidden()){
11095            t.setHidden(false);
11096            this.hiddenCount--;
11097            this.autoSizeTabs();
11098         }
11099     },
11100
11101     /**
11102      * Adds an existing {@link Roo.TabPanelItem}.
11103      * @param {Roo.TabPanelItem} item The TabPanelItem to add
11104      */
11105     addTabItem : function(item){
11106         this.items[item.id] = item;
11107         this.items.push(item);
11108         if(this.resizeTabs){
11109            item.setWidth(this.currentTabWidth || this.preferredTabWidth);
11110            this.autoSizeTabs();
11111         }else{
11112             item.autoSize();
11113         }
11114     },
11115
11116     /**
11117      * Removes a {@link Roo.TabPanelItem}.
11118      * @param {String/Number} id The id or index of the TabPanelItem to remove.
11119      */
11120     removeTab : function(id){
11121         var items = this.items;
11122         var tab = items[id];
11123         if(!tab) { return; }
11124         var index = items.indexOf(tab);
11125         if(this.active == tab && items.length > 1){
11126             var newTab = this.getNextAvailable(index);
11127             if(newTab) {
11128                 newTab.activate();
11129             }
11130         }
11131         this.stripEl.dom.removeChild(tab.pnode.dom);
11132         if(tab.bodyEl.dom.parentNode == this.bodyEl.dom){ // if it was moved already prevent error
11133             this.bodyEl.dom.removeChild(tab.bodyEl.dom);
11134         }
11135         items.splice(index, 1);
11136         delete this.items[tab.id];
11137         tab.fireEvent("close", tab);
11138         tab.purgeListeners();
11139         this.autoSizeTabs();
11140     },
11141
11142     getNextAvailable : function(start){
11143         var items = this.items;
11144         var index = start;
11145         // look for a next tab that will slide over to
11146         // replace the one being removed
11147         while(index < items.length){
11148             var item = items[++index];
11149             if(item && !item.isHidden()){
11150                 return item;
11151             }
11152         }
11153         // if one isn't found select the previous tab (on the left)
11154         index = start;
11155         while(index >= 0){
11156             var item = items[--index];
11157             if(item && !item.isHidden()){
11158                 return item;
11159             }
11160         }
11161         return null;
11162     },
11163
11164     /**
11165      * Disables a {@link Roo.TabPanelItem}. It cannot be the active tab, if it is this call is ignored.
11166      * @param {String/Number} id The id or index of the TabPanelItem to disable.
11167      */
11168     disableTab : function(id){
11169         var tab = this.items[id];
11170         if(tab && this.active != tab){
11171             tab.disable();
11172         }
11173     },
11174
11175     /**
11176      * Enables a {@link Roo.TabPanelItem} that is disabled.
11177      * @param {String/Number} id The id or index of the TabPanelItem to enable.
11178      */
11179     enableTab : function(id){
11180         var tab = this.items[id];
11181         tab.enable();
11182     },
11183
11184     /**
11185      * Activates a {@link Roo.TabPanelItem}. The currently active one will be deactivated.
11186      * @param {String/Number} id The id or index of the TabPanelItem to activate.
11187      * @return {Roo.TabPanelItem} The TabPanelItem.
11188      */
11189     activate : function(id){
11190         var tab = this.items[id];
11191         if(!tab){
11192             return null;
11193         }
11194         if(tab == this.active || tab.disabled){
11195             return tab;
11196         }
11197         var e = {};
11198         this.fireEvent("beforetabchange", this, e, tab);
11199         if(e.cancel !== true && !tab.disabled){
11200             if(this.active){
11201                 this.active.hide();
11202             }
11203             this.active = this.items[id];
11204             this.active.show();
11205             this.fireEvent("tabchange", this, this.active);
11206         }
11207         return tab;
11208     },
11209
11210     /**
11211      * Gets the active {@link Roo.TabPanelItem}.
11212      * @return {Roo.TabPanelItem} The active TabPanelItem or null if none are active.
11213      */
11214     getActiveTab : function(){
11215         return this.active;
11216     },
11217
11218     /**
11219      * Updates the tab body element to fit the height of the container element
11220      * for overflow scrolling
11221      * @param {Number} targetHeight (optional) Override the starting height from the elements height
11222      */
11223     syncHeight : function(targetHeight){
11224         var height = (targetHeight || this.el.getHeight())-this.el.getBorderWidth("tb")-this.el.getPadding("tb");
11225         var bm = this.bodyEl.getMargins();
11226         var newHeight = height-(this.stripWrap.getHeight()||0)-(bm.top+bm.bottom);
11227         this.bodyEl.setHeight(newHeight);
11228         return newHeight;
11229     },
11230
11231     onResize : function(){
11232         if(this.monitorResize){
11233             this.autoSizeTabs();
11234         }
11235     },
11236
11237     /**
11238      * Disables tab resizing while tabs are being added (if {@link #resizeTabs} is false this does nothing)
11239      */
11240     beginUpdate : function(){
11241         this.updating = true;
11242     },
11243
11244     /**
11245      * Stops an update and resizes the tabs (if {@link #resizeTabs} is false this does nothing)
11246      */
11247     endUpdate : function(){
11248         this.updating = false;
11249         this.autoSizeTabs();
11250     },
11251
11252     /**
11253      * Manual call to resize the tabs (if {@link #resizeTabs} is false this does nothing)
11254      */
11255     autoSizeTabs : function(){
11256         var count = this.items.length;
11257         var vcount = count - this.hiddenCount;
11258         if(!this.resizeTabs || count < 1 || vcount < 1 || this.updating) return;
11259         var w = Math.max(this.el.getWidth() - this.cpad, 10);
11260         var availWidth = Math.floor(w / vcount);
11261         var b = this.stripBody;
11262         if(b.getWidth() > w){
11263             var tabs = this.items;
11264             this.setTabWidth(Math.max(availWidth, this.minTabWidth)-2);
11265             if(availWidth < this.minTabWidth){
11266                 /*if(!this.sleft){    // incomplete scrolling code
11267                     this.createScrollButtons();
11268                 }
11269                 this.showScroll();
11270                 this.stripClip.setWidth(w - (this.sleft.getWidth()+this.sright.getWidth()));*/
11271             }
11272         }else{
11273             if(this.currentTabWidth < this.preferredTabWidth){
11274                 this.setTabWidth(Math.min(availWidth, this.preferredTabWidth)-2);
11275             }
11276         }
11277     },
11278
11279     /**
11280      * Returns the number of tabs in this TabPanel.
11281      * @return {Number}
11282      */
11283      getCount : function(){
11284          return this.items.length;
11285      },
11286
11287     /**
11288      * Resizes all the tabs to the passed width
11289      * @param {Number} The new width
11290      */
11291     setTabWidth : function(width){
11292         this.currentTabWidth = width;
11293         for(var i = 0, len = this.items.length; i < len; i++) {
11294                 if(!this.items[i].isHidden())this.items[i].setWidth(width);
11295         }
11296     },
11297
11298     /**
11299      * Destroys this TabPanel
11300      * @param {Boolean} removeEl (optional) True to remove the element from the DOM as well (defaults to undefined)
11301      */
11302     destroy : function(removeEl){
11303         Roo.EventManager.removeResizeListener(this.onResize, this);
11304         for(var i = 0, len = this.items.length; i < len; i++){
11305             this.items[i].purgeListeners();
11306         }
11307         if(removeEl === true){
11308             this.el.update("");
11309             this.el.remove();
11310         }
11311     }
11312 });
11313
11314 /**
11315  * @class Roo.TabPanelItem
11316  * @extends Roo.util.Observable
11317  * Represents an individual item (tab plus body) in a TabPanel.
11318  * @param {Roo.TabPanel} tabPanel The {@link Roo.TabPanel} this TabPanelItem belongs to
11319  * @param {String} id The id of this TabPanelItem
11320  * @param {String} text The text for the tab of this TabPanelItem
11321  * @param {Boolean} closable True to allow this TabPanelItem to be closable (defaults to false)
11322  */
11323 Roo.TabPanelItem = function(tabPanel, id, text, closable){
11324     /**
11325      * The {@link Roo.TabPanel} this TabPanelItem belongs to
11326      * @type Roo.TabPanel
11327      */
11328     this.tabPanel = tabPanel;
11329     /**
11330      * The id for this TabPanelItem
11331      * @type String
11332      */
11333     this.id = id;
11334     /** @private */
11335     this.disabled = false;
11336     /** @private */
11337     this.text = text;
11338     /** @private */
11339     this.loaded = false;
11340     this.closable = closable;
11341
11342     /**
11343      * The body element for this TabPanelItem.
11344      * @type Roo.Element
11345      */
11346     this.bodyEl = Roo.get(tabPanel.createItemBody(tabPanel.bodyEl.dom, id));
11347     this.bodyEl.setVisibilityMode(Roo.Element.VISIBILITY);
11348     this.bodyEl.setStyle("display", "block");
11349     this.bodyEl.setStyle("zoom", "1");
11350     this.hideAction();
11351
11352     var els = tabPanel.createStripElements(tabPanel.stripEl.dom, text, closable);
11353     /** @private */
11354     this.el = Roo.get(els.el, true);
11355     this.inner = Roo.get(els.inner, true);
11356     this.textEl = Roo.get(this.el.dom.firstChild.firstChild.firstChild, true);
11357     this.pnode = Roo.get(els.el.parentNode, true);
11358     this.el.on("mousedown", this.onTabMouseDown, this);
11359     this.el.on("click", this.onTabClick, this);
11360     /** @private */
11361     if(closable){
11362         var c = Roo.get(els.close, true);
11363         c.dom.title = this.closeText;
11364         c.addClassOnOver("close-over");
11365         c.on("click", this.closeClick, this);
11366      }
11367
11368     this.addEvents({
11369          /**
11370          * @event activate
11371          * Fires when this tab becomes the active tab.
11372          * @param {Roo.TabPanel} tabPanel The parent TabPanel
11373          * @param {Roo.TabPanelItem} this
11374          */
11375         "activate": true,
11376         /**
11377          * @event beforeclose
11378          * Fires before this tab is closed. To cancel the close, set cancel to true on e (e.cancel = true).
11379          * @param {Roo.TabPanelItem} this
11380          * @param {Object} e Set cancel to true on this object to cancel the close.
11381          */
11382         "beforeclose": true,
11383         /**
11384          * @event close
11385          * Fires when this tab is closed.
11386          * @param {Roo.TabPanelItem} this
11387          */
11388          "close": true,
11389         /**
11390          * @event deactivate
11391          * Fires when this tab is no longer the active tab.
11392          * @param {Roo.TabPanel} tabPanel The parent TabPanel
11393          * @param {Roo.TabPanelItem} this
11394          */
11395          "deactivate" : true
11396     });
11397     this.hidden = false;
11398
11399     Roo.TabPanelItem.superclass.constructor.call(this);
11400 };
11401
11402 Roo.extend(Roo.TabPanelItem, Roo.util.Observable, {
11403     purgeListeners : function(){
11404        Roo.util.Observable.prototype.purgeListeners.call(this);
11405        this.el.removeAllListeners();
11406     },
11407     /**
11408      * Shows this TabPanelItem -- this <b>does not</b> deactivate the currently active TabPanelItem.
11409      */
11410     show : function(){
11411         this.pnode.addClass("on");
11412         this.showAction();
11413         if(Roo.isOpera){
11414             this.tabPanel.stripWrap.repaint();
11415         }
11416         this.fireEvent("activate", this.tabPanel, this);
11417     },
11418
11419     /**
11420      * Returns true if this tab is the active tab.
11421      * @return {Boolean}
11422      */
11423     isActive : function(){
11424         return this.tabPanel.getActiveTab() == this;
11425     },
11426
11427     /**
11428      * Hides this TabPanelItem -- if you don't activate another TabPanelItem this could look odd.
11429      */
11430     hide : function(){
11431         this.pnode.removeClass("on");
11432         this.hideAction();
11433         this.fireEvent("deactivate", this.tabPanel, this);
11434     },
11435
11436     hideAction : function(){
11437         this.bodyEl.hide();
11438         this.bodyEl.setStyle("position", "absolute");
11439         this.bodyEl.setLeft("-20000px");
11440         this.bodyEl.setTop("-20000px");
11441     },
11442
11443     showAction : function(){
11444         this.bodyEl.setStyle("position", "relative");
11445         this.bodyEl.setTop("");
11446         this.bodyEl.setLeft("");
11447         this.bodyEl.show();
11448     },
11449
11450     /**
11451      * Set the tooltip for the tab.
11452      * @param {String} tooltip The tab's tooltip
11453      */
11454     setTooltip : function(text){
11455         if(Roo.QuickTips && Roo.QuickTips.isEnabled()){
11456             this.textEl.dom.qtip = text;
11457             this.textEl.dom.removeAttribute('title');
11458         }else{
11459             this.textEl.dom.title = text;
11460         }
11461     },
11462
11463     onTabClick : function(e){
11464         e.preventDefault();
11465         this.tabPanel.activate(this.id);
11466     },
11467
11468     onTabMouseDown : function(e){
11469         e.preventDefault();
11470         this.tabPanel.activate(this.id);
11471     },
11472
11473     getWidth : function(){
11474         return this.inner.getWidth();
11475     },
11476
11477     setWidth : function(width){
11478         var iwidth = width - this.pnode.getPadding("lr");
11479         this.inner.setWidth(iwidth);
11480         this.textEl.setWidth(iwidth-this.inner.getPadding("lr"));
11481         this.pnode.setWidth(width);
11482     },
11483
11484     /**
11485      * Show or hide the tab
11486      * @param {Boolean} hidden True to hide or false to show.
11487      */
11488     setHidden : function(hidden){
11489         this.hidden = hidden;
11490         this.pnode.setStyle("display", hidden ? "none" : "");
11491     },
11492
11493     /**
11494      * Returns true if this tab is "hidden"
11495      * @return {Boolean}
11496      */
11497     isHidden : function(){
11498         return this.hidden;
11499     },
11500
11501     /**
11502      * Returns the text for this tab
11503      * @return {String}
11504      */
11505     getText : function(){
11506         return this.text;
11507     },
11508
11509     autoSize : function(){
11510         //this.el.beginMeasure();
11511         this.textEl.setWidth(1);
11512         this.setWidth(this.textEl.dom.scrollWidth+this.pnode.getPadding("lr")+this.inner.getPadding("lr"));
11513         //this.el.endMeasure();
11514     },
11515
11516     /**
11517      * Sets the text for the tab (Note: this also sets the tooltip text)
11518      * @param {String} text The tab's text and tooltip
11519      */
11520     setText : function(text){
11521         this.text = text;
11522         this.textEl.update(text);
11523         this.setTooltip(text);
11524         if(!this.tabPanel.resizeTabs){
11525             this.autoSize();
11526         }
11527     },
11528     /**
11529      * Activates this TabPanelItem -- this <b>does</b> deactivate the currently active TabPanelItem.
11530      */
11531     activate : function(){
11532         this.tabPanel.activate(this.id);
11533     },
11534
11535     /**
11536      * Disables this TabPanelItem -- this does nothing if this is the active TabPanelItem.
11537      */
11538     disable : function(){
11539         if(this.tabPanel.active != this){
11540             this.disabled = true;
11541             this.pnode.addClass("disabled");
11542         }
11543     },
11544
11545     /**
11546      * Enables this TabPanelItem if it was previously disabled.
11547      */
11548     enable : function(){
11549         this.disabled = false;
11550         this.pnode.removeClass("disabled");
11551     },
11552
11553     /**
11554      * Sets the content for this TabPanelItem.
11555      * @param {String} content The content
11556      * @param {Boolean} loadScripts true to look for and load scripts
11557      */
11558     setContent : function(content, loadScripts){
11559         this.bodyEl.update(content, loadScripts);
11560     },
11561
11562     /**
11563      * Gets the {@link Roo.UpdateManager} for the body of this TabPanelItem. Enables you to perform Ajax updates.
11564      * @return {Roo.UpdateManager} The UpdateManager
11565      */
11566     getUpdateManager : function(){
11567         return this.bodyEl.getUpdateManager();
11568     },
11569
11570     /**
11571      * Set a URL to be used to load the content for this TabPanelItem.
11572      * @param {String/Function} url The URL to load the content from, or a function to call to get the URL
11573      * @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)
11574      * @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)
11575      * @return {Roo.UpdateManager} The UpdateManager
11576      */
11577     setUrl : function(url, params, loadOnce){
11578         if(this.refreshDelegate){
11579             this.un('activate', this.refreshDelegate);
11580         }
11581         this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
11582         this.on("activate", this.refreshDelegate);
11583         return this.bodyEl.getUpdateManager();
11584     },
11585
11586     /** @private */
11587     _handleRefresh : function(url, params, loadOnce){
11588         if(!loadOnce || !this.loaded){
11589             var updater = this.bodyEl.getUpdateManager();
11590             updater.update(url, params, this._setLoaded.createDelegate(this));
11591         }
11592     },
11593
11594     /**
11595      *   Forces a content refresh from the URL specified in the {@link #setUrl} method.
11596      *   Will fail silently if the setUrl method has not been called.
11597      *   This does not activate the panel, just updates its content.
11598      */
11599     refresh : function(){
11600         if(this.refreshDelegate){
11601            this.loaded = false;
11602            this.refreshDelegate();
11603         }
11604     },
11605
11606     /** @private */
11607     _setLoaded : function(){
11608         this.loaded = true;
11609     },
11610
11611     /** @private */
11612     closeClick : function(e){
11613         var o = {};
11614         e.stopEvent();
11615         this.fireEvent("beforeclose", this, o);
11616         if(o.cancel !== true){
11617             this.tabPanel.removeTab(this.id);
11618         }
11619     },
11620     /**
11621      * The text displayed in the tooltip for the close icon.
11622      * @type String
11623      */
11624     closeText : "Close this tab"
11625 });
11626
11627 /** @private */
11628 Roo.TabPanel.prototype.createStrip = function(container){
11629     var strip = document.createElement("div");
11630     strip.className = "x-tabs-wrap";
11631     container.appendChild(strip);
11632     return strip;
11633 };
11634 /** @private */
11635 Roo.TabPanel.prototype.createStripList = function(strip){
11636     // div wrapper for retard IE
11637     // returns the "tr" element.
11638     strip.innerHTML = '<div class="x-tabs-strip-wrap">'+
11639         '<table class="x-tabs-strip" cellspacing="0" cellpadding="0" border="0"><tbody><tr>'+
11640         '<td class="x-tab-strip-toolbar"></td></tr></tbody></table></div>';
11641     return strip.firstChild.firstChild.firstChild.firstChild;
11642 };
11643 /** @private */
11644 Roo.TabPanel.prototype.createBody = function(container){
11645     var body = document.createElement("div");
11646     Roo.id(body, "tab-body");
11647     Roo.fly(body).addClass("x-tabs-body");
11648     container.appendChild(body);
11649     return body;
11650 };
11651 /** @private */
11652 Roo.TabPanel.prototype.createItemBody = function(bodyEl, id){
11653     var body = Roo.getDom(id);
11654     if(!body){
11655         body = document.createElement("div");
11656         body.id = id;
11657     }
11658     Roo.fly(body).addClass("x-tabs-item-body");
11659     bodyEl.insertBefore(body, bodyEl.firstChild);
11660     return body;
11661 };
11662 /** @private */
11663 Roo.TabPanel.prototype.createStripElements = function(stripEl, text, closable){
11664     var td = document.createElement("td");
11665     stripEl.insertBefore(td, stripEl.childNodes[stripEl.childNodes.length-1]);
11666     //stripEl.appendChild(td);
11667     if(closable){
11668         td.className = "x-tabs-closable";
11669         if(!this.closeTpl){
11670             this.closeTpl = new Roo.Template(
11671                '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
11672                '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span>' +
11673                '<div unselectable="on" class="close-icon">&#160;</div></em></span></a>'
11674             );
11675         }
11676         var el = this.closeTpl.overwrite(td, {"text": text});
11677         var close = el.getElementsByTagName("div")[0];
11678         var inner = el.getElementsByTagName("em")[0];
11679         return {"el": el, "close": close, "inner": inner};
11680     } else {
11681         if(!this.tabTpl){
11682             this.tabTpl = new Roo.Template(
11683                '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
11684                '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span></em></span></a>'
11685             );
11686         }
11687         var el = this.tabTpl.overwrite(td, {"text": text});
11688         var inner = el.getElementsByTagName("em")[0];
11689         return {"el": el, "inner": inner};
11690     }
11691 };/*
11692  * Based on:
11693  * Ext JS Library 1.1.1
11694  * Copyright(c) 2006-2007, Ext JS, LLC.
11695  *
11696  * Originally Released Under LGPL - original licence link has changed is not relivant.
11697  *
11698  * Fork - LGPL
11699  * <script type="text/javascript">
11700  */
11701
11702 /**
11703  * @class Roo.Button
11704  * @extends Roo.util.Observable
11705  * Simple Button class
11706  * @cfg {String} text The button text
11707  * @cfg {String} icon The path to an image to display in the button (the image will be set as the background-image
11708  * CSS property of the button by default, so if you want a mixed icon/text button, set cls:"x-btn-text-icon")
11709  * @cfg {Function} handler A function called when the button is clicked (can be used instead of click event)
11710  * @cfg {Object} scope The scope of the handler
11711  * @cfg {Number} minWidth The minimum width for this button (used to give a set of buttons a common width)
11712  * @cfg {String/Object} tooltip The tooltip for the button - can be a string or QuickTips config object
11713  * @cfg {Boolean} hidden True to start hidden (defaults to false)
11714  * @cfg {Boolean} disabled True to start disabled (defaults to false)
11715  * @cfg {Boolean} pressed True to start pressed (only if enableToggle = true)
11716  * @cfg {String} toggleGroup The group this toggle button is a member of (only 1 per group can be pressed, only
11717    applies if enableToggle = true)
11718  * @cfg {String/HTMLElement/Element} renderTo The element to append the button to
11719  * @cfg {Boolean/Object} repeat True to repeat fire the click event while the mouse is down. This can also be
11720   an {@link Roo.util.ClickRepeater} config object (defaults to false).
11721  * @constructor
11722  * Create a new button
11723  * @param {Object} config The config object
11724  */
11725 Roo.Button = function(renderTo, config)
11726 {
11727     if (!config) {
11728         config = renderTo;
11729         renderTo = config.renderTo || false;
11730     }
11731     
11732     Roo.apply(this, config);
11733     this.addEvents({
11734         /**
11735              * @event click
11736              * Fires when this button is clicked
11737              * @param {Button} this
11738              * @param {EventObject} e The click event
11739              */
11740             "click" : true,
11741         /**
11742              * @event toggle
11743              * Fires when the "pressed" state of this button changes (only if enableToggle = true)
11744              * @param {Button} this
11745              * @param {Boolean} pressed
11746              */
11747             "toggle" : true,
11748         /**
11749              * @event mouseover
11750              * Fires when the mouse hovers over the button
11751              * @param {Button} this
11752              * @param {Event} e The event object
11753              */
11754         'mouseover' : true,
11755         /**
11756              * @event mouseout
11757              * Fires when the mouse exits the button
11758              * @param {Button} this
11759              * @param {Event} e The event object
11760              */
11761         'mouseout': true,
11762          /**
11763              * @event render
11764              * Fires when the button is rendered
11765              * @param {Button} this
11766              */
11767         'render': true
11768     });
11769     if(this.menu){
11770         this.menu = Roo.menu.MenuMgr.get(this.menu);
11771     }
11772     // register listeners first!!  - so render can be captured..
11773     Roo.util.Observable.call(this);
11774     if(renderTo){
11775         this.render(renderTo);
11776     }
11777     
11778   
11779 };
11780
11781 Roo.extend(Roo.Button, Roo.util.Observable, {
11782     /**
11783      * 
11784      */
11785     
11786     /**
11787      * Read-only. True if this button is hidden
11788      * @type Boolean
11789      */
11790     hidden : false,
11791     /**
11792      * Read-only. True if this button is disabled
11793      * @type Boolean
11794      */
11795     disabled : false,
11796     /**
11797      * Read-only. True if this button is pressed (only if enableToggle = true)
11798      * @type Boolean
11799      */
11800     pressed : false,
11801
11802     /**
11803      * @cfg {Number} tabIndex 
11804      * The DOM tabIndex for this button (defaults to undefined)
11805      */
11806     tabIndex : undefined,
11807
11808     /**
11809      * @cfg {Boolean} enableToggle
11810      * True to enable pressed/not pressed toggling (defaults to false)
11811      */
11812     enableToggle: false,
11813     /**
11814      * @cfg {Mixed} menu
11815      * Standard menu attribute consisting of a reference to a menu object, a menu id or a menu config blob (defaults to undefined).
11816      */
11817     menu : undefined,
11818     /**
11819      * @cfg {String} menuAlign
11820      * The position to align the menu to (see {@link Roo.Element#alignTo} for more details, defaults to 'tl-bl?').
11821      */
11822     menuAlign : "tl-bl?",
11823
11824     /**
11825      * @cfg {String} iconCls
11826      * A css class which sets a background image to be used as the icon for this button (defaults to undefined).
11827      */
11828     iconCls : undefined,
11829     /**
11830      * @cfg {String} type
11831      * The button's type, corresponding to the DOM input element type attribute.  Either "submit," "reset" or "button" (default).
11832      */
11833     type : 'button',
11834
11835     // private
11836     menuClassTarget: 'tr',
11837
11838     /**
11839      * @cfg {String} clickEvent
11840      * The type of event to map to the button's event handler (defaults to 'click')
11841      */
11842     clickEvent : 'click',
11843
11844     /**
11845      * @cfg {Boolean} handleMouseEvents
11846      * False to disable visual cues on mouseover, mouseout and mousedown (defaults to true)
11847      */
11848     handleMouseEvents : true,
11849
11850     /**
11851      * @cfg {String} tooltipType
11852      * The type of tooltip to use. Either "qtip" (default) for QuickTips or "title" for title attribute.
11853      */
11854     tooltipType : 'qtip',
11855
11856     /**
11857      * @cfg {String} cls
11858      * A CSS class to apply to the button's main element.
11859      */
11860     
11861     /**
11862      * @cfg {Roo.Template} template (Optional)
11863      * An {@link Roo.Template} with which to create the Button's main element. This Template must
11864      * contain numeric substitution parameter 0 if it is to display the tRoo property. Changing the template could
11865      * require code modifications if required elements (e.g. a button) aren't present.
11866      */
11867
11868     // private
11869     render : function(renderTo){
11870         var btn;
11871         if(this.hideParent){
11872             this.parentEl = Roo.get(renderTo);
11873         }
11874         if(!this.dhconfig){
11875             if(!this.template){
11876                 if(!Roo.Button.buttonTemplate){
11877                     // hideous table template
11878                     Roo.Button.buttonTemplate = new Roo.Template(
11879                         '<table border="0" cellpadding="0" cellspacing="0" class="x-btn-wrap"><tbody><tr>',
11880                         '<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>',
11881                         "</tr></tbody></table>");
11882                 }
11883                 this.template = Roo.Button.buttonTemplate;
11884             }
11885             btn = this.template.append(renderTo, [this.text || '&#160;', this.type], true);
11886             var btnEl = btn.child("button:first");
11887             btnEl.on('focus', this.onFocus, this);
11888             btnEl.on('blur', this.onBlur, this);
11889             if(this.cls){
11890                 btn.addClass(this.cls);
11891             }
11892             if(this.icon){
11893                 btnEl.setStyle('background-image', 'url(' +this.icon +')');
11894             }
11895             if(this.iconCls){
11896                 btnEl.addClass(this.iconCls);
11897                 if(!this.cls){
11898                     btn.addClass(this.text ? 'x-btn-text-icon' : 'x-btn-icon');
11899                 }
11900             }
11901             if(this.tabIndex !== undefined){
11902                 btnEl.dom.tabIndex = this.tabIndex;
11903             }
11904             if(this.tooltip){
11905                 if(typeof this.tooltip == 'object'){
11906                     Roo.QuickTips.tips(Roo.apply({
11907                           target: btnEl.id
11908                     }, this.tooltip));
11909                 } else {
11910                     btnEl.dom[this.tooltipType] = this.tooltip;
11911                 }
11912             }
11913         }else{
11914             btn = Roo.DomHelper.append(Roo.get(renderTo).dom, this.dhconfig, true);
11915         }
11916         this.el = btn;
11917         if(this.id){
11918             this.el.dom.id = this.el.id = this.id;
11919         }
11920         if(this.menu){
11921             this.el.child(this.menuClassTarget).addClass("x-btn-with-menu");
11922             this.menu.on("show", this.onMenuShow, this);
11923             this.menu.on("hide", this.onMenuHide, this);
11924         }
11925         btn.addClass("x-btn");
11926         if(Roo.isIE && !Roo.isIE7){
11927             this.autoWidth.defer(1, this);
11928         }else{
11929             this.autoWidth();
11930         }
11931         if(this.handleMouseEvents){
11932             btn.on("mouseover", this.onMouseOver, this);
11933             btn.on("mouseout", this.onMouseOut, this);
11934             btn.on("mousedown", this.onMouseDown, this);
11935         }
11936         btn.on(this.clickEvent, this.onClick, this);
11937         //btn.on("mouseup", this.onMouseUp, this);
11938         if(this.hidden){
11939             this.hide();
11940         }
11941         if(this.disabled){
11942             this.disable();
11943         }
11944         Roo.ButtonToggleMgr.register(this);
11945         if(this.pressed){
11946             this.el.addClass("x-btn-pressed");
11947         }
11948         if(this.repeat){
11949             var repeater = new Roo.util.ClickRepeater(btn,
11950                 typeof this.repeat == "object" ? this.repeat : {}
11951             );
11952             repeater.on("click", this.onClick,  this);
11953         }
11954         
11955         this.fireEvent('render', this);
11956         
11957     },
11958     /**
11959      * Returns the button's underlying element
11960      * @return {Roo.Element} The element
11961      */
11962     getEl : function(){
11963         return this.el;  
11964     },
11965     
11966     /**
11967      * Destroys this Button and removes any listeners.
11968      */
11969     destroy : function(){
11970         Roo.ButtonToggleMgr.unregister(this);
11971         this.el.removeAllListeners();
11972         this.purgeListeners();
11973         this.el.remove();
11974     },
11975
11976     // private
11977     autoWidth : function(){
11978         if(this.el){
11979             this.el.setWidth("auto");
11980             if(Roo.isIE7 && Roo.isStrict){
11981                 var ib = this.el.child('button');
11982                 if(ib && ib.getWidth() > 20){
11983                     ib.clip();
11984                     ib.setWidth(Roo.util.TextMetrics.measure(ib, this.text).width+ib.getFrameWidth('lr'));
11985                 }
11986             }
11987             if(this.minWidth){
11988                 if(this.hidden){
11989                     this.el.beginMeasure();
11990                 }
11991                 if(this.el.getWidth() < this.minWidth){
11992                     this.el.setWidth(this.minWidth);
11993                 }
11994                 if(this.hidden){
11995                     this.el.endMeasure();
11996                 }
11997             }
11998         }
11999     },
12000
12001     /**
12002      * Assigns this button's click handler
12003      * @param {Function} handler The function to call when the button is clicked
12004      * @param {Object} scope (optional) Scope for the function passed in
12005      */
12006     setHandler : function(handler, scope){
12007         this.handler = handler;
12008         this.scope = scope;  
12009     },
12010     
12011     /**
12012      * Sets this button's text
12013      * @param {String} text The button text
12014      */
12015     setText : function(text){
12016         this.text = text;
12017         if(this.el){
12018             this.el.child("td.x-btn-center button.x-btn-text").update(text);
12019         }
12020         this.autoWidth();
12021     },
12022     
12023     /**
12024      * Gets the text for this button
12025      * @return {String} The button text
12026      */
12027     getText : function(){
12028         return this.text;  
12029     },
12030     
12031     /**
12032      * Show this button
12033      */
12034     show: function(){
12035         this.hidden = false;
12036         if(this.el){
12037             this[this.hideParent? 'parentEl' : 'el'].setStyle("display", "");
12038         }
12039     },
12040     
12041     /**
12042      * Hide this button
12043      */
12044     hide: function(){
12045         this.hidden = true;
12046         if(this.el){
12047             this[this.hideParent? 'parentEl' : 'el'].setStyle("display", "none");
12048         }
12049     },
12050     
12051     /**
12052      * Convenience function for boolean show/hide
12053      * @param {Boolean} visible True to show, false to hide
12054      */
12055     setVisible: function(visible){
12056         if(visible) {
12057             this.show();
12058         }else{
12059             this.hide();
12060         }
12061     },
12062     
12063     /**
12064      * If a state it passed, it becomes the pressed state otherwise the current state is toggled.
12065      * @param {Boolean} state (optional) Force a particular state
12066      */
12067     toggle : function(state){
12068         state = state === undefined ? !this.pressed : state;
12069         if(state != this.pressed){
12070             if(state){
12071                 this.el.addClass("x-btn-pressed");
12072                 this.pressed = true;
12073                 this.fireEvent("toggle", this, true);
12074             }else{
12075                 this.el.removeClass("x-btn-pressed");
12076                 this.pressed = false;
12077                 this.fireEvent("toggle", this, false);
12078             }
12079             if(this.toggleHandler){
12080                 this.toggleHandler.call(this.scope || this, this, state);
12081             }
12082         }
12083     },
12084     
12085     /**
12086      * Focus the button
12087      */
12088     focus : function(){
12089         this.el.child('button:first').focus();
12090     },
12091     
12092     /**
12093      * Disable this button
12094      */
12095     disable : function(){
12096         if(this.el){
12097             this.el.addClass("x-btn-disabled");
12098         }
12099         this.disabled = true;
12100     },
12101     
12102     /**
12103      * Enable this button
12104      */
12105     enable : function(){
12106         if(this.el){
12107             this.el.removeClass("x-btn-disabled");
12108         }
12109         this.disabled = false;
12110     },
12111
12112     /**
12113      * Convenience function for boolean enable/disable
12114      * @param {Boolean} enabled True to enable, false to disable
12115      */
12116     setDisabled : function(v){
12117         this[v !== true ? "enable" : "disable"]();
12118     },
12119
12120     // private
12121     onClick : function(e){
12122         if(e){
12123             e.preventDefault();
12124         }
12125         if(e.button != 0){
12126             return;
12127         }
12128         if(!this.disabled){
12129             if(this.enableToggle){
12130                 this.toggle();
12131             }
12132             if(this.menu && !this.menu.isVisible()){
12133                 this.menu.show(this.el, this.menuAlign);
12134             }
12135             this.fireEvent("click", this, e);
12136             if(this.handler){
12137                 this.el.removeClass("x-btn-over");
12138                 this.handler.call(this.scope || this, this, e);
12139             }
12140         }
12141     },
12142     // private
12143     onMouseOver : function(e){
12144         if(!this.disabled){
12145             this.el.addClass("x-btn-over");
12146             this.fireEvent('mouseover', this, e);
12147         }
12148     },
12149     // private
12150     onMouseOut : function(e){
12151         if(!e.within(this.el,  true)){
12152             this.el.removeClass("x-btn-over");
12153             this.fireEvent('mouseout', this, e);
12154         }
12155     },
12156     // private
12157     onFocus : function(e){
12158         if(!this.disabled){
12159             this.el.addClass("x-btn-focus");
12160         }
12161     },
12162     // private
12163     onBlur : function(e){
12164         this.el.removeClass("x-btn-focus");
12165     },
12166     // private
12167     onMouseDown : function(e){
12168         if(!this.disabled && e.button == 0){
12169             this.el.addClass("x-btn-click");
12170             Roo.get(document).on('mouseup', this.onMouseUp, this);
12171         }
12172     },
12173     // private
12174     onMouseUp : function(e){
12175         if(e.button == 0){
12176             this.el.removeClass("x-btn-click");
12177             Roo.get(document).un('mouseup', this.onMouseUp, this);
12178         }
12179     },
12180     // private
12181     onMenuShow : function(e){
12182         this.el.addClass("x-btn-menu-active");
12183     },
12184     // private
12185     onMenuHide : function(e){
12186         this.el.removeClass("x-btn-menu-active");
12187     }   
12188 });
12189
12190 // Private utility class used by Button
12191 Roo.ButtonToggleMgr = function(){
12192    var groups = {};
12193    
12194    function toggleGroup(btn, state){
12195        if(state){
12196            var g = groups[btn.toggleGroup];
12197            for(var i = 0, l = g.length; i < l; i++){
12198                if(g[i] != btn){
12199                    g[i].toggle(false);
12200                }
12201            }
12202        }
12203    }
12204    
12205    return {
12206        register : function(btn){
12207            if(!btn.toggleGroup){
12208                return;
12209            }
12210            var g = groups[btn.toggleGroup];
12211            if(!g){
12212                g = groups[btn.toggleGroup] = [];
12213            }
12214            g.push(btn);
12215            btn.on("toggle", toggleGroup);
12216        },
12217        
12218        unregister : function(btn){
12219            if(!btn.toggleGroup){
12220                return;
12221            }
12222            var g = groups[btn.toggleGroup];
12223            if(g){
12224                g.remove(btn);
12225                btn.un("toggle", toggleGroup);
12226            }
12227        }
12228    };
12229 }();/*
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.SplitButton
12242  * @extends Roo.Button
12243  * A split button that provides a built-in dropdown arrow that can fire an event separately from the default
12244  * click event of the button.  Typically this would be used to display a dropdown menu that provides additional
12245  * options to the primary button action, but any custom handler can provide the arrowclick implementation.
12246  * @cfg {Function} arrowHandler A function called when the arrow button is clicked (can be used instead of click event)
12247  * @cfg {String} arrowTooltip The title attribute of the arrow
12248  * @constructor
12249  * Create a new menu button
12250  * @param {String/HTMLElement/Element} renderTo The element to append the button to
12251  * @param {Object} config The config object
12252  */
12253 Roo.SplitButton = function(renderTo, config){
12254     Roo.SplitButton.superclass.constructor.call(this, renderTo, config);
12255     /**
12256      * @event arrowclick
12257      * Fires when this button's arrow is clicked
12258      * @param {SplitButton} this
12259      * @param {EventObject} e The click event
12260      */
12261     this.addEvents({"arrowclick":true});
12262 };
12263
12264 Roo.extend(Roo.SplitButton, Roo.Button, {
12265     render : function(renderTo){
12266         // this is one sweet looking template!
12267         var tpl = new Roo.Template(
12268             '<table cellspacing="0" class="x-btn-menu-wrap x-btn"><tr><td>',
12269             '<table cellspacing="0" class="x-btn-wrap x-btn-menu-text-wrap"><tbody>',
12270             '<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>',
12271             "</tbody></table></td><td>",
12272             '<table cellspacing="0" class="x-btn-wrap x-btn-menu-arrow-wrap"><tbody>',
12273             '<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>',
12274             "</tbody></table></td></tr></table>"
12275         );
12276         var btn = tpl.append(renderTo, [this.text, this.type], true);
12277         var btnEl = btn.child("button");
12278         if(this.cls){
12279             btn.addClass(this.cls);
12280         }
12281         if(this.icon){
12282             btnEl.setStyle('background-image', 'url(' +this.icon +')');
12283         }
12284         if(this.iconCls){
12285             btnEl.addClass(this.iconCls);
12286             if(!this.cls){
12287                 btn.addClass(this.text ? 'x-btn-text-icon' : 'x-btn-icon');
12288             }
12289         }
12290         this.el = btn;
12291         if(this.handleMouseEvents){
12292             btn.on("mouseover", this.onMouseOver, this);
12293             btn.on("mouseout", this.onMouseOut, this);
12294             btn.on("mousedown", this.onMouseDown, this);
12295             btn.on("mouseup", this.onMouseUp, this);
12296         }
12297         btn.on(this.clickEvent, this.onClick, this);
12298         if(this.tooltip){
12299             if(typeof this.tooltip == 'object'){
12300                 Roo.QuickTips.tips(Roo.apply({
12301                       target: btnEl.id
12302                 }, this.tooltip));
12303             } else {
12304                 btnEl.dom[this.tooltipType] = this.tooltip;
12305             }
12306         }
12307         if(this.arrowTooltip){
12308             btn.child("button:nth(2)").dom[this.tooltipType] = this.arrowTooltip;
12309         }
12310         if(this.hidden){
12311             this.hide();
12312         }
12313         if(this.disabled){
12314             this.disable();
12315         }
12316         if(this.pressed){
12317             this.el.addClass("x-btn-pressed");
12318         }
12319         if(Roo.isIE && !Roo.isIE7){
12320             this.autoWidth.defer(1, this);
12321         }else{
12322             this.autoWidth();
12323         }
12324         if(this.menu){
12325             this.menu.on("show", this.onMenuShow, this);
12326             this.menu.on("hide", this.onMenuHide, this);
12327         }
12328         this.fireEvent('render', this);
12329     },
12330
12331     // private
12332     autoWidth : function(){
12333         if(this.el){
12334             var tbl = this.el.child("table:first");
12335             var tbl2 = this.el.child("table:last");
12336             this.el.setWidth("auto");
12337             tbl.setWidth("auto");
12338             if(Roo.isIE7 && Roo.isStrict){
12339                 var ib = this.el.child('button:first');
12340                 if(ib && ib.getWidth() > 20){
12341                     ib.clip();
12342                     ib.setWidth(Roo.util.TextMetrics.measure(ib, this.text).width+ib.getFrameWidth('lr'));
12343                 }
12344             }
12345             if(this.minWidth){
12346                 if(this.hidden){
12347                     this.el.beginMeasure();
12348                 }
12349                 if((tbl.getWidth()+tbl2.getWidth()) < this.minWidth){
12350                     tbl.setWidth(this.minWidth-tbl2.getWidth());
12351                 }
12352                 if(this.hidden){
12353                     this.el.endMeasure();
12354                 }
12355             }
12356             this.el.setWidth(tbl.getWidth()+tbl2.getWidth());
12357         } 
12358     },
12359     /**
12360      * Sets this button's click handler
12361      * @param {Function} handler The function to call when the button is clicked
12362      * @param {Object} scope (optional) Scope for the function passed above
12363      */
12364     setHandler : function(handler, scope){
12365         this.handler = handler;
12366         this.scope = scope;  
12367     },
12368     
12369     /**
12370      * Sets this button's arrow click handler
12371      * @param {Function} handler The function to call when the arrow is clicked
12372      * @param {Object} scope (optional) Scope for the function passed above
12373      */
12374     setArrowHandler : function(handler, scope){
12375         this.arrowHandler = handler;
12376         this.scope = scope;  
12377     },
12378     
12379     /**
12380      * Focus the button
12381      */
12382     focus : function(){
12383         if(this.el){
12384             this.el.child("button:first").focus();
12385         }
12386     },
12387
12388     // private
12389     onClick : function(e){
12390         e.preventDefault();
12391         if(!this.disabled){
12392             if(e.getTarget(".x-btn-menu-arrow-wrap")){
12393                 if(this.menu && !this.menu.isVisible()){
12394                     this.menu.show(this.el, this.menuAlign);
12395                 }
12396                 this.fireEvent("arrowclick", this, e);
12397                 if(this.arrowHandler){
12398                     this.arrowHandler.call(this.scope || this, this, e);
12399                 }
12400             }else{
12401                 this.fireEvent("click", this, e);
12402                 if(this.handler){
12403                     this.handler.call(this.scope || this, this, e);
12404                 }
12405             }
12406         }
12407     },
12408     // private
12409     onMouseDown : function(e){
12410         if(!this.disabled){
12411             Roo.fly(e.getTarget("table")).addClass("x-btn-click");
12412         }
12413     },
12414     // private
12415     onMouseUp : function(e){
12416         Roo.fly(e.getTarget("table")).removeClass("x-btn-click");
12417     }   
12418 });
12419
12420
12421 // backwards compat
12422 Roo.MenuButton = Roo.SplitButton;/*
12423  * Based on:
12424  * Ext JS Library 1.1.1
12425  * Copyright(c) 2006-2007, Ext JS, LLC.
12426  *
12427  * Originally Released Under LGPL - original licence link has changed is not relivant.
12428  *
12429  * Fork - LGPL
12430  * <script type="text/javascript">
12431  */
12432
12433 /**
12434  * @class Roo.Toolbar
12435  * Basic Toolbar class.
12436  * @constructor
12437  * Creates a new Toolbar
12438  * @param {Object} container The config object
12439  */ 
12440 Roo.Toolbar = function(container, buttons, config)
12441 {
12442     /// old consturctor format still supported..
12443     if(container instanceof Array){ // omit the container for later rendering
12444         buttons = container;
12445         config = buttons;
12446         container = null;
12447     }
12448     if (typeof(container) == 'object' && container.xtype) {
12449         config = container;
12450         container = config.container;
12451         buttons = config.buttons || []; // not really - use items!!
12452     }
12453     var xitems = [];
12454     if (config && config.items) {
12455         xitems = config.items;
12456         delete config.items;
12457     }
12458     Roo.apply(this, config);
12459     this.buttons = buttons;
12460     
12461     if(container){
12462         this.render(container);
12463     }
12464     this.xitems = xitems;
12465     Roo.each(xitems, function(b) {
12466         this.add(b);
12467     }, this);
12468     
12469 };
12470
12471 Roo.Toolbar.prototype = {
12472     /**
12473      * @cfg {Array} items
12474      * array of button configs or elements to add (will be converted to a MixedCollection)
12475      */
12476     
12477     /**
12478      * @cfg {String/HTMLElement/Element} container
12479      * The id or element that will contain the toolbar
12480      */
12481     // private
12482     render : function(ct){
12483         this.el = Roo.get(ct);
12484         if(this.cls){
12485             this.el.addClass(this.cls);
12486         }
12487         // using a table allows for vertical alignment
12488         // 100% width is needed by Safari...
12489         this.el.update('<div class="x-toolbar x-small-editor"><table cellspacing="0"><tr></tr></table></div>');
12490         this.tr = this.el.child("tr", true);
12491         var autoId = 0;
12492         this.items = new Roo.util.MixedCollection(false, function(o){
12493             return o.id || ("item" + (++autoId));
12494         });
12495         if(this.buttons){
12496             this.add.apply(this, this.buttons);
12497             delete this.buttons;
12498         }
12499     },
12500
12501     /**
12502      * Adds element(s) to the toolbar -- this function takes a variable number of 
12503      * arguments of mixed type and adds them to the toolbar.
12504      * @param {Mixed} arg1 The following types of arguments are all valid:<br />
12505      * <ul>
12506      * <li>{@link Roo.Toolbar.Button} config: A valid button config object (equivalent to {@link #addButton})</li>
12507      * <li>HtmlElement: Any standard HTML element (equivalent to {@link #addElement})</li>
12508      * <li>Field: Any form field (equivalent to {@link #addField})</li>
12509      * <li>Item: Any subclass of {@link Roo.Toolbar.Item} (equivalent to {@link #addItem})</li>
12510      * <li>String: Any generic string (gets wrapped in a {@link Roo.Toolbar.TextItem}, equivalent to {@link #addText}).
12511      * Note that there are a few special strings that are treated differently as explained nRoo.</li>
12512      * <li>'separator' or '-': Creates a separator element (equivalent to {@link #addSeparator})</li>
12513      * <li>' ': Creates a spacer element (equivalent to {@link #addSpacer})</li>
12514      * <li>'->': Creates a fill element (equivalent to {@link #addFill})</li>
12515      * </ul>
12516      * @param {Mixed} arg2
12517      * @param {Mixed} etc.
12518      */
12519     add : function(){
12520         var a = arguments, l = a.length;
12521         for(var i = 0; i < l; i++){
12522             this._add(a[i]);
12523         }
12524     },
12525     // private..
12526     _add : function(el) {
12527         
12528         if (el.xtype) {
12529             el = Roo.factory(el, typeof(Roo.Toolbar[el.xtype]) == 'undefined' ? Roo.form : Roo.Toolbar);
12530         }
12531         
12532         if (el.applyTo){ // some kind of form field
12533             return this.addField(el);
12534         } 
12535         if (el.render){ // some kind of Toolbar.Item
12536             return this.addItem(el);
12537         }
12538         if (typeof el == "string"){ // string
12539             if(el == "separator" || el == "-"){
12540                 return this.addSeparator();
12541             }
12542             if (el == " "){
12543                 return this.addSpacer();
12544             }
12545             if(el == "->"){
12546                 return this.addFill();
12547             }
12548             return this.addText(el);
12549             
12550         }
12551         if(el.tagName){ // element
12552             return this.addElement(el);
12553         }
12554         if(typeof el == "object"){ // must be button config?
12555             return this.addButton(el);
12556         }
12557         // and now what?!?!
12558         return false;
12559         
12560     },
12561     
12562     /**
12563      * Add an Xtype element
12564      * @param {Object} xtype Xtype Object
12565      * @return {Object} created Object
12566      */
12567     addxtype : function(e){
12568         return this.add(e);  
12569     },
12570     
12571     /**
12572      * Returns the Element for this toolbar.
12573      * @return {Roo.Element}
12574      */
12575     getEl : function(){
12576         return this.el;  
12577     },
12578     
12579     /**
12580      * Adds a separator
12581      * @return {Roo.Toolbar.Item} The separator item
12582      */
12583     addSeparator : function(){
12584         return this.addItem(new Roo.Toolbar.Separator());
12585     },
12586
12587     /**
12588      * Adds a spacer element
12589      * @return {Roo.Toolbar.Spacer} The spacer item
12590      */
12591     addSpacer : function(){
12592         return this.addItem(new Roo.Toolbar.Spacer());
12593     },
12594
12595     /**
12596      * Adds a fill element that forces subsequent additions to the right side of the toolbar
12597      * @return {Roo.Toolbar.Fill} The fill item
12598      */
12599     addFill : function(){
12600         return this.addItem(new Roo.Toolbar.Fill());
12601     },
12602
12603     /**
12604      * Adds any standard HTML element to the toolbar
12605      * @param {String/HTMLElement/Element} el The element or id of the element to add
12606      * @return {Roo.Toolbar.Item} The element's item
12607      */
12608     addElement : function(el){
12609         return this.addItem(new Roo.Toolbar.Item(el));
12610     },
12611     /**
12612      * Collection of items on the toolbar.. (only Toolbar Items, so use fields to retrieve fields)
12613      * @type Roo.util.MixedCollection  
12614      */
12615     items : false,
12616      
12617     /**
12618      * Adds any Toolbar.Item or subclass
12619      * @param {Roo.Toolbar.Item} item
12620      * @return {Roo.Toolbar.Item} The item
12621      */
12622     addItem : function(item){
12623         var td = this.nextBlock();
12624         item.render(td);
12625         this.items.add(item);
12626         return item;
12627     },
12628     
12629     /**
12630      * Adds a button (or buttons). See {@link Roo.Toolbar.Button} for more info on the config.
12631      * @param {Object/Array} config A button config or array of configs
12632      * @return {Roo.Toolbar.Button/Array}
12633      */
12634     addButton : function(config){
12635         if(config instanceof Array){
12636             var buttons = [];
12637             for(var i = 0, len = config.length; i < len; i++) {
12638                 buttons.push(this.addButton(config[i]));
12639             }
12640             return buttons;
12641         }
12642         var b = config;
12643         if(!(config instanceof Roo.Toolbar.Button)){
12644             b = config.split ?
12645                 new Roo.Toolbar.SplitButton(config) :
12646                 new Roo.Toolbar.Button(config);
12647         }
12648         var td = this.nextBlock();
12649         b.render(td);
12650         this.items.add(b);
12651         return b;
12652     },
12653     
12654     /**
12655      * Adds text to the toolbar
12656      * @param {String} text The text to add
12657      * @return {Roo.Toolbar.Item} The element's item
12658      */
12659     addText : function(text){
12660         return this.addItem(new Roo.Toolbar.TextItem(text));
12661     },
12662     
12663     /**
12664      * Inserts any {@link Roo.Toolbar.Item}/{@link Roo.Toolbar.Button} at the specified index.
12665      * @param {Number} index The index where the item is to be inserted
12666      * @param {Object/Roo.Toolbar.Item/Roo.Toolbar.Button (may be Array)} item The button, or button config object to be inserted.
12667      * @return {Roo.Toolbar.Button/Item}
12668      */
12669     insertButton : function(index, item){
12670         if(item instanceof Array){
12671             var buttons = [];
12672             for(var i = 0, len = item.length; i < len; i++) {
12673                buttons.push(this.insertButton(index + i, item[i]));
12674             }
12675             return buttons;
12676         }
12677         if (!(item instanceof Roo.Toolbar.Button)){
12678            item = new Roo.Toolbar.Button(item);
12679         }
12680         var td = document.createElement("td");
12681         this.tr.insertBefore(td, this.tr.childNodes[index]);
12682         item.render(td);
12683         this.items.insert(index, item);
12684         return item;
12685     },
12686     
12687     /**
12688      * Adds a new element to the toolbar from the passed {@link Roo.DomHelper} config.
12689      * @param {Object} config
12690      * @return {Roo.Toolbar.Item} The element's item
12691      */
12692     addDom : function(config, returnEl){
12693         var td = this.nextBlock();
12694         Roo.DomHelper.overwrite(td, config);
12695         var ti = new Roo.Toolbar.Item(td.firstChild);
12696         ti.render(td);
12697         this.items.add(ti);
12698         return ti;
12699     },
12700
12701     /**
12702      * Collection of fields on the toolbar.. usefull for quering (value is false if there are no fields)
12703      * @type Roo.util.MixedCollection  
12704      */
12705     fields : false,
12706     
12707     /**
12708      * Adds a dynamically rendered Roo.form field (TextField, ComboBox, etc).
12709      * Note: the field should not have been rendered yet. For a field that has already been
12710      * rendered, use {@link #addElement}.
12711      * @param {Roo.form.Field} field
12712      * @return {Roo.ToolbarItem}
12713      */
12714      
12715       
12716     addField : function(field) {
12717         if (!this.fields) {
12718             var autoId = 0;
12719             this.fields = new Roo.util.MixedCollection(false, function(o){
12720                 return o.id || ("item" + (++autoId));
12721             });
12722
12723         }
12724         
12725         var td = this.nextBlock();
12726         field.render(td);
12727         var ti = new Roo.Toolbar.Item(td.firstChild);
12728         ti.render(td);
12729         this.items.add(ti);
12730         this.fields.add(field);
12731         return ti;
12732     },
12733     /**
12734      * Hide the toolbar
12735      * @method hide
12736      */
12737      
12738       
12739     hide : function()
12740     {
12741         this.el.child('div').setVisibilityMode(Roo.Element.DISPLAY);
12742         this.el.child('div').hide();
12743     },
12744     /**
12745      * Show the toolbar
12746      * @method show
12747      */
12748     show : function()
12749     {
12750         this.el.child('div').show();
12751     },
12752       
12753     // private
12754     nextBlock : function(){
12755         var td = document.createElement("td");
12756         this.tr.appendChild(td);
12757         return td;
12758     },
12759
12760     // private
12761     destroy : function(){
12762         if(this.items){ // rendered?
12763             Roo.destroy.apply(Roo, this.items.items);
12764         }
12765         if(this.fields){ // rendered?
12766             Roo.destroy.apply(Roo, this.fields.items);
12767         }
12768         Roo.Element.uncache(this.el, this.tr);
12769     }
12770 };
12771
12772 /**
12773  * @class Roo.Toolbar.Item
12774  * The base class that other classes should extend in order to get some basic common toolbar item functionality.
12775  * @constructor
12776  * Creates a new Item
12777  * @param {HTMLElement} el 
12778  */
12779 Roo.Toolbar.Item = function(el){
12780     this.el = Roo.getDom(el);
12781     this.id = Roo.id(this.el);
12782     this.hidden = false;
12783 };
12784
12785 Roo.Toolbar.Item.prototype = {
12786     
12787     /**
12788      * Get this item's HTML Element
12789      * @return {HTMLElement}
12790      */
12791     getEl : function(){
12792        return this.el;  
12793     },
12794
12795     // private
12796     render : function(td){
12797         this.td = td;
12798         td.appendChild(this.el);
12799     },
12800     
12801     /**
12802      * Removes and destroys this item.
12803      */
12804     destroy : function(){
12805         this.td.parentNode.removeChild(this.td);
12806     },
12807     
12808     /**
12809      * Shows this item.
12810      */
12811     show: function(){
12812         this.hidden = false;
12813         this.td.style.display = "";
12814     },
12815     
12816     /**
12817      * Hides this item.
12818      */
12819     hide: function(){
12820         this.hidden = true;
12821         this.td.style.display = "none";
12822     },
12823     
12824     /**
12825      * Convenience function for boolean show/hide.
12826      * @param {Boolean} visible true to show/false to hide
12827      */
12828     setVisible: function(visible){
12829         if(visible) {
12830             this.show();
12831         }else{
12832             this.hide();
12833         }
12834     },
12835     
12836     /**
12837      * Try to focus this item.
12838      */
12839     focus : function(){
12840         Roo.fly(this.el).focus();
12841     },
12842     
12843     /**
12844      * Disables this item.
12845      */
12846     disable : function(){
12847         Roo.fly(this.td).addClass("x-item-disabled");
12848         this.disabled = true;
12849         this.el.disabled = true;
12850     },
12851     
12852     /**
12853      * Enables this item.
12854      */
12855     enable : function(){
12856         Roo.fly(this.td).removeClass("x-item-disabled");
12857         this.disabled = false;
12858         this.el.disabled = false;
12859     }
12860 };
12861
12862
12863 /**
12864  * @class Roo.Toolbar.Separator
12865  * @extends Roo.Toolbar.Item
12866  * A simple toolbar separator class
12867  * @constructor
12868  * Creates a new Separator
12869  */
12870 Roo.Toolbar.Separator = function(){
12871     var s = document.createElement("span");
12872     s.className = "ytb-sep";
12873     Roo.Toolbar.Separator.superclass.constructor.call(this, s);
12874 };
12875 Roo.extend(Roo.Toolbar.Separator, Roo.Toolbar.Item, {
12876     enable:Roo.emptyFn,
12877     disable:Roo.emptyFn,
12878     focus:Roo.emptyFn
12879 });
12880
12881 /**
12882  * @class Roo.Toolbar.Spacer
12883  * @extends Roo.Toolbar.Item
12884  * A simple element that adds extra horizontal space to a toolbar.
12885  * @constructor
12886  * Creates a new Spacer
12887  */
12888 Roo.Toolbar.Spacer = function(){
12889     var s = document.createElement("div");
12890     s.className = "ytb-spacer";
12891     Roo.Toolbar.Spacer.superclass.constructor.call(this, s);
12892 };
12893 Roo.extend(Roo.Toolbar.Spacer, Roo.Toolbar.Item, {
12894     enable:Roo.emptyFn,
12895     disable:Roo.emptyFn,
12896     focus:Roo.emptyFn
12897 });
12898
12899 /**
12900  * @class Roo.Toolbar.Fill
12901  * @extends Roo.Toolbar.Spacer
12902  * A simple element that adds a greedy (100% width) horizontal space to a toolbar.
12903  * @constructor
12904  * Creates a new Spacer
12905  */
12906 Roo.Toolbar.Fill = Roo.extend(Roo.Toolbar.Spacer, {
12907     // private
12908     render : function(td){
12909         td.style.width = '100%';
12910         Roo.Toolbar.Fill.superclass.render.call(this, td);
12911     }
12912 });
12913
12914 /**
12915  * @class Roo.Toolbar.TextItem
12916  * @extends Roo.Toolbar.Item
12917  * A simple class that renders text directly into a toolbar.
12918  * @constructor
12919  * Creates a new TextItem
12920  * @param {String} text
12921  */
12922 Roo.Toolbar.TextItem = function(text){
12923     if (typeof(text) == 'object') {
12924         text = text.text;
12925     }
12926     var s = document.createElement("span");
12927     s.className = "ytb-text";
12928     s.innerHTML = text;
12929     Roo.Toolbar.TextItem.superclass.constructor.call(this, s);
12930 };
12931 Roo.extend(Roo.Toolbar.TextItem, Roo.Toolbar.Item, {
12932     enable:Roo.emptyFn,
12933     disable:Roo.emptyFn,
12934     focus:Roo.emptyFn
12935 });
12936
12937 /**
12938  * @class Roo.Toolbar.Button
12939  * @extends Roo.Button
12940  * A button that renders into a toolbar.
12941  * @constructor
12942  * Creates a new Button
12943  * @param {Object} config A standard {@link Roo.Button} config object
12944  */
12945 Roo.Toolbar.Button = function(config){
12946     Roo.Toolbar.Button.superclass.constructor.call(this, null, config);
12947 };
12948 Roo.extend(Roo.Toolbar.Button, Roo.Button, {
12949     render : function(td){
12950         this.td = td;
12951         Roo.Toolbar.Button.superclass.render.call(this, td);
12952     },
12953     
12954     /**
12955      * Removes and destroys this button
12956      */
12957     destroy : function(){
12958         Roo.Toolbar.Button.superclass.destroy.call(this);
12959         this.td.parentNode.removeChild(this.td);
12960     },
12961     
12962     /**
12963      * Shows this button
12964      */
12965     show: function(){
12966         this.hidden = false;
12967         this.td.style.display = "";
12968     },
12969     
12970     /**
12971      * Hides this button
12972      */
12973     hide: function(){
12974         this.hidden = true;
12975         this.td.style.display = "none";
12976     },
12977
12978     /**
12979      * Disables this item
12980      */
12981     disable : function(){
12982         Roo.fly(this.td).addClass("x-item-disabled");
12983         this.disabled = true;
12984     },
12985
12986     /**
12987      * Enables this item
12988      */
12989     enable : function(){
12990         Roo.fly(this.td).removeClass("x-item-disabled");
12991         this.disabled = false;
12992     }
12993 });
12994 // backwards compat
12995 Roo.ToolbarButton = Roo.Toolbar.Button;
12996
12997 /**
12998  * @class Roo.Toolbar.SplitButton
12999  * @extends Roo.SplitButton
13000  * A menu button that renders into a toolbar.
13001  * @constructor
13002  * Creates a new SplitButton
13003  * @param {Object} config A standard {@link Roo.SplitButton} config object
13004  */
13005 Roo.Toolbar.SplitButton = function(config){
13006     Roo.Toolbar.SplitButton.superclass.constructor.call(this, null, config);
13007 };
13008 Roo.extend(Roo.Toolbar.SplitButton, Roo.SplitButton, {
13009     render : function(td){
13010         this.td = td;
13011         Roo.Toolbar.SplitButton.superclass.render.call(this, td);
13012     },
13013     
13014     /**
13015      * Removes and destroys this button
13016      */
13017     destroy : function(){
13018         Roo.Toolbar.SplitButton.superclass.destroy.call(this);
13019         this.td.parentNode.removeChild(this.td);
13020     },
13021     
13022     /**
13023      * Shows this button
13024      */
13025     show: function(){
13026         this.hidden = false;
13027         this.td.style.display = "";
13028     },
13029     
13030     /**
13031      * Hides this button
13032      */
13033     hide: function(){
13034         this.hidden = true;
13035         this.td.style.display = "none";
13036     }
13037 });
13038
13039 // backwards compat
13040 Roo.Toolbar.MenuButton = Roo.Toolbar.SplitButton;/*
13041  * Based on:
13042  * Ext JS Library 1.1.1
13043  * Copyright(c) 2006-2007, Ext JS, LLC.
13044  *
13045  * Originally Released Under LGPL - original licence link has changed is not relivant.
13046  *
13047  * Fork - LGPL
13048  * <script type="text/javascript">
13049  */
13050  
13051 /**
13052  * @class Roo.PagingToolbar
13053  * @extends Roo.Toolbar
13054  * A specialized toolbar that is bound to a {@link Roo.data.Store} and provides automatic paging controls.
13055  * @constructor
13056  * Create a new PagingToolbar
13057  * @param {Object} config The config object
13058  */
13059 Roo.PagingToolbar = function(el, ds, config)
13060 {
13061     // old args format still supported... - xtype is prefered..
13062     if (typeof(el) == 'object' && el.xtype) {
13063         // created from xtype...
13064         config = el;
13065         ds = el.dataSource;
13066         el = config.container;
13067     }
13068     var items = [];
13069     if (config.items) {
13070         items = config.items;
13071         config.items = [];
13072     }
13073     
13074     Roo.PagingToolbar.superclass.constructor.call(this, el, null, config);
13075     this.ds = ds;
13076     this.cursor = 0;
13077     this.renderButtons(this.el);
13078     this.bind(ds);
13079     
13080     // supprot items array.
13081    
13082     Roo.each(items, function(e) {
13083         this.add(Roo.factory(e));
13084     },this);
13085     
13086 };
13087
13088 Roo.extend(Roo.PagingToolbar, Roo.Toolbar, {
13089     /**
13090      * @cfg {Roo.data.Store} dataSource
13091      * The underlying data store providing the paged data
13092      */
13093     /**
13094      * @cfg {String/HTMLElement/Element} container
13095      * container The id or element that will contain the toolbar
13096      */
13097     /**
13098      * @cfg {Boolean} displayInfo
13099      * True to display the displayMsg (defaults to false)
13100      */
13101     /**
13102      * @cfg {Number} pageSize
13103      * The number of records to display per page (defaults to 20)
13104      */
13105     pageSize: 20,
13106     /**
13107      * @cfg {String} displayMsg
13108      * The paging status message to display (defaults to "Displaying {start} - {end} of {total}")
13109      */
13110     displayMsg : 'Displaying {0} - {1} of {2}',
13111     /**
13112      * @cfg {String} emptyMsg
13113      * The message to display when no records are found (defaults to "No data to display")
13114      */
13115     emptyMsg : 'No data to display',
13116     /**
13117      * Customizable piece of the default paging text (defaults to "Page")
13118      * @type String
13119      */
13120     beforePageText : "Page",
13121     /**
13122      * Customizable piece of the default paging text (defaults to "of %0")
13123      * @type String
13124      */
13125     afterPageText : "of {0}",
13126     /**
13127      * Customizable piece of the default paging text (defaults to "First Page")
13128      * @type String
13129      */
13130     firstText : "First Page",
13131     /**
13132      * Customizable piece of the default paging text (defaults to "Previous Page")
13133      * @type String
13134      */
13135     prevText : "Previous Page",
13136     /**
13137      * Customizable piece of the default paging text (defaults to "Next Page")
13138      * @type String
13139      */
13140     nextText : "Next Page",
13141     /**
13142      * Customizable piece of the default paging text (defaults to "Last Page")
13143      * @type String
13144      */
13145     lastText : "Last Page",
13146     /**
13147      * Customizable piece of the default paging text (defaults to "Refresh")
13148      * @type String
13149      */
13150     refreshText : "Refresh",
13151
13152     // private
13153     renderButtons : function(el){
13154         Roo.PagingToolbar.superclass.render.call(this, el);
13155         this.first = this.addButton({
13156             tooltip: this.firstText,
13157             cls: "x-btn-icon x-grid-page-first",
13158             disabled: true,
13159             handler: this.onClick.createDelegate(this, ["first"])
13160         });
13161         this.prev = this.addButton({
13162             tooltip: this.prevText,
13163             cls: "x-btn-icon x-grid-page-prev",
13164             disabled: true,
13165             handler: this.onClick.createDelegate(this, ["prev"])
13166         });
13167         //this.addSeparator();
13168         this.add(this.beforePageText);
13169         this.field = Roo.get(this.addDom({
13170            tag: "input",
13171            type: "text",
13172            size: "3",
13173            value: "1",
13174            cls: "x-grid-page-number"
13175         }).el);
13176         this.field.on("keydown", this.onPagingKeydown, this);
13177         this.field.on("focus", function(){this.dom.select();});
13178         this.afterTextEl = this.addText(String.format(this.afterPageText, 1));
13179         this.field.setHeight(18);
13180         //this.addSeparator();
13181         this.next = this.addButton({
13182             tooltip: this.nextText,
13183             cls: "x-btn-icon x-grid-page-next",
13184             disabled: true,
13185             handler: this.onClick.createDelegate(this, ["next"])
13186         });
13187         this.last = this.addButton({
13188             tooltip: this.lastText,
13189             cls: "x-btn-icon x-grid-page-last",
13190             disabled: true,
13191             handler: this.onClick.createDelegate(this, ["last"])
13192         });
13193         //this.addSeparator();
13194         this.loading = this.addButton({
13195             tooltip: this.refreshText,
13196             cls: "x-btn-icon x-grid-loading",
13197             handler: this.onClick.createDelegate(this, ["refresh"])
13198         });
13199
13200         if(this.displayInfo){
13201             this.displayEl = Roo.fly(this.el.dom.firstChild).createChild({cls:'x-paging-info'});
13202         }
13203     },
13204
13205     // private
13206     updateInfo : function(){
13207         if(this.displayEl){
13208             var count = this.ds.getCount();
13209             var msg = count == 0 ?
13210                 this.emptyMsg :
13211                 String.format(
13212                     this.displayMsg,
13213                     this.cursor+1, this.cursor+count, this.ds.getTotalCount()    
13214                 );
13215             this.displayEl.update(msg);
13216         }
13217     },
13218
13219     // private
13220     onLoad : function(ds, r, o){
13221        this.cursor = o.params ? o.params.start : 0;
13222        var d = this.getPageData(), ap = d.activePage, ps = d.pages;
13223
13224        this.afterTextEl.el.innerHTML = String.format(this.afterPageText, d.pages);
13225        this.field.dom.value = ap;
13226        this.first.setDisabled(ap == 1);
13227        this.prev.setDisabled(ap == 1);
13228        this.next.setDisabled(ap == ps);
13229        this.last.setDisabled(ap == ps);
13230        this.loading.enable();
13231        this.updateInfo();
13232     },
13233
13234     // private
13235     getPageData : function(){
13236         var total = this.ds.getTotalCount();
13237         return {
13238             total : total,
13239             activePage : Math.ceil((this.cursor+this.pageSize)/this.pageSize),
13240             pages :  total < this.pageSize ? 1 : Math.ceil(total/this.pageSize)
13241         };
13242     },
13243
13244     // private
13245     onLoadError : function(){
13246         this.loading.enable();
13247     },
13248
13249     // private
13250     onPagingKeydown : function(e){
13251         var k = e.getKey();
13252         var d = this.getPageData();
13253         if(k == e.RETURN){
13254             var v = this.field.dom.value, pageNum;
13255             if(!v || isNaN(pageNum = parseInt(v, 10))){
13256                 this.field.dom.value = d.activePage;
13257                 return;
13258             }
13259             pageNum = Math.min(Math.max(1, pageNum), d.pages) - 1;
13260             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
13261             e.stopEvent();
13262         }
13263         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))
13264         {
13265           var pageNum = (k == e.HOME || (k == e.DOWN && e.ctrlKey) || (k == e.LEFT && e.ctrlKey) || (k == e.PAGEDOWN && e.ctrlKey)) ? 1 : d.pages;
13266           this.field.dom.value = pageNum;
13267           this.ds.load({params:{start: (pageNum - 1) * this.pageSize, limit: this.pageSize}});
13268           e.stopEvent();
13269         }
13270         else if(k == e.UP || k == e.RIGHT || k == e.PAGEUP || k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN)
13271         {
13272           var v = this.field.dom.value, pageNum; 
13273           var increment = (e.shiftKey) ? 10 : 1;
13274           if(k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN)
13275             increment *= -1;
13276           if(!v || isNaN(pageNum = parseInt(v, 10))) {
13277             this.field.dom.value = d.activePage;
13278             return;
13279           }
13280           else if(parseInt(v, 10) + increment >= 1 & parseInt(v, 10) + increment <= d.pages)
13281           {
13282             this.field.dom.value = parseInt(v, 10) + increment;
13283             pageNum = Math.min(Math.max(1, pageNum + increment), d.pages) - 1;
13284             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
13285           }
13286           e.stopEvent();
13287         }
13288     },
13289
13290     // private
13291     beforeLoad : function(){
13292         if(this.loading){
13293             this.loading.disable();
13294         }
13295     },
13296
13297     // private
13298     onClick : function(which){
13299         var ds = this.ds;
13300         switch(which){
13301             case "first":
13302                 ds.load({params:{start: 0, limit: this.pageSize}});
13303             break;
13304             case "prev":
13305                 ds.load({params:{start: Math.max(0, this.cursor-this.pageSize), limit: this.pageSize}});
13306             break;
13307             case "next":
13308                 ds.load({params:{start: this.cursor+this.pageSize, limit: this.pageSize}});
13309             break;
13310             case "last":
13311                 var total = ds.getTotalCount();
13312                 var extra = total % this.pageSize;
13313                 var lastStart = extra ? (total - extra) : total-this.pageSize;
13314                 ds.load({params:{start: lastStart, limit: this.pageSize}});
13315             break;
13316             case "refresh":
13317                 ds.load({params:{start: this.cursor, limit: this.pageSize}});
13318             break;
13319         }
13320     },
13321
13322     /**
13323      * Unbinds the paging toolbar from the specified {@link Roo.data.Store}
13324      * @param {Roo.data.Store} store The data store to unbind
13325      */
13326     unbind : function(ds){
13327         ds.un("beforeload", this.beforeLoad, this);
13328         ds.un("load", this.onLoad, this);
13329         ds.un("loadexception", this.onLoadError, this);
13330         ds.un("remove", this.updateInfo, this);
13331         ds.un("add", this.updateInfo, this);
13332         this.ds = undefined;
13333     },
13334
13335     /**
13336      * Binds the paging toolbar to the specified {@link Roo.data.Store}
13337      * @param {Roo.data.Store} store The data store to bind
13338      */
13339     bind : function(ds){
13340         ds.on("beforeload", this.beforeLoad, this);
13341         ds.on("load", this.onLoad, this);
13342         ds.on("loadexception", this.onLoadError, this);
13343         ds.on("remove", this.updateInfo, this);
13344         ds.on("add", this.updateInfo, this);
13345         this.ds = ds;
13346     }
13347 });/*
13348  * Based on:
13349  * Ext JS Library 1.1.1
13350  * Copyright(c) 2006-2007, Ext JS, LLC.
13351  *
13352  * Originally Released Under LGPL - original licence link has changed is not relivant.
13353  *
13354  * Fork - LGPL
13355  * <script type="text/javascript">
13356  */
13357
13358 /**
13359  * @class Roo.Resizable
13360  * @extends Roo.util.Observable
13361  * <p>Applies drag handles to an element to make it resizable. The drag handles are inserted into the element
13362  * and positioned absolute. Some elements, such as a textarea or image, don't support this. To overcome that, you can wrap
13363  * 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
13364  * the element will be wrapped for you automatically.</p>
13365  * <p>Here is the list of valid resize handles:</p>
13366  * <pre>
13367 Value   Description
13368 ------  -------------------
13369  'n'     north
13370  's'     south
13371  'e'     east
13372  'w'     west
13373  'nw'    northwest
13374  'sw'    southwest
13375  'se'    southeast
13376  'ne'    northeast
13377  'hd'    horizontal drag
13378  'all'   all
13379 </pre>
13380  * <p>Here's an example showing the creation of a typical Resizable:</p>
13381  * <pre><code>
13382 var resizer = new Roo.Resizable("element-id", {
13383     handles: 'all',
13384     minWidth: 200,
13385     minHeight: 100,
13386     maxWidth: 500,
13387     maxHeight: 400,
13388     pinned: true
13389 });
13390 resizer.on("resize", myHandler);
13391 </code></pre>
13392  * <p>To hide a particular handle, set its display to none in CSS, or through script:<br>
13393  * resizer.east.setDisplayed(false);</p>
13394  * @cfg {Boolean/String/Element} resizeChild True to resize the first child, or id/element to resize (defaults to false)
13395  * @cfg {Array/String} adjustments String "auto" or an array [width, height] with values to be <b>added</b> to the
13396  * resize operation's new size (defaults to [0, 0])
13397  * @cfg {Number} minWidth The minimum width for the element (defaults to 5)
13398  * @cfg {Number} minHeight The minimum height for the element (defaults to 5)
13399  * @cfg {Number} maxWidth The maximum width for the element (defaults to 10000)
13400  * @cfg {Number} maxHeight The maximum height for the element (defaults to 10000)
13401  * @cfg {Boolean} enabled False to disable resizing (defaults to true)
13402  * @cfg {Boolean} wrap True to wrap an element with a div if needed (required for textareas and images, defaults to false)
13403  * @cfg {Number} width The width of the element in pixels (defaults to null)
13404  * @cfg {Number} height The height of the element in pixels (defaults to null)
13405  * @cfg {Boolean} animate True to animate the resize (not compatible with dynamic sizing, defaults to false)
13406  * @cfg {Number} duration Animation duration if animate = true (defaults to .35)
13407  * @cfg {Boolean} dynamic True to resize the element while dragging instead of using a proxy (defaults to false)
13408  * @cfg {String} handles String consisting of the resize handles to display (defaults to undefined)
13409  * @cfg {Boolean} multiDirectional <b>Deprecated</b>.  The old style of adding multi-direction resize handles, deprecated
13410  * in favor of the handles config option (defaults to false)
13411  * @cfg {Boolean} disableTrackOver True to disable mouse tracking. This is only applied at config time. (defaults to false)
13412  * @cfg {String} easing Animation easing if animate = true (defaults to 'easingOutStrong')
13413  * @cfg {Number} widthIncrement The increment to snap the width resize in pixels (dynamic must be true, defaults to 0)
13414  * @cfg {Number} heightIncrement The increment to snap the height resize in pixels (dynamic must be true, defaults to 0)
13415  * @cfg {Boolean} pinned True to ensure that the resize handles are always visible, false to display them only when the
13416  * user mouses over the resizable borders. This is only applied at config time. (defaults to false)
13417  * @cfg {Boolean} preserveRatio True to preserve the original ratio between height and width during resize (defaults to false)
13418  * @cfg {Boolean} transparent True for transparent handles. This is only applied at config time. (defaults to false)
13419  * @cfg {Number} minX The minimum allowed page X for the element (only used for west resizing, defaults to 0)
13420  * @cfg {Number} minY The minimum allowed page Y for the element (only used for north resizing, defaults to 0)
13421  * @cfg {Boolean} draggable Convenience to initialize drag drop (defaults to false)
13422  * @constructor
13423  * Create a new resizable component
13424  * @param {String/HTMLElement/Roo.Element} el The id or element to resize
13425  * @param {Object} config configuration options
13426   */
13427 Roo.Resizable = function(el, config)
13428 {
13429     this.el = Roo.get(el);
13430
13431     if(config && config.wrap){
13432         config.resizeChild = this.el;
13433         this.el = this.el.wrap(typeof config.wrap == "object" ? config.wrap : {cls:"xresizable-wrap"});
13434         this.el.id = this.el.dom.id = config.resizeChild.id + "-rzwrap";
13435         this.el.setStyle("overflow", "hidden");
13436         this.el.setPositioning(config.resizeChild.getPositioning());
13437         config.resizeChild.clearPositioning();
13438         if(!config.width || !config.height){
13439             var csize = config.resizeChild.getSize();
13440             this.el.setSize(csize.width, csize.height);
13441         }
13442         if(config.pinned && !config.adjustments){
13443             config.adjustments = "auto";
13444         }
13445     }
13446
13447     this.proxy = this.el.createProxy({tag: "div", cls: "x-resizable-proxy", id: this.el.id + "-rzproxy"});
13448     this.proxy.unselectable();
13449     this.proxy.enableDisplayMode('block');
13450
13451     Roo.apply(this, config);
13452
13453     if(this.pinned){
13454         this.disableTrackOver = true;
13455         this.el.addClass("x-resizable-pinned");
13456     }
13457     // if the element isn't positioned, make it relative
13458     var position = this.el.getStyle("position");
13459     if(position != "absolute" && position != "fixed"){
13460         this.el.setStyle("position", "relative");
13461     }
13462     if(!this.handles){ // no handles passed, must be legacy style
13463         this.handles = 's,e,se';
13464         if(this.multiDirectional){
13465             this.handles += ',n,w';
13466         }
13467     }
13468     if(this.handles == "all"){
13469         this.handles = "n s e w ne nw se sw";
13470     }
13471     var hs = this.handles.split(/\s*?[,;]\s*?| /);
13472     var ps = Roo.Resizable.positions;
13473     for(var i = 0, len = hs.length; i < len; i++){
13474         if(hs[i] && ps[hs[i]]){
13475             var pos = ps[hs[i]];
13476             this[pos] = new Roo.Resizable.Handle(this, pos, this.disableTrackOver, this.transparent);
13477         }
13478     }
13479     // legacy
13480     this.corner = this.southeast;
13481     
13482     // updateBox = the box can move..
13483     if(this.handles.indexOf("n") != -1 || this.handles.indexOf("w") != -1 || this.handles.indexOf("hd") != -1) {
13484         this.updateBox = true;
13485     }
13486
13487     this.activeHandle = null;
13488
13489     if(this.resizeChild){
13490         if(typeof this.resizeChild == "boolean"){
13491             this.resizeChild = Roo.get(this.el.dom.firstChild, true);
13492         }else{
13493             this.resizeChild = Roo.get(this.resizeChild, true);
13494         }
13495     }
13496     
13497     if(this.adjustments == "auto"){
13498         var rc = this.resizeChild;
13499         var hw = this.west, he = this.east, hn = this.north, hs = this.south;
13500         if(rc && (hw || hn)){
13501             rc.position("relative");
13502             rc.setLeft(hw ? hw.el.getWidth() : 0);
13503             rc.setTop(hn ? hn.el.getHeight() : 0);
13504         }
13505         this.adjustments = [
13506             (he ? -he.el.getWidth() : 0) + (hw ? -hw.el.getWidth() : 0),
13507             (hn ? -hn.el.getHeight() : 0) + (hs ? -hs.el.getHeight() : 0) -1
13508         ];
13509     }
13510
13511     if(this.draggable){
13512         this.dd = this.dynamic ?
13513             this.el.initDD(null) : this.el.initDDProxy(null, {dragElId: this.proxy.id});
13514         this.dd.setHandleElId(this.resizeChild ? this.resizeChild.id : this.el.id);
13515     }
13516
13517     // public events
13518     this.addEvents({
13519         /**
13520          * @event beforeresize
13521          * Fired before resize is allowed. Set enabled to false to cancel resize.
13522          * @param {Roo.Resizable} this
13523          * @param {Roo.EventObject} e The mousedown event
13524          */
13525         "beforeresize" : true,
13526         /**
13527          * @event resize
13528          * Fired after a resize.
13529          * @param {Roo.Resizable} this
13530          * @param {Number} width The new width
13531          * @param {Number} height The new height
13532          * @param {Roo.EventObject} e The mouseup event
13533          */
13534         "resize" : true
13535     });
13536
13537     if(this.width !== null && this.height !== null){
13538         this.resizeTo(this.width, this.height);
13539     }else{
13540         this.updateChildSize();
13541     }
13542     if(Roo.isIE){
13543         this.el.dom.style.zoom = 1;
13544     }
13545     Roo.Resizable.superclass.constructor.call(this);
13546 };
13547
13548 Roo.extend(Roo.Resizable, Roo.util.Observable, {
13549         resizeChild : false,
13550         adjustments : [0, 0],
13551         minWidth : 5,
13552         minHeight : 5,
13553         maxWidth : 10000,
13554         maxHeight : 10000,
13555         enabled : true,
13556         animate : false,
13557         duration : .35,
13558         dynamic : false,
13559         handles : false,
13560         multiDirectional : false,
13561         disableTrackOver : false,
13562         easing : 'easeOutStrong',
13563         widthIncrement : 0,
13564         heightIncrement : 0,
13565         pinned : false,
13566         width : null,
13567         height : null,
13568         preserveRatio : false,
13569         transparent: false,
13570         minX: 0,
13571         minY: 0,
13572         draggable: false,
13573
13574         /**
13575          * @cfg {String/HTMLElement/Element} constrainTo Constrain the resize to a particular element
13576          */
13577         constrainTo: undefined,
13578         /**
13579          * @cfg {Roo.lib.Region} resizeRegion Constrain the resize to a particular region
13580          */
13581         resizeRegion: undefined,
13582
13583
13584     /**
13585      * Perform a manual resize
13586      * @param {Number} width
13587      * @param {Number} height
13588      */
13589     resizeTo : function(width, height){
13590         this.el.setSize(width, height);
13591         this.updateChildSize();
13592         this.fireEvent("resize", this, width, height, null);
13593     },
13594
13595     // private
13596     startSizing : function(e, handle){
13597         this.fireEvent("beforeresize", this, e);
13598         if(this.enabled){ // 2nd enabled check in case disabled before beforeresize handler
13599
13600             if(!this.overlay){
13601                 this.overlay = this.el.createProxy({tag: "div", cls: "x-resizable-overlay", html: "&#160;"});
13602                 this.overlay.unselectable();
13603                 this.overlay.enableDisplayMode("block");
13604                 this.overlay.on("mousemove", this.onMouseMove, this);
13605                 this.overlay.on("mouseup", this.onMouseUp, this);
13606             }
13607             this.overlay.setStyle("cursor", handle.el.getStyle("cursor"));
13608
13609             this.resizing = true;
13610             this.startBox = this.el.getBox();
13611             this.startPoint = e.getXY();
13612             this.offsets = [(this.startBox.x + this.startBox.width) - this.startPoint[0],
13613                             (this.startBox.y + this.startBox.height) - this.startPoint[1]];
13614
13615             this.overlay.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
13616             this.overlay.show();
13617
13618             if(this.constrainTo) {
13619                 var ct = Roo.get(this.constrainTo);
13620                 this.resizeRegion = ct.getRegion().adjust(
13621                     ct.getFrameWidth('t'),
13622                     ct.getFrameWidth('l'),
13623                     -ct.getFrameWidth('b'),
13624                     -ct.getFrameWidth('r')
13625                 );
13626             }
13627
13628             this.proxy.setStyle('visibility', 'hidden'); // workaround display none
13629             this.proxy.show();
13630             this.proxy.setBox(this.startBox);
13631             if(!this.dynamic){
13632                 this.proxy.setStyle('visibility', 'visible');
13633             }
13634         }
13635     },
13636
13637     // private
13638     onMouseDown : function(handle, e){
13639         if(this.enabled){
13640             e.stopEvent();
13641             this.activeHandle = handle;
13642             this.startSizing(e, handle);
13643         }
13644     },
13645
13646     // private
13647     onMouseUp : function(e){
13648         var size = this.resizeElement();
13649         this.resizing = false;
13650         this.handleOut();
13651         this.overlay.hide();
13652         this.proxy.hide();
13653         this.fireEvent("resize", this, size.width, size.height, e);
13654     },
13655
13656     // private
13657     updateChildSize : function(){
13658         if(this.resizeChild){
13659             var el = this.el;
13660             var child = this.resizeChild;
13661             var adj = this.adjustments;
13662             if(el.dom.offsetWidth){
13663                 var b = el.getSize(true);
13664                 child.setSize(b.width+adj[0], b.height+adj[1]);
13665             }
13666             // Second call here for IE
13667             // The first call enables instant resizing and
13668             // the second call corrects scroll bars if they
13669             // exist
13670             if(Roo.isIE){
13671                 setTimeout(function(){
13672                     if(el.dom.offsetWidth){
13673                         var b = el.getSize(true);
13674                         child.setSize(b.width+adj[0], b.height+adj[1]);
13675                     }
13676                 }, 10);
13677             }
13678         }
13679     },
13680
13681     // private
13682     snap : function(value, inc, min){
13683         if(!inc || !value) return value;
13684         var newValue = value;
13685         var m = value % inc;
13686         if(m > 0){
13687             if(m > (inc/2)){
13688                 newValue = value + (inc-m);
13689             }else{
13690                 newValue = value - m;
13691             }
13692         }
13693         return Math.max(min, newValue);
13694     },
13695
13696     // private
13697     resizeElement : function(){
13698         var box = this.proxy.getBox();
13699         if(this.updateBox){
13700             this.el.setBox(box, false, this.animate, this.duration, null, this.easing);
13701         }else{
13702             this.el.setSize(box.width, box.height, this.animate, this.duration, null, this.easing);
13703         }
13704         this.updateChildSize();
13705         if(!this.dynamic){
13706             this.proxy.hide();
13707         }
13708         return box;
13709     },
13710
13711     // private
13712     constrain : function(v, diff, m, mx){
13713         if(v - diff < m){
13714             diff = v - m;
13715         }else if(v - diff > mx){
13716             diff = mx - v;
13717         }
13718         return diff;
13719     },
13720
13721     // private
13722     onMouseMove : function(e){
13723         if(this.enabled){
13724             try{// try catch so if something goes wrong the user doesn't get hung
13725
13726             if(this.resizeRegion && !this.resizeRegion.contains(e.getPoint())) {
13727                 return;
13728             }
13729
13730             //var curXY = this.startPoint;
13731             var curSize = this.curSize || this.startBox;
13732             var x = this.startBox.x, y = this.startBox.y;
13733             var ox = x, oy = y;
13734             var w = curSize.width, h = curSize.height;
13735             var ow = w, oh = h;
13736             var mw = this.minWidth, mh = this.minHeight;
13737             var mxw = this.maxWidth, mxh = this.maxHeight;
13738             var wi = this.widthIncrement;
13739             var hi = this.heightIncrement;
13740
13741             var eventXY = e.getXY();
13742             var diffX = -(this.startPoint[0] - Math.max(this.minX, eventXY[0]));
13743             var diffY = -(this.startPoint[1] - Math.max(this.minY, eventXY[1]));
13744
13745             var pos = this.activeHandle.position;
13746
13747             switch(pos){
13748                 case "east":
13749                     w += diffX;
13750                     w = Math.min(Math.max(mw, w), mxw);
13751                     break;
13752              
13753                 case "south":
13754                     h += diffY;
13755                     h = Math.min(Math.max(mh, h), mxh);
13756                     break;
13757                 case "southeast":
13758                     w += diffX;
13759                     h += diffY;
13760                     w = Math.min(Math.max(mw, w), mxw);
13761                     h = Math.min(Math.max(mh, h), mxh);
13762                     break;
13763                 case "north":
13764                     diffY = this.constrain(h, diffY, mh, mxh);
13765                     y += diffY;
13766                     h -= diffY;
13767                     break;
13768                 case "hdrag":
13769                     
13770                     if (wi) {
13771                         var adiffX = Math.abs(diffX);
13772                         var sub = (adiffX % wi); // how much 
13773                         if (sub > (wi/2)) { // far enough to snap
13774                             diffX = (diffX > 0) ? diffX-sub + wi : diffX+sub - wi;
13775                         } else {
13776                             // remove difference.. 
13777                             diffX = (diffX > 0) ? diffX-sub : diffX+sub;
13778                         }
13779                     }
13780                     x += diffX;
13781                     x = Math.max(this.minX, x);
13782                     break;
13783                 case "west":
13784                     diffX = this.constrain(w, diffX, mw, mxw);
13785                     x += diffX;
13786                     w -= diffX;
13787                     break;
13788                 case "northeast":
13789                     w += diffX;
13790                     w = Math.min(Math.max(mw, w), mxw);
13791                     diffY = this.constrain(h, diffY, mh, mxh);
13792                     y += diffY;
13793                     h -= diffY;
13794                     break;
13795                 case "northwest":
13796                     diffX = this.constrain(w, diffX, mw, mxw);
13797                     diffY = this.constrain(h, diffY, mh, mxh);
13798                     y += diffY;
13799                     h -= diffY;
13800                     x += diffX;
13801                     w -= diffX;
13802                     break;
13803                case "southwest":
13804                     diffX = this.constrain(w, diffX, mw, mxw);
13805                     h += diffY;
13806                     h = Math.min(Math.max(mh, h), mxh);
13807                     x += diffX;
13808                     w -= diffX;
13809                     break;
13810             }
13811
13812             var sw = this.snap(w, wi, mw);
13813             var sh = this.snap(h, hi, mh);
13814             if(sw != w || sh != h){
13815                 switch(pos){
13816                     case "northeast":
13817                         y -= sh - h;
13818                     break;
13819                     case "north":
13820                         y -= sh - h;
13821                         break;
13822                     case "southwest":
13823                         x -= sw - w;
13824                     break;
13825                     case "west":
13826                         x -= sw - w;
13827                         break;
13828                     case "northwest":
13829                         x -= sw - w;
13830                         y -= sh - h;
13831                     break;
13832                 }
13833                 w = sw;
13834                 h = sh;
13835             }
13836
13837             if(this.preserveRatio){
13838                 switch(pos){
13839                     case "southeast":
13840                     case "east":
13841                         h = oh * (w/ow);
13842                         h = Math.min(Math.max(mh, h), mxh);
13843                         w = ow * (h/oh);
13844                        break;
13845                     case "south":
13846                         w = ow * (h/oh);
13847                         w = Math.min(Math.max(mw, w), mxw);
13848                         h = oh * (w/ow);
13849                         break;
13850                     case "northeast":
13851                         w = ow * (h/oh);
13852                         w = Math.min(Math.max(mw, w), mxw);
13853                         h = oh * (w/ow);
13854                     break;
13855                     case "north":
13856                         var tw = w;
13857                         w = ow * (h/oh);
13858                         w = Math.min(Math.max(mw, w), mxw);
13859                         h = oh * (w/ow);
13860                         x += (tw - w) / 2;
13861                         break;
13862                     case "southwest":
13863                         h = oh * (w/ow);
13864                         h = Math.min(Math.max(mh, h), mxh);
13865                         var tw = w;
13866                         w = ow * (h/oh);
13867                         x += tw - w;
13868                         break;
13869                     case "west":
13870                         var th = h;
13871                         h = oh * (w/ow);
13872                         h = Math.min(Math.max(mh, h), mxh);
13873                         y += (th - h) / 2;
13874                         var tw = w;
13875                         w = ow * (h/oh);
13876                         x += tw - w;
13877                        break;
13878                     case "northwest":
13879                         var tw = w;
13880                         var th = h;
13881                         h = oh * (w/ow);
13882                         h = Math.min(Math.max(mh, h), mxh);
13883                         w = ow * (h/oh);
13884                         y += th - h;
13885                         x += tw - w;
13886                        break;
13887
13888                 }
13889             }
13890             if (pos == 'hdrag') {
13891                 w = ow;
13892             }
13893             this.proxy.setBounds(x, y, w, h);
13894             if(this.dynamic){
13895                 this.resizeElement();
13896             }
13897             }catch(e){}
13898         }
13899     },
13900
13901     // private
13902     handleOver : function(){
13903         if(this.enabled){
13904             this.el.addClass("x-resizable-over");
13905         }
13906     },
13907
13908     // private
13909     handleOut : function(){
13910         if(!this.resizing){
13911             this.el.removeClass("x-resizable-over");
13912         }
13913     },
13914
13915     /**
13916      * Returns the element this component is bound to.
13917      * @return {Roo.Element}
13918      */
13919     getEl : function(){
13920         return this.el;
13921     },
13922
13923     /**
13924      * Returns the resizeChild element (or null).
13925      * @return {Roo.Element}
13926      */
13927     getResizeChild : function(){
13928         return this.resizeChild;
13929     },
13930
13931     /**
13932      * Destroys this resizable. If the element was wrapped and
13933      * removeEl is not true then the element remains.
13934      * @param {Boolean} removeEl (optional) true to remove the element from the DOM
13935      */
13936     destroy : function(removeEl){
13937         this.proxy.remove();
13938         if(this.overlay){
13939             this.overlay.removeAllListeners();
13940             this.overlay.remove();
13941         }
13942         var ps = Roo.Resizable.positions;
13943         for(var k in ps){
13944             if(typeof ps[k] != "function" && this[ps[k]]){
13945                 var h = this[ps[k]];
13946                 h.el.removeAllListeners();
13947                 h.el.remove();
13948             }
13949         }
13950         if(removeEl){
13951             this.el.update("");
13952             this.el.remove();
13953         }
13954     }
13955 });
13956
13957 // private
13958 // hash to map config positions to true positions
13959 Roo.Resizable.positions = {
13960     n: "north", s: "south", e: "east", w: "west", se: "southeast", sw: "southwest", nw: "northwest", ne: "northeast", 
13961     hd: "hdrag"
13962 };
13963
13964 // private
13965 Roo.Resizable.Handle = function(rz, pos, disableTrackOver, transparent){
13966     if(!this.tpl){
13967         // only initialize the template if resizable is used
13968         var tpl = Roo.DomHelper.createTemplate(
13969             {tag: "div", cls: "x-resizable-handle x-resizable-handle-{0}"}
13970         );
13971         tpl.compile();
13972         Roo.Resizable.Handle.prototype.tpl = tpl;
13973     }
13974     this.position = pos;
13975     this.rz = rz;
13976     // show north drag fro topdra
13977     var handlepos = pos == 'hdrag' ? 'north' : pos;
13978     
13979     this.el = this.tpl.append(rz.el.dom, [handlepos], true);
13980     if (pos == 'hdrag') {
13981         this.el.setStyle('cursor', 'pointer');
13982     }
13983     this.el.unselectable();
13984     if(transparent){
13985         this.el.setOpacity(0);
13986     }
13987     this.el.on("mousedown", this.onMouseDown, this);
13988     if(!disableTrackOver){
13989         this.el.on("mouseover", this.onMouseOver, this);
13990         this.el.on("mouseout", this.onMouseOut, this);
13991     }
13992 };
13993
13994 // private
13995 Roo.Resizable.Handle.prototype = {
13996     afterResize : function(rz){
13997         // do nothing
13998     },
13999     // private
14000     onMouseDown : function(e){
14001         this.rz.onMouseDown(this, e);
14002     },
14003     // private
14004     onMouseOver : function(e){
14005         this.rz.handleOver(this, e);
14006     },
14007     // private
14008     onMouseOut : function(e){
14009         this.rz.handleOut(this, e);
14010     }
14011 };/*
14012  * Based on:
14013  * Ext JS Library 1.1.1
14014  * Copyright(c) 2006-2007, Ext JS, LLC.
14015  *
14016  * Originally Released Under LGPL - original licence link has changed is not relivant.
14017  *
14018  * Fork - LGPL
14019  * <script type="text/javascript">
14020  */
14021
14022 /**
14023  * @class Roo.Editor
14024  * @extends Roo.Component
14025  * A base editor field that handles displaying/hiding on demand and has some built-in sizing and event handling logic.
14026  * @constructor
14027  * Create a new Editor
14028  * @param {Roo.form.Field} field The Field object (or descendant)
14029  * @param {Object} config The config object
14030  */
14031 Roo.Editor = function(field, config){
14032     Roo.Editor.superclass.constructor.call(this, config);
14033     this.field = field;
14034     this.addEvents({
14035         /**
14036              * @event beforestartedit
14037              * Fires when editing is initiated, but before the value changes.  Editing can be canceled by returning
14038              * false from the handler of this event.
14039              * @param {Editor} this
14040              * @param {Roo.Element} boundEl The underlying element bound to this editor
14041              * @param {Mixed} value The field value being set
14042              */
14043         "beforestartedit" : true,
14044         /**
14045              * @event startedit
14046              * Fires when this editor is displayed
14047              * @param {Roo.Element} boundEl The underlying element bound to this editor
14048              * @param {Mixed} value The starting field value
14049              */
14050         "startedit" : true,
14051         /**
14052              * @event beforecomplete
14053              * Fires after a change has been made to the field, but before the change is reflected in the underlying
14054              * field.  Saving the change to the field can be canceled by returning false from the handler of this event.
14055              * Note that if the value has not changed and ignoreNoChange = true, the editing will still end but this
14056              * event will not fire since no edit actually occurred.
14057              * @param {Editor} this
14058              * @param {Mixed} value The current field value
14059              * @param {Mixed} startValue The original field value
14060              */
14061         "beforecomplete" : true,
14062         /**
14063              * @event complete
14064              * Fires after editing is complete and any changed value has been written to the underlying field.
14065              * @param {Editor} this
14066              * @param {Mixed} value The current field value
14067              * @param {Mixed} startValue The original field value
14068              */
14069         "complete" : true,
14070         /**
14071          * @event specialkey
14072          * Fires when any key related to navigation (arrows, tab, enter, esc, etc.) is pressed.  You can check
14073          * {@link Roo.EventObject#getKey} to determine which key was pressed.
14074          * @param {Roo.form.Field} this
14075          * @param {Roo.EventObject} e The event object
14076          */
14077         "specialkey" : true
14078     });
14079 };
14080
14081 Roo.extend(Roo.Editor, Roo.Component, {
14082     /**
14083      * @cfg {Boolean/String} autosize
14084      * True for the editor to automatically adopt the size of the underlying field, "width" to adopt the width only,
14085      * or "height" to adopt the height only (defaults to false)
14086      */
14087     /**
14088      * @cfg {Boolean} revertInvalid
14089      * True to automatically revert the field value and cancel the edit when the user completes an edit and the field
14090      * validation fails (defaults to true)
14091      */
14092     /**
14093      * @cfg {Boolean} ignoreNoChange
14094      * True to skip the the edit completion process (no save, no events fired) if the user completes an edit and
14095      * the value has not changed (defaults to false).  Applies only to string values - edits for other data types
14096      * will never be ignored.
14097      */
14098     /**
14099      * @cfg {Boolean} hideEl
14100      * False to keep the bound element visible while the editor is displayed (defaults to true)
14101      */
14102     /**
14103      * @cfg {Mixed} value
14104      * The data value of the underlying field (defaults to "")
14105      */
14106     value : "",
14107     /**
14108      * @cfg {String} alignment
14109      * The position to align to (see {@link Roo.Element#alignTo} for more details, defaults to "c-c?").
14110      */
14111     alignment: "c-c?",
14112     /**
14113      * @cfg {Boolean/String} shadow "sides" for sides/bottom only, "frame" for 4-way shadow, and "drop"
14114      * for bottom-right shadow (defaults to "frame")
14115      */
14116     shadow : "frame",
14117     /**
14118      * @cfg {Boolean} constrain True to constrain the editor to the viewport
14119      */
14120     constrain : false,
14121     /**
14122      * @cfg {Boolean} completeOnEnter True to complete the edit when the enter key is pressed (defaults to false)
14123      */
14124     completeOnEnter : false,
14125     /**
14126      * @cfg {Boolean} cancelOnEsc True to cancel the edit when the escape key is pressed (defaults to false)
14127      */
14128     cancelOnEsc : false,
14129     /**
14130      * @cfg {Boolean} updateEl True to update the innerHTML of the bound element when the update completes (defaults to false)
14131      */
14132     updateEl : false,
14133
14134     // private
14135     onRender : function(ct, position){
14136         this.el = new Roo.Layer({
14137             shadow: this.shadow,
14138             cls: "x-editor",
14139             parentEl : ct,
14140             shim : this.shim,
14141             shadowOffset:4,
14142             id: this.id,
14143             constrain: this.constrain
14144         });
14145         this.el.setStyle("overflow", Roo.isGecko ? "auto" : "hidden");
14146         if(this.field.msgTarget != 'title'){
14147             this.field.msgTarget = 'qtip';
14148         }
14149         this.field.render(this.el);
14150         if(Roo.isGecko){
14151             this.field.el.dom.setAttribute('autocomplete', 'off');
14152         }
14153         this.field.on("specialkey", this.onSpecialKey, this);
14154         if(this.swallowKeys){
14155             this.field.el.swallowEvent(['keydown','keypress']);
14156         }
14157         this.field.show();
14158         this.field.on("blur", this.onBlur, this);
14159         if(this.field.grow){
14160             this.field.on("autosize", this.el.sync,  this.el, {delay:1});
14161         }
14162     },
14163
14164     onSpecialKey : function(field, e)
14165     {
14166         //Roo.log('editor onSpecialKey');
14167         if(this.completeOnEnter && e.getKey() == e.ENTER){
14168             e.stopEvent();
14169             this.completeEdit();
14170             return;
14171         }
14172         // do not fire special key otherwise it might hide close the editor...
14173         if(e.getKey() == e.ENTER){    
14174             return;
14175         }
14176         if(this.cancelOnEsc && e.getKey() == e.ESC){
14177             this.cancelEdit();
14178             return;
14179         } 
14180         this.fireEvent('specialkey', field, e);
14181     
14182     },
14183
14184     /**
14185      * Starts the editing process and shows the editor.
14186      * @param {String/HTMLElement/Element} el The element to edit
14187      * @param {String} value (optional) A value to initialize the editor with. If a value is not provided, it defaults
14188       * to the innerHTML of el.
14189      */
14190     startEdit : function(el, value){
14191         if(this.editing){
14192             this.completeEdit();
14193         }
14194         this.boundEl = Roo.get(el);
14195         var v = value !== undefined ? value : this.boundEl.dom.innerHTML;
14196         if(!this.rendered){
14197             this.render(this.parentEl || document.body);
14198         }
14199         if(this.fireEvent("beforestartedit", this, this.boundEl, v) === false){
14200             return;
14201         }
14202         this.startValue = v;
14203         this.field.setValue(v);
14204         if(this.autoSize){
14205             var sz = this.boundEl.getSize();
14206             switch(this.autoSize){
14207                 case "width":
14208                 this.setSize(sz.width,  "");
14209                 break;
14210                 case "height":
14211                 this.setSize("",  sz.height);
14212                 break;
14213                 default:
14214                 this.setSize(sz.width,  sz.height);
14215             }
14216         }
14217         this.el.alignTo(this.boundEl, this.alignment);
14218         this.editing = true;
14219         if(Roo.QuickTips){
14220             Roo.QuickTips.disable();
14221         }
14222         this.show();
14223     },
14224
14225     /**
14226      * Sets the height and width of this editor.
14227      * @param {Number} width The new width
14228      * @param {Number} height The new height
14229      */
14230     setSize : function(w, h){
14231         this.field.setSize(w, h);
14232         if(this.el){
14233             this.el.sync();
14234         }
14235     },
14236
14237     /**
14238      * Realigns the editor to the bound field based on the current alignment config value.
14239      */
14240     realign : function(){
14241         this.el.alignTo(this.boundEl, this.alignment);
14242     },
14243
14244     /**
14245      * Ends the editing process, persists the changed value to the underlying field, and hides the editor.
14246      * @param {Boolean} remainVisible Override the default behavior and keep the editor visible after edit (defaults to false)
14247      */
14248     completeEdit : function(remainVisible){
14249         if(!this.editing){
14250             return;
14251         }
14252         var v = this.getValue();
14253         if(this.revertInvalid !== false && !this.field.isValid()){
14254             v = this.startValue;
14255             this.cancelEdit(true);
14256         }
14257         if(String(v) === String(this.startValue) && this.ignoreNoChange){
14258             this.editing = false;
14259             this.hide();
14260             return;
14261         }
14262         if(this.fireEvent("beforecomplete", this, v, this.startValue) !== false){
14263             this.editing = false;
14264             if(this.updateEl && this.boundEl){
14265                 this.boundEl.update(v);
14266             }
14267             if(remainVisible !== true){
14268                 this.hide();
14269             }
14270             this.fireEvent("complete", this, v, this.startValue);
14271         }
14272     },
14273
14274     // private
14275     onShow : function(){
14276         this.el.show();
14277         if(this.hideEl !== false){
14278             this.boundEl.hide();
14279         }
14280         this.field.show();
14281         if(Roo.isIE && !this.fixIEFocus){ // IE has problems with focusing the first time
14282             this.fixIEFocus = true;
14283             this.deferredFocus.defer(50, this);
14284         }else{
14285             this.field.focus();
14286         }
14287         this.fireEvent("startedit", this.boundEl, this.startValue);
14288     },
14289
14290     deferredFocus : function(){
14291         if(this.editing){
14292             this.field.focus();
14293         }
14294     },
14295
14296     /**
14297      * Cancels the editing process and hides the editor without persisting any changes.  The field value will be
14298      * reverted to the original starting value.
14299      * @param {Boolean} remainVisible Override the default behavior and keep the editor visible after
14300      * cancel (defaults to false)
14301      */
14302     cancelEdit : function(remainVisible){
14303         if(this.editing){
14304             this.setValue(this.startValue);
14305             if(remainVisible !== true){
14306                 this.hide();
14307             }
14308         }
14309     },
14310
14311     // private
14312     onBlur : function(){
14313         if(this.allowBlur !== true && this.editing){
14314             this.completeEdit();
14315         }
14316     },
14317
14318     // private
14319     onHide : function(){
14320         if(this.editing){
14321             this.completeEdit();
14322             return;
14323         }
14324         this.field.blur();
14325         if(this.field.collapse){
14326             this.field.collapse();
14327         }
14328         this.el.hide();
14329         if(this.hideEl !== false){
14330             this.boundEl.show();
14331         }
14332         if(Roo.QuickTips){
14333             Roo.QuickTips.enable();
14334         }
14335     },
14336
14337     /**
14338      * Sets the data value of the editor
14339      * @param {Mixed} value Any valid value supported by the underlying field
14340      */
14341     setValue : function(v){
14342         this.field.setValue(v);
14343     },
14344
14345     /**
14346      * Gets the data value of the editor
14347      * @return {Mixed} The data value
14348      */
14349     getValue : function(){
14350         return this.field.getValue();
14351     }
14352 });/*
14353  * Based on:
14354  * Ext JS Library 1.1.1
14355  * Copyright(c) 2006-2007, Ext JS, LLC.
14356  *
14357  * Originally Released Under LGPL - original licence link has changed is not relivant.
14358  *
14359  * Fork - LGPL
14360  * <script type="text/javascript">
14361  */
14362  
14363 /**
14364  * @class Roo.BasicDialog
14365  * @extends Roo.util.Observable
14366  * Lightweight Dialog Class.  The code below shows the creation of a typical dialog using existing HTML markup:
14367  * <pre><code>
14368 var dlg = new Roo.BasicDialog("my-dlg", {
14369     height: 200,
14370     width: 300,
14371     minHeight: 100,
14372     minWidth: 150,
14373     modal: true,
14374     proxyDrag: true,
14375     shadow: true
14376 });
14377 dlg.addKeyListener(27, dlg.hide, dlg); // ESC can also close the dialog
14378 dlg.addButton('OK', dlg.hide, dlg);    // Could call a save function instead of hiding
14379 dlg.addButton('Cancel', dlg.hide, dlg);
14380 dlg.show();
14381 </code></pre>
14382   <b>A Dialog should always be a direct child of the body element.</b>
14383  * @cfg {Boolean/DomHelper} autoCreate True to auto create from scratch, or using a DomHelper Object (defaults to false)
14384  * @cfg {String} title Default text to display in the title bar (defaults to null)
14385  * @cfg {Number} width Width of the dialog in pixels (can also be set via CSS).  Determined by browser if unspecified.
14386  * @cfg {Number} height Height of the dialog in pixels (can also be set via CSS).  Determined by browser if unspecified.
14387  * @cfg {Number} x The default left page coordinate of the dialog (defaults to center screen)
14388  * @cfg {Number} y The default top page coordinate of the dialog (defaults to center screen)
14389  * @cfg {String/Element} animateTarget Id or element from which the dialog should animate while opening
14390  * (defaults to null with no animation)
14391  * @cfg {Boolean} resizable False to disable manual dialog resizing (defaults to true)
14392  * @cfg {String} resizeHandles Which resize handles to display - see the {@link Roo.Resizable} handles config
14393  * property for valid values (defaults to 'all')
14394  * @cfg {Number} minHeight The minimum allowable height for a resizable dialog (defaults to 80)
14395  * @cfg {Number} minWidth The minimum allowable width for a resizable dialog (defaults to 200)
14396  * @cfg {Boolean} modal True to show the dialog modally, preventing user interaction with the rest of the page (defaults to false)
14397  * @cfg {Boolean} autoScroll True to allow the dialog body contents to overflow and display scrollbars (defaults to false)
14398  * @cfg {Boolean} closable False to remove the built-in top-right corner close button (defaults to true)
14399  * @cfg {Boolean} collapsible False to remove the built-in top-right corner collapse button (defaults to true)
14400  * @cfg {Boolean} constraintoviewport True to keep the dialog constrained within the visible viewport boundaries (defaults to true)
14401  * @cfg {Boolean} syncHeightBeforeShow True to cause the dimensions to be recalculated before the dialog is shown (defaults to false)
14402  * @cfg {Boolean} draggable False to disable dragging of the dialog within the viewport (defaults to true)
14403  * @cfg {Boolean} autoTabs If true, all elements with class 'x-dlg-tab' will get automatically converted to tabs (defaults to false)
14404  * @cfg {String} tabTag The tag name of tab elements, used when autoTabs = true (defaults to 'div')
14405  * @cfg {Boolean} proxyDrag True to drag a lightweight proxy element rather than the dialog itself, used when
14406  * draggable = true (defaults to false)
14407  * @cfg {Boolean} fixedcenter True to ensure that anytime the dialog is shown or resized it gets centered (defaults to false)
14408  * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
14409  * shadow (defaults to false)
14410  * @cfg {Number} shadowOffset The number of pixels to offset the shadow if displayed (defaults to 5)
14411  * @cfg {String} buttonAlign Valid values are "left," "center" and "right" (defaults to "right")
14412  * @cfg {Number} minButtonWidth Minimum width of all dialog buttons (defaults to 75)
14413  * @cfg {Array} buttons Array of buttons
14414  * @cfg {Boolean} shim True to create an iframe shim that prevents selects from showing through (defaults to false)
14415  * @constructor
14416  * Create a new BasicDialog.
14417  * @param {String/HTMLElement/Roo.Element} el The container element or DOM node, or its id
14418  * @param {Object} config Configuration options
14419  */
14420 Roo.BasicDialog = function(el, config){
14421     this.el = Roo.get(el);
14422     var dh = Roo.DomHelper;
14423     if(!this.el && config && config.autoCreate){
14424         if(typeof config.autoCreate == "object"){
14425             if(!config.autoCreate.id){
14426                 config.autoCreate.id = el;
14427             }
14428             this.el = dh.append(document.body,
14429                         config.autoCreate, true);
14430         }else{
14431             this.el = dh.append(document.body,
14432                         {tag: "div", id: el, style:'visibility:hidden;'}, true);
14433         }
14434     }
14435     el = this.el;
14436     el.setDisplayed(true);
14437     el.hide = this.hideAction;
14438     this.id = el.id;
14439     el.addClass("x-dlg");
14440
14441     Roo.apply(this, config);
14442
14443     this.proxy = el.createProxy("x-dlg-proxy");
14444     this.proxy.hide = this.hideAction;
14445     this.proxy.setOpacity(.5);
14446     this.proxy.hide();
14447
14448     if(config.width){
14449         el.setWidth(config.width);
14450     }
14451     if(config.height){
14452         el.setHeight(config.height);
14453     }
14454     this.size = el.getSize();
14455     if(typeof config.x != "undefined" && typeof config.y != "undefined"){
14456         this.xy = [config.x,config.y];
14457     }else{
14458         this.xy = el.getCenterXY(true);
14459     }
14460     /** The header element @type Roo.Element */
14461     this.header = el.child("> .x-dlg-hd");
14462     /** The body element @type Roo.Element */
14463     this.body = el.child("> .x-dlg-bd");
14464     /** The footer element @type Roo.Element */
14465     this.footer = el.child("> .x-dlg-ft");
14466
14467     if(!this.header){
14468         this.header = el.createChild({tag: "div", cls:"x-dlg-hd", html: "&#160;"}, this.body ? this.body.dom : null);
14469     }
14470     if(!this.body){
14471         this.body = el.createChild({tag: "div", cls:"x-dlg-bd"});
14472     }
14473
14474     this.header.unselectable();
14475     if(this.title){
14476         this.header.update(this.title);
14477     }
14478     // this element allows the dialog to be focused for keyboard event
14479     this.focusEl = el.createChild({tag: "a", href:"#", cls:"x-dlg-focus", tabIndex:"-1"});
14480     this.focusEl.swallowEvent("click", true);
14481
14482     this.header.wrap({cls:"x-dlg-hd-right"}).wrap({cls:"x-dlg-hd-left"}, true);
14483
14484     // wrap the body and footer for special rendering
14485     this.bwrap = this.body.wrap({tag: "div", cls:"x-dlg-dlg-body"});
14486     if(this.footer){
14487         this.bwrap.dom.appendChild(this.footer.dom);
14488     }
14489
14490     this.bg = this.el.createChild({
14491         tag: "div", cls:"x-dlg-bg",
14492         html: '<div class="x-dlg-bg-left"><div class="x-dlg-bg-right"><div class="x-dlg-bg-center">&#160;</div></div></div>'
14493     });
14494     this.centerBg = this.bg.child("div.x-dlg-bg-center");
14495
14496
14497     if(this.autoScroll !== false && !this.autoTabs){
14498         this.body.setStyle("overflow", "auto");
14499     }
14500
14501     this.toolbox = this.el.createChild({cls: "x-dlg-toolbox"});
14502
14503     if(this.closable !== false){
14504         this.el.addClass("x-dlg-closable");
14505         this.close = this.toolbox.createChild({cls:"x-dlg-close"});
14506         this.close.on("click", this.closeClick, this);
14507         this.close.addClassOnOver("x-dlg-close-over");
14508     }
14509     if(this.collapsible !== false){
14510         this.collapseBtn = this.toolbox.createChild({cls:"x-dlg-collapse"});
14511         this.collapseBtn.on("click", this.collapseClick, this);
14512         this.collapseBtn.addClassOnOver("x-dlg-collapse-over");
14513         this.header.on("dblclick", this.collapseClick, this);
14514     }
14515     if(this.resizable !== false){
14516         this.el.addClass("x-dlg-resizable");
14517         this.resizer = new Roo.Resizable(el, {
14518             minWidth: this.minWidth || 80,
14519             minHeight:this.minHeight || 80,
14520             handles: this.resizeHandles || "all",
14521             pinned: true
14522         });
14523         this.resizer.on("beforeresize", this.beforeResize, this);
14524         this.resizer.on("resize", this.onResize, this);
14525     }
14526     if(this.draggable !== false){
14527         el.addClass("x-dlg-draggable");
14528         if (!this.proxyDrag) {
14529             var dd = new Roo.dd.DD(el.dom.id, "WindowDrag");
14530         }
14531         else {
14532             var dd = new Roo.dd.DDProxy(el.dom.id, "WindowDrag", {dragElId: this.proxy.id});
14533         }
14534         dd.setHandleElId(this.header.id);
14535         dd.endDrag = this.endMove.createDelegate(this);
14536         dd.startDrag = this.startMove.createDelegate(this);
14537         dd.onDrag = this.onDrag.createDelegate(this);
14538         dd.scroll = false;
14539         this.dd = dd;
14540     }
14541     if(this.modal){
14542         this.mask = dh.append(document.body, {tag: "div", cls:"x-dlg-mask"}, true);
14543         this.mask.enableDisplayMode("block");
14544         this.mask.hide();
14545         this.el.addClass("x-dlg-modal");
14546     }
14547     if(this.shadow){
14548         this.shadow = new Roo.Shadow({
14549             mode : typeof this.shadow == "string" ? this.shadow : "sides",
14550             offset : this.shadowOffset
14551         });
14552     }else{
14553         this.shadowOffset = 0;
14554     }
14555     if(Roo.useShims && this.shim !== false){
14556         this.shim = this.el.createShim();
14557         this.shim.hide = this.hideAction;
14558         this.shim.hide();
14559     }else{
14560         this.shim = false;
14561     }
14562     if(this.autoTabs){
14563         this.initTabs();
14564     }
14565     if (this.buttons) { 
14566         var bts= this.buttons;
14567         this.buttons = [];
14568         Roo.each(bts, function(b) {
14569             this.addButton(b);
14570         }, this);
14571     }
14572     
14573     
14574     this.addEvents({
14575         /**
14576          * @event keydown
14577          * Fires when a key is pressed
14578          * @param {Roo.BasicDialog} this
14579          * @param {Roo.EventObject} e
14580          */
14581         "keydown" : true,
14582         /**
14583          * @event move
14584          * Fires when this dialog is moved by the user.
14585          * @param {Roo.BasicDialog} this
14586          * @param {Number} x The new page X
14587          * @param {Number} y The new page Y
14588          */
14589         "move" : true,
14590         /**
14591          * @event resize
14592          * Fires when this dialog is resized by the user.
14593          * @param {Roo.BasicDialog} this
14594          * @param {Number} width The new width
14595          * @param {Number} height The new height
14596          */
14597         "resize" : true,
14598         /**
14599          * @event beforehide
14600          * Fires before this dialog is hidden.
14601          * @param {Roo.BasicDialog} this
14602          */
14603         "beforehide" : true,
14604         /**
14605          * @event hide
14606          * Fires when this dialog is hidden.
14607          * @param {Roo.BasicDialog} this
14608          */
14609         "hide" : true,
14610         /**
14611          * @event beforeshow
14612          * Fires before this dialog is shown.
14613          * @param {Roo.BasicDialog} this
14614          */
14615         "beforeshow" : true,
14616         /**
14617          * @event show
14618          * Fires when this dialog is shown.
14619          * @param {Roo.BasicDialog} this
14620          */
14621         "show" : true
14622     });
14623     el.on("keydown", this.onKeyDown, this);
14624     el.on("mousedown", this.toFront, this);
14625     Roo.EventManager.onWindowResize(this.adjustViewport, this, true);
14626     this.el.hide();
14627     Roo.DialogManager.register(this);
14628     Roo.BasicDialog.superclass.constructor.call(this);
14629 };
14630
14631 Roo.extend(Roo.BasicDialog, Roo.util.Observable, {
14632     shadowOffset: Roo.isIE ? 6 : 5,
14633     minHeight: 80,
14634     minWidth: 200,
14635     minButtonWidth: 75,
14636     defaultButton: null,
14637     buttonAlign: "right",
14638     tabTag: 'div',
14639     firstShow: true,
14640
14641     /**
14642      * Sets the dialog title text
14643      * @param {String} text The title text to display
14644      * @return {Roo.BasicDialog} this
14645      */
14646     setTitle : function(text){
14647         this.header.update(text);
14648         return this;
14649     },
14650
14651     // private
14652     closeClick : function(){
14653         this.hide();
14654     },
14655
14656     // private
14657     collapseClick : function(){
14658         this[this.collapsed ? "expand" : "collapse"]();
14659     },
14660
14661     /**
14662      * Collapses the dialog to its minimized state (only the title bar is visible).
14663      * Equivalent to the user clicking the collapse dialog button.
14664      */
14665     collapse : function(){
14666         if(!this.collapsed){
14667             this.collapsed = true;
14668             this.el.addClass("x-dlg-collapsed");
14669             this.restoreHeight = this.el.getHeight();
14670             this.resizeTo(this.el.getWidth(), this.header.getHeight());
14671         }
14672     },
14673
14674     /**
14675      * Expands a collapsed dialog back to its normal state.  Equivalent to the user
14676      * clicking the expand dialog button.
14677      */
14678     expand : function(){
14679         if(this.collapsed){
14680             this.collapsed = false;
14681             this.el.removeClass("x-dlg-collapsed");
14682             this.resizeTo(this.el.getWidth(), this.restoreHeight);
14683         }
14684     },
14685
14686     /**
14687      * Reinitializes the tabs component, clearing out old tabs and finding new ones.
14688      * @return {Roo.TabPanel} The tabs component
14689      */
14690     initTabs : function(){
14691         var tabs = this.getTabs();
14692         while(tabs.getTab(0)){
14693             tabs.removeTab(0);
14694         }
14695         this.el.select(this.tabTag+'.x-dlg-tab').each(function(el){
14696             var dom = el.dom;
14697             tabs.addTab(Roo.id(dom), dom.title);
14698             dom.title = "";
14699         });
14700         tabs.activate(0);
14701         return tabs;
14702     },
14703
14704     // private
14705     beforeResize : function(){
14706         this.resizer.minHeight = Math.max(this.minHeight, this.getHeaderFooterHeight(true)+40);
14707     },
14708
14709     // private
14710     onResize : function(){
14711         this.refreshSize();
14712         this.syncBodyHeight();
14713         this.adjustAssets();
14714         this.focus();
14715         this.fireEvent("resize", this, this.size.width, this.size.height);
14716     },
14717
14718     // private
14719     onKeyDown : function(e){
14720         if(this.isVisible()){
14721             this.fireEvent("keydown", this, e);
14722         }
14723     },
14724
14725     /**
14726      * Resizes the dialog.
14727      * @param {Number} width
14728      * @param {Number} height
14729      * @return {Roo.BasicDialog} this
14730      */
14731     resizeTo : function(width, height){
14732         this.el.setSize(width, height);
14733         this.size = {width: width, height: height};
14734         this.syncBodyHeight();
14735         if(this.fixedcenter){
14736             this.center();
14737         }
14738         if(this.isVisible()){
14739             this.constrainXY();
14740             this.adjustAssets();
14741         }
14742         this.fireEvent("resize", this, width, height);
14743         return this;
14744     },
14745
14746
14747     /**
14748      * Resizes the dialog to fit the specified content size.
14749      * @param {Number} width
14750      * @param {Number} height
14751      * @return {Roo.BasicDialog} this
14752      */
14753     setContentSize : function(w, h){
14754         h += this.getHeaderFooterHeight() + this.body.getMargins("tb");
14755         w += this.body.getMargins("lr") + this.bwrap.getMargins("lr") + this.centerBg.getPadding("lr");
14756         //if(!this.el.isBorderBox()){
14757             h +=  this.body.getPadding("tb") + this.bwrap.getBorderWidth("tb") + this.body.getBorderWidth("tb") + this.el.getBorderWidth("tb");
14758             w += this.body.getPadding("lr") + this.bwrap.getBorderWidth("lr") + this.body.getBorderWidth("lr") + this.bwrap.getPadding("lr") + this.el.getBorderWidth("lr");
14759         //}
14760         if(this.tabs){
14761             h += this.tabs.stripWrap.getHeight() + this.tabs.bodyEl.getMargins("tb") + this.tabs.bodyEl.getPadding("tb");
14762             w += this.tabs.bodyEl.getMargins("lr") + this.tabs.bodyEl.getPadding("lr");
14763         }
14764         this.resizeTo(w, h);
14765         return this;
14766     },
14767
14768     /**
14769      * Adds a key listener for when this dialog is displayed.  This allows you to hook in a function that will be
14770      * executed in response to a particular key being pressed while the dialog is active.
14771      * @param {Number/Array/Object} key Either the numeric key code, array of key codes or an object with the following options:
14772      *                                  {key: (number or array), shift: (true/false), ctrl: (true/false), alt: (true/false)}
14773      * @param {Function} fn The function to call
14774      * @param {Object} scope (optional) The scope of the function
14775      * @return {Roo.BasicDialog} this
14776      */
14777     addKeyListener : function(key, fn, scope){
14778         var keyCode, shift, ctrl, alt;
14779         if(typeof key == "object" && !(key instanceof Array)){
14780             keyCode = key["key"];
14781             shift = key["shift"];
14782             ctrl = key["ctrl"];
14783             alt = key["alt"];
14784         }else{
14785             keyCode = key;
14786         }
14787         var handler = function(dlg, e){
14788             if((!shift || e.shiftKey) && (!ctrl || e.ctrlKey) &&  (!alt || e.altKey)){
14789                 var k = e.getKey();
14790                 if(keyCode instanceof Array){
14791                     for(var i = 0, len = keyCode.length; i < len; i++){
14792                         if(keyCode[i] == k){
14793                           fn.call(scope || window, dlg, k, e);
14794                           return;
14795                         }
14796                     }
14797                 }else{
14798                     if(k == keyCode){
14799                         fn.call(scope || window, dlg, k, e);
14800                     }
14801                 }
14802             }
14803         };
14804         this.on("keydown", handler);
14805         return this;
14806     },
14807
14808     /**
14809      * Returns the TabPanel component (creates it if it doesn't exist).
14810      * Note: If you wish to simply check for the existence of tabs without creating them,
14811      * check for a null 'tabs' property.
14812      * @return {Roo.TabPanel} The tabs component
14813      */
14814     getTabs : function(){
14815         if(!this.tabs){
14816             this.el.addClass("x-dlg-auto-tabs");
14817             this.body.addClass(this.tabPosition == "bottom" ? "x-tabs-bottom" : "x-tabs-top");
14818             this.tabs = new Roo.TabPanel(this.body.dom, this.tabPosition == "bottom");
14819         }
14820         return this.tabs;
14821     },
14822
14823     /**
14824      * Adds a button to the footer section of the dialog.
14825      * @param {String/Object} config A string becomes the button text, an object can either be a Button config
14826      * object or a valid Roo.DomHelper element config
14827      * @param {Function} handler The function called when the button is clicked
14828      * @param {Object} scope (optional) The scope of the handler function (accepts position as a property)
14829      * @return {Roo.Button} The new button
14830      */
14831     addButton : function(config, handler, scope){
14832         var dh = Roo.DomHelper;
14833         if(!this.footer){
14834             this.footer = dh.append(this.bwrap, {tag: "div", cls:"x-dlg-ft"}, true);
14835         }
14836         if(!this.btnContainer){
14837             var tb = this.footer.createChild({
14838
14839                 cls:"x-dlg-btns x-dlg-btns-"+this.buttonAlign,
14840                 html:'<table cellspacing="0"><tbody><tr></tr></tbody></table><div class="x-clear"></div>'
14841             }, null, true);
14842             this.btnContainer = tb.firstChild.firstChild.firstChild;
14843         }
14844         var bconfig = {
14845             handler: handler,
14846             scope: scope,
14847             minWidth: this.minButtonWidth,
14848             hideParent:true
14849         };
14850         if(typeof config == "string"){
14851             bconfig.text = config;
14852         }else{
14853             if(config.tag){
14854                 bconfig.dhconfig = config;
14855             }else{
14856                 Roo.apply(bconfig, config);
14857             }
14858         }
14859         var fc = false;
14860         if ((typeof(bconfig.position) != 'undefined') && bconfig.position < this.btnContainer.childNodes.length-1) {
14861             bconfig.position = Math.max(0, bconfig.position);
14862             fc = this.btnContainer.childNodes[bconfig.position];
14863         }
14864          
14865         var btn = new Roo.Button(
14866             fc ? 
14867                 this.btnContainer.insertBefore(document.createElement("td"),fc)
14868                 : this.btnContainer.appendChild(document.createElement("td")),
14869             //Roo.get(this.btnContainer).createChild( { tag: 'td'},  fc ),
14870             bconfig
14871         );
14872         this.syncBodyHeight();
14873         if(!this.buttons){
14874             /**
14875              * Array of all the buttons that have been added to this dialog via addButton
14876              * @type Array
14877              */
14878             this.buttons = [];
14879         }
14880         this.buttons.push(btn);
14881         return btn;
14882     },
14883
14884     /**
14885      * Sets the default button to be focused when the dialog is displayed.
14886      * @param {Roo.BasicDialog.Button} btn The button object returned by {@link #addButton}
14887      * @return {Roo.BasicDialog} this
14888      */
14889     setDefaultButton : function(btn){
14890         this.defaultButton = btn;
14891         return this;
14892     },
14893
14894     // private
14895     getHeaderFooterHeight : function(safe){
14896         var height = 0;
14897         if(this.header){
14898            height += this.header.getHeight();
14899         }
14900         if(this.footer){
14901            var fm = this.footer.getMargins();
14902             height += (this.footer.getHeight()+fm.top+fm.bottom);
14903         }
14904         height += this.bwrap.getPadding("tb")+this.bwrap.getBorderWidth("tb");
14905         height += this.centerBg.getPadding("tb");
14906         return height;
14907     },
14908
14909     // private
14910     syncBodyHeight : function()
14911     {
14912         var bd = this.body, // the text
14913             cb = this.centerBg, // wrapper around bottom.. but does not seem to be used..
14914             bw = this.bwrap;
14915         var height = this.size.height - this.getHeaderFooterHeight(false);
14916         bd.setHeight(height-bd.getMargins("tb"));
14917         var hh = this.header.getHeight();
14918         var h = this.size.height-hh;
14919         cb.setHeight(h);
14920         
14921         bw.setLeftTop(cb.getPadding("l"), hh+cb.getPadding("t"));
14922         bw.setHeight(h-cb.getPadding("tb"));
14923         
14924         bw.setWidth(this.el.getWidth(true)-cb.getPadding("lr"));
14925         bd.setWidth(bw.getWidth(true));
14926         if(this.tabs){
14927             this.tabs.syncHeight();
14928             if(Roo.isIE){
14929                 this.tabs.el.repaint();
14930             }
14931         }
14932     },
14933
14934     /**
14935      * Restores the previous state of the dialog if Roo.state is configured.
14936      * @return {Roo.BasicDialog} this
14937      */
14938     restoreState : function(){
14939         var box = Roo.state.Manager.get(this.stateId || (this.el.id + "-state"));
14940         if(box && box.width){
14941             this.xy = [box.x, box.y];
14942             this.resizeTo(box.width, box.height);
14943         }
14944         return this;
14945     },
14946
14947     // private
14948     beforeShow : function(){
14949         this.expand();
14950         if(this.fixedcenter){
14951             this.xy = this.el.getCenterXY(true);
14952         }
14953         if(this.modal){
14954             Roo.get(document.body).addClass("x-body-masked");
14955             this.mask.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
14956             this.mask.show();
14957         }
14958         this.constrainXY();
14959     },
14960
14961     // private
14962     animShow : function(){
14963         var b = Roo.get(this.animateTarget).getBox();
14964         this.proxy.setSize(b.width, b.height);
14965         this.proxy.setLocation(b.x, b.y);
14966         this.proxy.show();
14967         this.proxy.setBounds(this.xy[0], this.xy[1], this.size.width, this.size.height,
14968                     true, .35, this.showEl.createDelegate(this));
14969     },
14970
14971     /**
14972      * Shows the dialog.
14973      * @param {String/HTMLElement/Roo.Element} animateTarget (optional) Reset the animation target
14974      * @return {Roo.BasicDialog} this
14975      */
14976     show : function(animateTarget){
14977         if (this.fireEvent("beforeshow", this) === false){
14978             return;
14979         }
14980         if(this.syncHeightBeforeShow){
14981             this.syncBodyHeight();
14982         }else if(this.firstShow){
14983             this.firstShow = false;
14984             this.syncBodyHeight(); // sync the height on the first show instead of in the constructor
14985         }
14986         this.animateTarget = animateTarget || this.animateTarget;
14987         if(!this.el.isVisible()){
14988             this.beforeShow();
14989             if(this.animateTarget && Roo.get(this.animateTarget)){
14990                 this.animShow();
14991             }else{
14992                 this.showEl();
14993             }
14994         }
14995         return this;
14996     },
14997
14998     // private
14999     showEl : function(){
15000         this.proxy.hide();
15001         this.el.setXY(this.xy);
15002         this.el.show();
15003         this.adjustAssets(true);
15004         this.toFront();
15005         this.focus();
15006         // IE peekaboo bug - fix found by Dave Fenwick
15007         if(Roo.isIE){
15008             this.el.repaint();
15009         }
15010         this.fireEvent("show", this);
15011     },
15012
15013     /**
15014      * Focuses the dialog.  If a defaultButton is set, it will receive focus, otherwise the
15015      * dialog itself will receive focus.
15016      */
15017     focus : function(){
15018         if(this.defaultButton){
15019             this.defaultButton.focus();
15020         }else{
15021             this.focusEl.focus();
15022         }
15023     },
15024
15025     // private
15026     constrainXY : function(){
15027         if(this.constraintoviewport !== false){
15028             if(!this.viewSize){
15029                 if(this.container){
15030                     var s = this.container.getSize();
15031                     this.viewSize = [s.width, s.height];
15032                 }else{
15033                     this.viewSize = [Roo.lib.Dom.getViewWidth(),Roo.lib.Dom.getViewHeight()];
15034                 }
15035             }
15036             var s = Roo.get(this.container||document).getScroll();
15037
15038             var x = this.xy[0], y = this.xy[1];
15039             var w = this.size.width, h = this.size.height;
15040             var vw = this.viewSize[0], vh = this.viewSize[1];
15041             // only move it if it needs it
15042             var moved = false;
15043             // first validate right/bottom
15044             if(x + w > vw+s.left){
15045                 x = vw - w;
15046                 moved = true;
15047             }
15048             if(y + h > vh+s.top){
15049                 y = vh - h;
15050                 moved = true;
15051             }
15052             // then make sure top/left isn't negative
15053             if(x < s.left){
15054                 x = s.left;
15055                 moved = true;
15056             }
15057             if(y < s.top){
15058                 y = s.top;
15059                 moved = true;
15060             }
15061             if(moved){
15062                 // cache xy
15063                 this.xy = [x, y];
15064                 if(this.isVisible()){
15065                     this.el.setLocation(x, y);
15066                     this.adjustAssets();
15067                 }
15068             }
15069         }
15070     },
15071
15072     // private
15073     onDrag : function(){
15074         if(!this.proxyDrag){
15075             this.xy = this.el.getXY();
15076             this.adjustAssets();
15077         }
15078     },
15079
15080     // private
15081     adjustAssets : function(doShow){
15082         var x = this.xy[0], y = this.xy[1];
15083         var w = this.size.width, h = this.size.height;
15084         if(doShow === true){
15085             if(this.shadow){
15086                 this.shadow.show(this.el);
15087             }
15088             if(this.shim){
15089                 this.shim.show();
15090             }
15091         }
15092         if(this.shadow && this.shadow.isVisible()){
15093             this.shadow.show(this.el);
15094         }
15095         if(this.shim && this.shim.isVisible()){
15096             this.shim.setBounds(x, y, w, h);
15097         }
15098     },
15099
15100     // private
15101     adjustViewport : function(w, h){
15102         if(!w || !h){
15103             w = Roo.lib.Dom.getViewWidth();
15104             h = Roo.lib.Dom.getViewHeight();
15105         }
15106         // cache the size
15107         this.viewSize = [w, h];
15108         if(this.modal && this.mask.isVisible()){
15109             this.mask.setSize(w, h); // first make sure the mask isn't causing overflow
15110             this.mask.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
15111         }
15112         if(this.isVisible()){
15113             this.constrainXY();
15114         }
15115     },
15116
15117     /**
15118      * Destroys this dialog and all its supporting elements (including any tabs, shim,
15119      * shadow, proxy, mask, etc.)  Also removes all event listeners.
15120      * @param {Boolean} removeEl (optional) true to remove the element from the DOM
15121      */
15122     destroy : function(removeEl){
15123         if(this.isVisible()){
15124             this.animateTarget = null;
15125             this.hide();
15126         }
15127         Roo.EventManager.removeResizeListener(this.adjustViewport, this);
15128         if(this.tabs){
15129             this.tabs.destroy(removeEl);
15130         }
15131         Roo.destroy(
15132              this.shim,
15133              this.proxy,
15134              this.resizer,
15135              this.close,
15136              this.mask
15137         );
15138         if(this.dd){
15139             this.dd.unreg();
15140         }
15141         if(this.buttons){
15142            for(var i = 0, len = this.buttons.length; i < len; i++){
15143                this.buttons[i].destroy();
15144            }
15145         }
15146         this.el.removeAllListeners();
15147         if(removeEl === true){
15148             this.el.update("");
15149             this.el.remove();
15150         }
15151         Roo.DialogManager.unregister(this);
15152     },
15153
15154     // private
15155     startMove : function(){
15156         if(this.proxyDrag){
15157             this.proxy.show();
15158         }
15159         if(this.constraintoviewport !== false){
15160             this.dd.constrainTo(document.body, {right: this.shadowOffset, bottom: this.shadowOffset});
15161         }
15162     },
15163
15164     // private
15165     endMove : function(){
15166         if(!this.proxyDrag){
15167             Roo.dd.DD.prototype.endDrag.apply(this.dd, arguments);
15168         }else{
15169             Roo.dd.DDProxy.prototype.endDrag.apply(this.dd, arguments);
15170             this.proxy.hide();
15171         }
15172         this.refreshSize();
15173         this.adjustAssets();
15174         this.focus();
15175         this.fireEvent("move", this, this.xy[0], this.xy[1]);
15176     },
15177
15178     /**
15179      * Brings this dialog to the front of any other visible dialogs
15180      * @return {Roo.BasicDialog} this
15181      */
15182     toFront : function(){
15183         Roo.DialogManager.bringToFront(this);
15184         return this;
15185     },
15186
15187     /**
15188      * Sends this dialog to the back (under) of any other visible dialogs
15189      * @return {Roo.BasicDialog} this
15190      */
15191     toBack : function(){
15192         Roo.DialogManager.sendToBack(this);
15193         return this;
15194     },
15195
15196     /**
15197      * Centers this dialog in the viewport
15198      * @return {Roo.BasicDialog} this
15199      */
15200     center : function(){
15201         var xy = this.el.getCenterXY(true);
15202         this.moveTo(xy[0], xy[1]);
15203         return this;
15204     },
15205
15206     /**
15207      * Moves the dialog's top-left corner to the specified point
15208      * @param {Number} x
15209      * @param {Number} y
15210      * @return {Roo.BasicDialog} this
15211      */
15212     moveTo : function(x, y){
15213         this.xy = [x,y];
15214         if(this.isVisible()){
15215             this.el.setXY(this.xy);
15216             this.adjustAssets();
15217         }
15218         return this;
15219     },
15220
15221     /**
15222      * Aligns the dialog to the specified element
15223      * @param {String/HTMLElement/Roo.Element} element The element to align to.
15224      * @param {String} position The position to align to (see {@link Roo.Element#alignTo} for more details).
15225      * @param {Array} offsets (optional) Offset the positioning by [x, y]
15226      * @return {Roo.BasicDialog} this
15227      */
15228     alignTo : function(element, position, offsets){
15229         this.xy = this.el.getAlignToXY(element, position, offsets);
15230         if(this.isVisible()){
15231             this.el.setXY(this.xy);
15232             this.adjustAssets();
15233         }
15234         return this;
15235     },
15236
15237     /**
15238      * Anchors an element to another element and realigns it when the window is resized.
15239      * @param {String/HTMLElement/Roo.Element} element The element to align to.
15240      * @param {String} position The position to align to (see {@link Roo.Element#alignTo} for more details)
15241      * @param {Array} offsets (optional) Offset the positioning by [x, y]
15242      * @param {Boolean/Number} monitorScroll (optional) true to monitor body scroll and reposition. If this parameter
15243      * is a number, it is used as the buffer delay (defaults to 50ms).
15244      * @return {Roo.BasicDialog} this
15245      */
15246     anchorTo : function(el, alignment, offsets, monitorScroll){
15247         var action = function(){
15248             this.alignTo(el, alignment, offsets);
15249         };
15250         Roo.EventManager.onWindowResize(action, this);
15251         var tm = typeof monitorScroll;
15252         if(tm != 'undefined'){
15253             Roo.EventManager.on(window, 'scroll', action, this,
15254                 {buffer: tm == 'number' ? monitorScroll : 50});
15255         }
15256         action.call(this);
15257         return this;
15258     },
15259
15260     /**
15261      * Returns true if the dialog is visible
15262      * @return {Boolean}
15263      */
15264     isVisible : function(){
15265         return this.el.isVisible();
15266     },
15267
15268     // private
15269     animHide : function(callback){
15270         var b = Roo.get(this.animateTarget).getBox();
15271         this.proxy.show();
15272         this.proxy.setBounds(this.xy[0], this.xy[1], this.size.width, this.size.height);
15273         this.el.hide();
15274         this.proxy.setBounds(b.x, b.y, b.width, b.height, true, .35,
15275                     this.hideEl.createDelegate(this, [callback]));
15276     },
15277
15278     /**
15279      * Hides the dialog.
15280      * @param {Function} callback (optional) Function to call when the dialog is hidden
15281      * @return {Roo.BasicDialog} this
15282      */
15283     hide : function(callback){
15284         if (this.fireEvent("beforehide", this) === false){
15285             return;
15286         }
15287         if(this.shadow){
15288             this.shadow.hide();
15289         }
15290         if(this.shim) {
15291           this.shim.hide();
15292         }
15293         // sometimes animateTarget seems to get set.. causing problems...
15294         // this just double checks..
15295         if(this.animateTarget && Roo.get(this.animateTarget)) {
15296            this.animHide(callback);
15297         }else{
15298             this.el.hide();
15299             this.hideEl(callback);
15300         }
15301         return this;
15302     },
15303
15304     // private
15305     hideEl : function(callback){
15306         this.proxy.hide();
15307         if(this.modal){
15308             this.mask.hide();
15309             Roo.get(document.body).removeClass("x-body-masked");
15310         }
15311         this.fireEvent("hide", this);
15312         if(typeof callback == "function"){
15313             callback();
15314         }
15315     },
15316
15317     // private
15318     hideAction : function(){
15319         this.setLeft("-10000px");
15320         this.setTop("-10000px");
15321         this.setStyle("visibility", "hidden");
15322     },
15323
15324     // private
15325     refreshSize : function(){
15326         this.size = this.el.getSize();
15327         this.xy = this.el.getXY();
15328         Roo.state.Manager.set(this.stateId || this.el.id + "-state", this.el.getBox());
15329     },
15330
15331     // private
15332     // z-index is managed by the DialogManager and may be overwritten at any time
15333     setZIndex : function(index){
15334         if(this.modal){
15335             this.mask.setStyle("z-index", index);
15336         }
15337         if(this.shim){
15338             this.shim.setStyle("z-index", ++index);
15339         }
15340         if(this.shadow){
15341             this.shadow.setZIndex(++index);
15342         }
15343         this.el.setStyle("z-index", ++index);
15344         if(this.proxy){
15345             this.proxy.setStyle("z-index", ++index);
15346         }
15347         if(this.resizer){
15348             this.resizer.proxy.setStyle("z-index", ++index);
15349         }
15350
15351         this.lastZIndex = index;
15352     },
15353
15354     /**
15355      * Returns the element for this dialog
15356      * @return {Roo.Element} The underlying dialog Element
15357      */
15358     getEl : function(){
15359         return this.el;
15360     }
15361 });
15362
15363 /**
15364  * @class Roo.DialogManager
15365  * Provides global access to BasicDialogs that have been created and
15366  * support for z-indexing (layering) multiple open dialogs.
15367  */
15368 Roo.DialogManager = function(){
15369     var list = {};
15370     var accessList = [];
15371     var front = null;
15372
15373     // private
15374     var sortDialogs = function(d1, d2){
15375         return (!d1._lastAccess || d1._lastAccess < d2._lastAccess) ? -1 : 1;
15376     };
15377
15378     // private
15379     var orderDialogs = function(){
15380         accessList.sort(sortDialogs);
15381         var seed = Roo.DialogManager.zseed;
15382         for(var i = 0, len = accessList.length; i < len; i++){
15383             var dlg = accessList[i];
15384             if(dlg){
15385                 dlg.setZIndex(seed + (i*10));
15386             }
15387         }
15388     };
15389
15390     return {
15391         /**
15392          * The starting z-index for BasicDialogs (defaults to 9000)
15393          * @type Number The z-index value
15394          */
15395         zseed : 9000,
15396
15397         // private
15398         register : function(dlg){
15399             list[dlg.id] = dlg;
15400             accessList.push(dlg);
15401         },
15402
15403         // private
15404         unregister : function(dlg){
15405             delete list[dlg.id];
15406             var i=0;
15407             var len=0;
15408             if(!accessList.indexOf){
15409                 for(  i = 0, len = accessList.length; i < len; i++){
15410                     if(accessList[i] == dlg){
15411                         accessList.splice(i, 1);
15412                         return;
15413                     }
15414                 }
15415             }else{
15416                  i = accessList.indexOf(dlg);
15417                 if(i != -1){
15418                     accessList.splice(i, 1);
15419                 }
15420             }
15421         },
15422
15423         /**
15424          * Gets a registered dialog by id
15425          * @param {String/Object} id The id of the dialog or a dialog
15426          * @return {Roo.BasicDialog} this
15427          */
15428         get : function(id){
15429             return typeof id == "object" ? id : list[id];
15430         },
15431
15432         /**
15433          * Brings the specified dialog to the front
15434          * @param {String/Object} dlg The id of the dialog or a dialog
15435          * @return {Roo.BasicDialog} this
15436          */
15437         bringToFront : function(dlg){
15438             dlg = this.get(dlg);
15439             if(dlg != front){
15440                 front = dlg;
15441                 dlg._lastAccess = new Date().getTime();
15442                 orderDialogs();
15443             }
15444             return dlg;
15445         },
15446
15447         /**
15448          * Sends the specified dialog to the back
15449          * @param {String/Object} dlg The id of the dialog or a dialog
15450          * @return {Roo.BasicDialog} this
15451          */
15452         sendToBack : function(dlg){
15453             dlg = this.get(dlg);
15454             dlg._lastAccess = -(new Date().getTime());
15455             orderDialogs();
15456             return dlg;
15457         },
15458
15459         /**
15460          * Hides all dialogs
15461          */
15462         hideAll : function(){
15463             for(var id in list){
15464                 if(list[id] && typeof list[id] != "function" && list[id].isVisible()){
15465                     list[id].hide();
15466                 }
15467             }
15468         }
15469     };
15470 }();
15471
15472 /**
15473  * @class Roo.LayoutDialog
15474  * @extends Roo.BasicDialog
15475  * Dialog which provides adjustments for working with a layout in a Dialog.
15476  * Add your necessary layout config options to the dialog's config.<br>
15477  * Example usage (including a nested layout):
15478  * <pre><code>
15479 if(!dialog){
15480     dialog = new Roo.LayoutDialog("download-dlg", {
15481         modal: true,
15482         width:600,
15483         height:450,
15484         shadow:true,
15485         minWidth:500,
15486         minHeight:350,
15487         autoTabs:true,
15488         proxyDrag:true,
15489         // layout config merges with the dialog config
15490         center:{
15491             tabPosition: "top",
15492             alwaysShowTabs: true
15493         }
15494     });
15495     dialog.addKeyListener(27, dialog.hide, dialog);
15496     dialog.setDefaultButton(dialog.addButton("Close", dialog.hide, dialog));
15497     dialog.addButton("Build It!", this.getDownload, this);
15498
15499     // we can even add nested layouts
15500     var innerLayout = new Roo.BorderLayout("dl-inner", {
15501         east: {
15502             initialSize: 200,
15503             autoScroll:true,
15504             split:true
15505         },
15506         center: {
15507             autoScroll:true
15508         }
15509     });
15510     innerLayout.beginUpdate();
15511     innerLayout.add("east", new Roo.ContentPanel("dl-details"));
15512     innerLayout.add("center", new Roo.ContentPanel("selection-panel"));
15513     innerLayout.endUpdate(true);
15514
15515     var layout = dialog.getLayout();
15516     layout.beginUpdate();
15517     layout.add("center", new Roo.ContentPanel("standard-panel",
15518                         {title: "Download the Source", fitToFrame:true}));
15519     layout.add("center", new Roo.NestedLayoutPanel(innerLayout,
15520                {title: "Build your own roo.js"}));
15521     layout.getRegion("center").showPanel(sp);
15522     layout.endUpdate();
15523 }
15524 </code></pre>
15525     * @constructor
15526     * @param {String/HTMLElement/Roo.Element} el The id of or container element, or config
15527     * @param {Object} config configuration options
15528   */
15529 Roo.LayoutDialog = function(el, cfg){
15530     
15531     var config=  cfg;
15532     if (typeof(cfg) == 'undefined') {
15533         config = Roo.apply({}, el);
15534         // not sure why we use documentElement here.. - it should always be body.
15535         // IE7 borks horribly if we use documentElement.
15536         // webkit also does not like documentElement - it creates a body element...
15537         el = Roo.get( document.body || document.documentElement ).createChild();
15538         //config.autoCreate = true;
15539     }
15540     
15541     
15542     config.autoTabs = false;
15543     Roo.LayoutDialog.superclass.constructor.call(this, el, config);
15544     this.body.setStyle({overflow:"hidden", position:"relative"});
15545     this.layout = new Roo.BorderLayout(this.body.dom, config);
15546     this.layout.monitorWindowResize = false;
15547     this.el.addClass("x-dlg-auto-layout");
15548     // fix case when center region overwrites center function
15549     this.center = Roo.BasicDialog.prototype.center;
15550     this.on("show", this.layout.layout, this.layout, true);
15551     if (config.items) {
15552         var xitems = config.items;
15553         delete config.items;
15554         Roo.each(xitems, this.addxtype, this);
15555     }
15556     
15557     
15558 };
15559 Roo.extend(Roo.LayoutDialog, Roo.BasicDialog, {
15560     /**
15561      * Ends update of the layout <strike>and resets display to none</strike>. Use standard beginUpdate/endUpdate on the layout.
15562      * @deprecated
15563      */
15564     endUpdate : function(){
15565         this.layout.endUpdate();
15566     },
15567
15568     /**
15569      * Begins an update of the layout <strike>and sets display to block and visibility to hidden</strike>. Use standard beginUpdate/endUpdate on the layout.
15570      *  @deprecated
15571      */
15572     beginUpdate : function(){
15573         this.layout.beginUpdate();
15574     },
15575
15576     /**
15577      * Get the BorderLayout for this dialog
15578      * @return {Roo.BorderLayout}
15579      */
15580     getLayout : function(){
15581         return this.layout;
15582     },
15583
15584     showEl : function(){
15585         Roo.LayoutDialog.superclass.showEl.apply(this, arguments);
15586         if(Roo.isIE7){
15587             this.layout.layout();
15588         }
15589     },
15590
15591     // private
15592     // Use the syncHeightBeforeShow config option to control this automatically
15593     syncBodyHeight : function(){
15594         Roo.LayoutDialog.superclass.syncBodyHeight.call(this);
15595         if(this.layout){this.layout.layout();}
15596     },
15597     
15598       /**
15599      * Add an xtype element (actually adds to the layout.)
15600      * @return {Object} xdata xtype object data.
15601      */
15602     
15603     addxtype : function(c) {
15604         return this.layout.addxtype(c);
15605     }
15606 });/*
15607  * Based on:
15608  * Ext JS Library 1.1.1
15609  * Copyright(c) 2006-2007, Ext JS, LLC.
15610  *
15611  * Originally Released Under LGPL - original licence link has changed is not relivant.
15612  *
15613  * Fork - LGPL
15614  * <script type="text/javascript">
15615  */
15616  
15617 /**
15618  * @class Roo.MessageBox
15619  * Utility class for generating different styles of message boxes.  The alias Roo.Msg can also be used.
15620  * Example usage:
15621  *<pre><code>
15622 // Basic alert:
15623 Roo.Msg.alert('Status', 'Changes saved successfully.');
15624
15625 // Prompt for user data:
15626 Roo.Msg.prompt('Name', 'Please enter your name:', function(btn, text){
15627     if (btn == 'ok'){
15628         // process text value...
15629     }
15630 });
15631
15632 // Show a dialog using config options:
15633 Roo.Msg.show({
15634    title:'Save Changes?',
15635    msg: 'Your are closing a tab that has unsaved changes. Would you like to save your changes?',
15636    buttons: Roo.Msg.YESNOCANCEL,
15637    fn: processResult,
15638    animEl: 'elId'
15639 });
15640 </code></pre>
15641  * @singleton
15642  */
15643 Roo.MessageBox = function(){
15644     var dlg, opt, mask, waitTimer;
15645     var bodyEl, msgEl, textboxEl, textareaEl, progressEl, pp;
15646     var buttons, activeTextEl, bwidth;
15647
15648     // private
15649     var handleButton = function(button){
15650         dlg.hide();
15651         Roo.callback(opt.fn, opt.scope||window, [button, activeTextEl.dom.value], 1);
15652     };
15653
15654     // private
15655     var handleHide = function(){
15656         if(opt && opt.cls){
15657             dlg.el.removeClass(opt.cls);
15658         }
15659         if(waitTimer){
15660             Roo.TaskMgr.stop(waitTimer);
15661             waitTimer = null;
15662         }
15663     };
15664
15665     // private
15666     var updateButtons = function(b){
15667         var width = 0;
15668         if(!b){
15669             buttons["ok"].hide();
15670             buttons["cancel"].hide();
15671             buttons["yes"].hide();
15672             buttons["no"].hide();
15673             dlg.footer.dom.style.display = 'none';
15674             return width;
15675         }
15676         dlg.footer.dom.style.display = '';
15677         for(var k in buttons){
15678             if(typeof buttons[k] != "function"){
15679                 if(b[k]){
15680                     buttons[k].show();
15681                     buttons[k].setText(typeof b[k] == "string" ? b[k] : Roo.MessageBox.buttonText[k]);
15682                     width += buttons[k].el.getWidth()+15;
15683                 }else{
15684                     buttons[k].hide();
15685                 }
15686             }
15687         }
15688         return width;
15689     };
15690
15691     // private
15692     var handleEsc = function(d, k, e){
15693         if(opt && opt.closable !== false){
15694             dlg.hide();
15695         }
15696         if(e){
15697             e.stopEvent();
15698         }
15699     };
15700
15701     return {
15702         /**
15703          * Returns a reference to the underlying {@link Roo.BasicDialog} element
15704          * @return {Roo.BasicDialog} The BasicDialog element
15705          */
15706         getDialog : function(){
15707            if(!dlg){
15708                 dlg = new Roo.BasicDialog("x-msg-box", {
15709                     autoCreate : true,
15710                     shadow: true,
15711                     draggable: true,
15712                     resizable:false,
15713                     constraintoviewport:false,
15714                     fixedcenter:true,
15715                     collapsible : false,
15716                     shim:true,
15717                     modal: true,
15718                     width:400, height:100,
15719                     buttonAlign:"center",
15720                     closeClick : function(){
15721                         if(opt && opt.buttons && opt.buttons.no && !opt.buttons.cancel){
15722                             handleButton("no");
15723                         }else{
15724                             handleButton("cancel");
15725                         }
15726                     }
15727                 });
15728                 dlg.on("hide", handleHide);
15729                 mask = dlg.mask;
15730                 dlg.addKeyListener(27, handleEsc);
15731                 buttons = {};
15732                 var bt = this.buttonText;
15733                 buttons["ok"] = dlg.addButton(bt["ok"], handleButton.createCallback("ok"));
15734                 buttons["yes"] = dlg.addButton(bt["yes"], handleButton.createCallback("yes"));
15735                 buttons["no"] = dlg.addButton(bt["no"], handleButton.createCallback("no"));
15736                 buttons["cancel"] = dlg.addButton(bt["cancel"], handleButton.createCallback("cancel"));
15737                 bodyEl = dlg.body.createChild({
15738
15739                     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>'
15740                 });
15741                 msgEl = bodyEl.dom.firstChild;
15742                 textboxEl = Roo.get(bodyEl.dom.childNodes[2]);
15743                 textboxEl.enableDisplayMode();
15744                 textboxEl.addKeyListener([10,13], function(){
15745                     if(dlg.isVisible() && opt && opt.buttons){
15746                         if(opt.buttons.ok){
15747                             handleButton("ok");
15748                         }else if(opt.buttons.yes){
15749                             handleButton("yes");
15750                         }
15751                     }
15752                 });
15753                 textareaEl = Roo.get(bodyEl.dom.childNodes[3]);
15754                 textareaEl.enableDisplayMode();
15755                 progressEl = Roo.get(bodyEl.dom.childNodes[4]);
15756                 progressEl.enableDisplayMode();
15757                 var pf = progressEl.dom.firstChild;
15758                 if (pf) {
15759                     pp = Roo.get(pf.firstChild);
15760                     pp.setHeight(pf.offsetHeight);
15761                 }
15762                 
15763             }
15764             return dlg;
15765         },
15766
15767         /**
15768          * Updates the message box body text
15769          * @param {String} text (optional) Replaces the message box element's innerHTML with the specified string (defaults to
15770          * the XHTML-compliant non-breaking space character '&amp;#160;')
15771          * @return {Roo.MessageBox} This message box
15772          */
15773         updateText : function(text){
15774             if(!dlg.isVisible() && !opt.width){
15775                 dlg.resizeTo(this.maxWidth, 100); // resize first so content is never clipped from previous shows
15776             }
15777             msgEl.innerHTML = text || '&#160;';
15778       
15779             var cw =  Math.max(msgEl.offsetWidth, msgEl.parentNode.scrollWidth);
15780             //Roo.log("guesed size: " + JSON.stringify([cw,msgEl.offsetWidth, msgEl.parentNode.scrollWidth]));
15781             var w = Math.max(
15782                     Math.min(opt.width || cw , this.maxWidth), 
15783                     Math.max(opt.minWidth || this.minWidth, bwidth)
15784             );
15785             if(opt.prompt){
15786                 activeTextEl.setWidth(w);
15787             }
15788             if(dlg.isVisible()){
15789                 dlg.fixedcenter = false;
15790             }
15791             // to big, make it scroll. = But as usual stupid IE does not support
15792             // !important..
15793             
15794             if ( bodyEl.getHeight() > (Roo.lib.Dom.getViewHeight() - 100)) {
15795                 bodyEl.setHeight ( Roo.lib.Dom.getViewHeight() - 100 );
15796                 bodyEl.dom.style.overflowY = 'auto' + ( Roo.isIE ? '' : ' !important');
15797             } else {
15798                 bodyEl.dom.style.height = '';
15799                 bodyEl.dom.style.overflowY = '';
15800             }
15801             if (cw > w) {
15802                 bodyEl.dom.style.get = 'auto' + ( Roo.isIE ? '' : ' !important');
15803             } else {
15804                 bodyEl.dom.style.overflowX = '';
15805             }
15806             
15807             dlg.setContentSize(w, bodyEl.getHeight());
15808             if(dlg.isVisible()){
15809                 dlg.fixedcenter = true;
15810             }
15811             return this;
15812         },
15813
15814         /**
15815          * Updates a progress-style message box's text and progress bar.  Only relevant on message boxes
15816          * initiated via {@link Roo.MessageBox#progress} or by calling {@link Roo.MessageBox#show} with progress: true.
15817          * @param {Number} value Any number between 0 and 1 (e.g., .5)
15818          * @param {String} text (optional) If defined, the message box's body text is replaced with the specified string (defaults to undefined)
15819          * @return {Roo.MessageBox} This message box
15820          */
15821         updateProgress : function(value, text){
15822             if(text){
15823                 this.updateText(text);
15824             }
15825             if (pp) { // weird bug on my firefox - for some reason this is not defined
15826                 pp.setWidth(Math.floor(value*progressEl.dom.firstChild.offsetWidth));
15827             }
15828             return this;
15829         },        
15830
15831         /**
15832          * Returns true if the message box is currently displayed
15833          * @return {Boolean} True if the message box is visible, else false
15834          */
15835         isVisible : function(){
15836             return dlg && dlg.isVisible();  
15837         },
15838
15839         /**
15840          * Hides the message box if it is displayed
15841          */
15842         hide : function(){
15843             if(this.isVisible()){
15844                 dlg.hide();
15845             }  
15846         },
15847
15848         /**
15849          * Displays a new message box, or reinitializes an existing message box, based on the config options
15850          * passed in. All functions (e.g. prompt, alert, etc) on MessageBox call this function internally.
15851          * The following config object properties are supported:
15852          * <pre>
15853 Property    Type             Description
15854 ----------  ---------------  ------------------------------------------------------------------------------------
15855 animEl            String/Element   An id or Element from which the message box should animate as it opens and
15856                                    closes (defaults to undefined)
15857 buttons           Object/Boolean   A button config object (e.g., Roo.MessageBox.OKCANCEL or {ok:'Foo',
15858                                    cancel:'Bar'}), or false to not show any buttons (defaults to false)
15859 closable          Boolean          False to hide the top-right close button (defaults to true).  Note that
15860                                    progress and wait dialogs will ignore this property and always hide the
15861                                    close button as they can only be closed programmatically.
15862 cls               String           A custom CSS class to apply to the message box element
15863 defaultTextHeight Number           The default height in pixels of the message box's multiline textarea if
15864                                    displayed (defaults to 75)
15865 fn                Function         A callback function to execute after closing the dialog.  The arguments to the
15866                                    function will be btn (the name of the button that was clicked, if applicable,
15867                                    e.g. "ok"), and text (the value of the active text field, if applicable).
15868                                    Progress and wait dialogs will ignore this option since they do not respond to
15869                                    user actions and can only be closed programmatically, so any required function
15870                                    should be called by the same code after it closes the dialog.
15871 icon              String           A CSS class that provides a background image to be used as an icon for
15872                                    the dialog (e.g., Roo.MessageBox.WARNING or 'custom-class', defaults to '')
15873 maxWidth          Number           The maximum width in pixels of the message box (defaults to 600)
15874 minWidth          Number           The minimum width in pixels of the message box (defaults to 100)
15875 modal             Boolean          False to allow user interaction with the page while the message box is
15876                                    displayed (defaults to true)
15877 msg               String           A string that will replace the existing message box body text (defaults
15878                                    to the XHTML-compliant non-breaking space character '&#160;')
15879 multiline         Boolean          True to prompt the user to enter multi-line text (defaults to false)
15880 progress          Boolean          True to display a progress bar (defaults to false)
15881 progressText      String           The text to display inside the progress bar if progress = true (defaults to '')
15882 prompt            Boolean          True to prompt the user to enter single-line text (defaults to false)
15883 proxyDrag         Boolean          True to display a lightweight proxy while dragging (defaults to false)
15884 title             String           The title text
15885 value             String           The string value to set into the active textbox element if displayed
15886 wait              Boolean          True to display a progress bar (defaults to false)
15887 width             Number           The width of the dialog in pixels
15888 </pre>
15889          *
15890          * Example usage:
15891          * <pre><code>
15892 Roo.Msg.show({
15893    title: 'Address',
15894    msg: 'Please enter your address:',
15895    width: 300,
15896    buttons: Roo.MessageBox.OKCANCEL,
15897    multiline: true,
15898    fn: saveAddress,
15899    animEl: 'addAddressBtn'
15900 });
15901 </code></pre>
15902          * @param {Object} config Configuration options
15903          * @return {Roo.MessageBox} This message box
15904          */
15905         show : function(options)
15906         {
15907             
15908             // this causes nightmares if you show one dialog after another
15909             // especially on callbacks..
15910              
15911             if(this.isVisible()){
15912                 
15913                 this.hide();
15914                 Roo.log("[Roo.Messagebox] Show called while message displayed:" );
15915                 Roo.log("Old Dialog Message:" +  msgEl.innerHTML );
15916                 Roo.log("New Dialog Message:" +  options.msg )
15917                 //this.alert("ERROR", "Multiple dialogs where displayed at the same time");
15918                 //throw "Roo.MessageBox ERROR : Multiple dialogs where displayed at the same time";
15919                 
15920             }
15921             var d = this.getDialog();
15922             opt = options;
15923             d.setTitle(opt.title || "&#160;");
15924             d.close.setDisplayed(opt.closable !== false);
15925             activeTextEl = textboxEl;
15926             opt.prompt = opt.prompt || (opt.multiline ? true : false);
15927             if(opt.prompt){
15928                 if(opt.multiline){
15929                     textboxEl.hide();
15930                     textareaEl.show();
15931                     textareaEl.setHeight(typeof opt.multiline == "number" ?
15932                         opt.multiline : this.defaultTextHeight);
15933                     activeTextEl = textareaEl;
15934                 }else{
15935                     textboxEl.show();
15936                     textareaEl.hide();
15937                 }
15938             }else{
15939                 textboxEl.hide();
15940                 textareaEl.hide();
15941             }
15942             progressEl.setDisplayed(opt.progress === true);
15943             this.updateProgress(0);
15944             activeTextEl.dom.value = opt.value || "";
15945             if(opt.prompt){
15946                 dlg.setDefaultButton(activeTextEl);
15947             }else{
15948                 var bs = opt.buttons;
15949                 var db = null;
15950                 if(bs && bs.ok){
15951                     db = buttons["ok"];
15952                 }else if(bs && bs.yes){
15953                     db = buttons["yes"];
15954                 }
15955                 dlg.setDefaultButton(db);
15956             }
15957             bwidth = updateButtons(opt.buttons);
15958             this.updateText(opt.msg);
15959             if(opt.cls){
15960                 d.el.addClass(opt.cls);
15961             }
15962             d.proxyDrag = opt.proxyDrag === true;
15963             d.modal = opt.modal !== false;
15964             d.mask = opt.modal !== false ? mask : false;
15965             if(!d.isVisible()){
15966                 // force it to the end of the z-index stack so it gets a cursor in FF
15967                 document.body.appendChild(dlg.el.dom);
15968                 d.animateTarget = null;
15969                 d.show(options.animEl);
15970             }
15971             return this;
15972         },
15973
15974         /**
15975          * Displays a message box with a progress bar.  This message box has no buttons and is not closeable by
15976          * the user.  You are responsible for updating the progress bar as needed via {@link Roo.MessageBox#updateProgress}
15977          * and closing the message box when the process is complete.
15978          * @param {String} title The title bar text
15979          * @param {String} msg The message box body text
15980          * @return {Roo.MessageBox} This message box
15981          */
15982         progress : function(title, msg){
15983             this.show({
15984                 title : title,
15985                 msg : msg,
15986                 buttons: false,
15987                 progress:true,
15988                 closable:false,
15989                 minWidth: this.minProgressWidth,
15990                 modal : true
15991             });
15992             return this;
15993         },
15994
15995         /**
15996          * Displays a standard read-only message box with an OK button (comparable to the basic JavaScript Window.alert).
15997          * If a callback function is passed it will be called after the user clicks the button, and the
15998          * id of the button that was clicked will be passed as the only parameter to the callback
15999          * (could also be the top-right close button).
16000          * @param {String} title The title bar text
16001          * @param {String} msg The message box body text
16002          * @param {Function} fn (optional) The callback function invoked after the message box is closed
16003          * @param {Object} scope (optional) The scope of the callback function
16004          * @return {Roo.MessageBox} This message box
16005          */
16006         alert : function(title, msg, fn, scope){
16007             this.show({
16008                 title : title,
16009                 msg : msg,
16010                 buttons: this.OK,
16011                 fn: fn,
16012                 scope : scope,
16013                 modal : true
16014             });
16015             return this;
16016         },
16017
16018         /**
16019          * Displays a message box with an infinitely auto-updating progress bar.  This can be used to block user
16020          * interaction while waiting for a long-running process to complete that does not have defined intervals.
16021          * You are responsible for closing the message box when the process is complete.
16022          * @param {String} msg The message box body text
16023          * @param {String} title (optional) The title bar text
16024          * @return {Roo.MessageBox} This message box
16025          */
16026         wait : function(msg, title){
16027             this.show({
16028                 title : title,
16029                 msg : msg,
16030                 buttons: false,
16031                 closable:false,
16032                 progress:true,
16033                 modal:true,
16034                 width:300,
16035                 wait:true
16036             });
16037             waitTimer = Roo.TaskMgr.start({
16038                 run: function(i){
16039                     Roo.MessageBox.updateProgress(((((i+20)%20)+1)*5)*.01);
16040                 },
16041                 interval: 1000
16042             });
16043             return this;
16044         },
16045
16046         /**
16047          * Displays a confirmation message box with Yes and No buttons (comparable to JavaScript's Window.confirm).
16048          * If a callback function is passed it will be called after the user clicks either button, and the id of the
16049          * button that was clicked will be passed as the only parameter to the callback (could also be the top-right close button).
16050          * @param {String} title The title bar text
16051          * @param {String} msg The message box body text
16052          * @param {Function} fn (optional) The callback function invoked after the message box is closed
16053          * @param {Object} scope (optional) The scope of the callback function
16054          * @return {Roo.MessageBox} This message box
16055          */
16056         confirm : function(title, msg, fn, scope){
16057             this.show({
16058                 title : title,
16059                 msg : msg,
16060                 buttons: this.YESNO,
16061                 fn: fn,
16062                 scope : scope,
16063                 modal : true
16064             });
16065             return this;
16066         },
16067
16068         /**
16069          * Displays a message box with OK and Cancel buttons prompting the user to enter some text (comparable to
16070          * JavaScript's Window.prompt).  The prompt can be a single-line or multi-line textbox.  If a callback function
16071          * is passed it will be called after the user clicks either button, and the id of the button that was clicked
16072          * (could also be the top-right close button) and the text that was entered will be passed as the two
16073          * parameters to the callback.
16074          * @param {String} title The title bar text
16075          * @param {String} msg The message box body text
16076          * @param {Function} fn (optional) The callback function invoked after the message box is closed
16077          * @param {Object} scope (optional) The scope of the callback function
16078          * @param {Boolean/Number} multiline (optional) True to create a multiline textbox using the defaultTextHeight
16079          * property, or the height in pixels to create the textbox (defaults to false / single-line)
16080          * @return {Roo.MessageBox} This message box
16081          */
16082         prompt : function(title, msg, fn, scope, multiline){
16083             this.show({
16084                 title : title,
16085                 msg : msg,
16086                 buttons: this.OKCANCEL,
16087                 fn: fn,
16088                 minWidth:250,
16089                 scope : scope,
16090                 prompt:true,
16091                 multiline: multiline,
16092                 modal : true
16093             });
16094             return this;
16095         },
16096
16097         /**
16098          * Button config that displays a single OK button
16099          * @type Object
16100          */
16101         OK : {ok:true},
16102         /**
16103          * Button config that displays Yes and No buttons
16104          * @type Object
16105          */
16106         YESNO : {yes:true, no:true},
16107         /**
16108          * Button config that displays OK and Cancel buttons
16109          * @type Object
16110          */
16111         OKCANCEL : {ok:true, cancel:true},
16112         /**
16113          * Button config that displays Yes, No and Cancel buttons
16114          * @type Object
16115          */
16116         YESNOCANCEL : {yes:true, no:true, cancel:true},
16117
16118         /**
16119          * The default height in pixels of the message box's multiline textarea if displayed (defaults to 75)
16120          * @type Number
16121          */
16122         defaultTextHeight : 75,
16123         /**
16124          * The maximum width in pixels of the message box (defaults to 600)
16125          * @type Number
16126          */
16127         maxWidth : 600,
16128         /**
16129          * The minimum width in pixels of the message box (defaults to 100)
16130          * @type Number
16131          */
16132         minWidth : 100,
16133         /**
16134          * The minimum width in pixels of the message box if it is a progress-style dialog.  This is useful
16135          * for setting a different minimum width than text-only dialogs may need (defaults to 250)
16136          * @type Number
16137          */
16138         minProgressWidth : 250,
16139         /**
16140          * An object containing the default button text strings that can be overriden for localized language support.
16141          * Supported properties are: ok, cancel, yes and no.
16142          * Customize the default text like so: Roo.MessageBox.buttonText.yes = "S?";
16143          * @type Object
16144          */
16145         buttonText : {
16146             ok : "OK",
16147             cancel : "Cancel",
16148             yes : "Yes",
16149             no : "No"
16150         }
16151     };
16152 }();
16153
16154 /**
16155  * Shorthand for {@link Roo.MessageBox}
16156  */
16157 Roo.Msg = Roo.MessageBox;/*
16158  * Based on:
16159  * Ext JS Library 1.1.1
16160  * Copyright(c) 2006-2007, Ext JS, LLC.
16161  *
16162  * Originally Released Under LGPL - original licence link has changed is not relivant.
16163  *
16164  * Fork - LGPL
16165  * <script type="text/javascript">
16166  */
16167 /**
16168  * @class Roo.QuickTips
16169  * Provides attractive and customizable tooltips for any element.
16170  * @singleton
16171  */
16172 Roo.QuickTips = function(){
16173     var el, tipBody, tipBodyText, tipTitle, tm, cfg, close, tagEls = {}, esc, removeCls = null, bdLeft, bdRight;
16174     var ce, bd, xy, dd;
16175     var visible = false, disabled = true, inited = false;
16176     var showProc = 1, hideProc = 1, dismissProc = 1, locks = [];
16177     
16178     var onOver = function(e){
16179         if(disabled){
16180             return;
16181         }
16182         var t = e.getTarget();
16183         if(!t || t.nodeType !== 1 || t == document || t == document.body){
16184             return;
16185         }
16186         if(ce && t == ce.el){
16187             clearTimeout(hideProc);
16188             return;
16189         }
16190         if(t && tagEls[t.id]){
16191             tagEls[t.id].el = t;
16192             showProc = show.defer(tm.showDelay, tm, [tagEls[t.id]]);
16193             return;
16194         }
16195         var ttp, et = Roo.fly(t);
16196         var ns = cfg.namespace;
16197         if(tm.interceptTitles && t.title){
16198             ttp = t.title;
16199             t.qtip = ttp;
16200             t.removeAttribute("title");
16201             e.preventDefault();
16202         }else{
16203             ttp = t.qtip || et.getAttributeNS(ns, cfg.attribute);
16204         }
16205         if(ttp){
16206             showProc = show.defer(tm.showDelay, tm, [{
16207                 el: t, 
16208                 text: ttp, 
16209                 width: et.getAttributeNS(ns, cfg.width),
16210                 autoHide: et.getAttributeNS(ns, cfg.hide) != "user",
16211                 title: et.getAttributeNS(ns, cfg.title),
16212                     cls: et.getAttributeNS(ns, cfg.cls)
16213             }]);
16214         }
16215     };
16216     
16217     var onOut = function(e){
16218         clearTimeout(showProc);
16219         var t = e.getTarget();
16220         if(t && ce && ce.el == t && (tm.autoHide && ce.autoHide !== false)){
16221             hideProc = setTimeout(hide, tm.hideDelay);
16222         }
16223     };
16224     
16225     var onMove = function(e){
16226         if(disabled){
16227             return;
16228         }
16229         xy = e.getXY();
16230         xy[1] += 18;
16231         if(tm.trackMouse && ce){
16232             el.setXY(xy);
16233         }
16234     };
16235     
16236     var onDown = function(e){
16237         clearTimeout(showProc);
16238         clearTimeout(hideProc);
16239         if(!e.within(el)){
16240             if(tm.hideOnClick){
16241                 hide();
16242                 tm.disable();
16243                 tm.enable.defer(100, tm);
16244             }
16245         }
16246     };
16247     
16248     var getPad = function(){
16249         return 2;//bdLeft.getPadding('l')+bdRight.getPadding('r');
16250     };
16251
16252     var show = function(o){
16253         if(disabled){
16254             return;
16255         }
16256         clearTimeout(dismissProc);
16257         ce = o;
16258         if(removeCls){ // in case manually hidden
16259             el.removeClass(removeCls);
16260             removeCls = null;
16261         }
16262         if(ce.cls){
16263             el.addClass(ce.cls);
16264             removeCls = ce.cls;
16265         }
16266         if(ce.title){
16267             tipTitle.update(ce.title);
16268             tipTitle.show();
16269         }else{
16270             tipTitle.update('');
16271             tipTitle.hide();
16272         }
16273         el.dom.style.width  = tm.maxWidth+'px';
16274         //tipBody.dom.style.width = '';
16275         tipBodyText.update(o.text);
16276         var p = getPad(), w = ce.width;
16277         if(!w){
16278             var td = tipBodyText.dom;
16279             var aw = Math.max(td.offsetWidth, td.clientWidth, td.scrollWidth);
16280             if(aw > tm.maxWidth){
16281                 w = tm.maxWidth;
16282             }else if(aw < tm.minWidth){
16283                 w = tm.minWidth;
16284             }else{
16285                 w = aw;
16286             }
16287         }
16288         //tipBody.setWidth(w);
16289         el.setWidth(parseInt(w, 10) + p);
16290         if(ce.autoHide === false){
16291             close.setDisplayed(true);
16292             if(dd){
16293                 dd.unlock();
16294             }
16295         }else{
16296             close.setDisplayed(false);
16297             if(dd){
16298                 dd.lock();
16299             }
16300         }
16301         if(xy){
16302             el.avoidY = xy[1]-18;
16303             el.setXY(xy);
16304         }
16305         if(tm.animate){
16306             el.setOpacity(.1);
16307             el.setStyle("visibility", "visible");
16308             el.fadeIn({callback: afterShow});
16309         }else{
16310             afterShow();
16311         }
16312     };
16313     
16314     var afterShow = function(){
16315         if(ce){
16316             el.show();
16317             esc.enable();
16318             if(tm.autoDismiss && ce.autoHide !== false){
16319                 dismissProc = setTimeout(hide, tm.autoDismissDelay);
16320             }
16321         }
16322     };
16323     
16324     var hide = function(noanim){
16325         clearTimeout(dismissProc);
16326         clearTimeout(hideProc);
16327         ce = null;
16328         if(el.isVisible()){
16329             esc.disable();
16330             if(noanim !== true && tm.animate){
16331                 el.fadeOut({callback: afterHide});
16332             }else{
16333                 afterHide();
16334             } 
16335         }
16336     };
16337     
16338     var afterHide = function(){
16339         el.hide();
16340         if(removeCls){
16341             el.removeClass(removeCls);
16342             removeCls = null;
16343         }
16344     };
16345     
16346     return {
16347         /**
16348         * @cfg {Number} minWidth
16349         * The minimum width of the quick tip (defaults to 40)
16350         */
16351        minWidth : 40,
16352         /**
16353         * @cfg {Number} maxWidth
16354         * The maximum width of the quick tip (defaults to 300)
16355         */
16356        maxWidth : 300,
16357         /**
16358         * @cfg {Boolean} interceptTitles
16359         * True to automatically use the element's DOM title value if available (defaults to false)
16360         */
16361        interceptTitles : false,
16362         /**
16363         * @cfg {Boolean} trackMouse
16364         * True to have the quick tip follow the mouse as it moves over the target element (defaults to false)
16365         */
16366        trackMouse : false,
16367         /**
16368         * @cfg {Boolean} hideOnClick
16369         * True to hide the quick tip if the user clicks anywhere in the document (defaults to true)
16370         */
16371        hideOnClick : true,
16372         /**
16373         * @cfg {Number} showDelay
16374         * Delay in milliseconds before the quick tip displays after the mouse enters the target element (defaults to 500)
16375         */
16376        showDelay : 500,
16377         /**
16378         * @cfg {Number} hideDelay
16379         * Delay in milliseconds before the quick tip hides when autoHide = true (defaults to 200)
16380         */
16381        hideDelay : 200,
16382         /**
16383         * @cfg {Boolean} autoHide
16384         * True to automatically hide the quick tip after the mouse exits the target element (defaults to true).
16385         * Used in conjunction with hideDelay.
16386         */
16387        autoHide : true,
16388         /**
16389         * @cfg {Boolean}
16390         * True to automatically hide the quick tip after a set period of time, regardless of the user's actions
16391         * (defaults to true).  Used in conjunction with autoDismissDelay.
16392         */
16393        autoDismiss : true,
16394         /**
16395         * @cfg {Number}
16396         * Delay in milliseconds before the quick tip hides when autoDismiss = true (defaults to 5000)
16397         */
16398        autoDismissDelay : 5000,
16399        /**
16400         * @cfg {Boolean} animate
16401         * True to turn on fade animation. Defaults to false (ClearType/scrollbar flicker issues in IE7).
16402         */
16403        animate : false,
16404
16405        /**
16406         * @cfg {String} title
16407         * Title text to display (defaults to '').  This can be any valid HTML markup.
16408         */
16409         title: '',
16410        /**
16411         * @cfg {String} text
16412         * Body text to display (defaults to '').  This can be any valid HTML markup.
16413         */
16414         text : '',
16415        /**
16416         * @cfg {String} cls
16417         * A CSS class to apply to the base quick tip element (defaults to '').
16418         */
16419         cls : '',
16420        /**
16421         * @cfg {Number} width
16422         * Width in pixels of the quick tip (defaults to auto).  Width will be ignored if it exceeds the bounds of
16423         * minWidth or maxWidth.
16424         */
16425         width : null,
16426
16427     /**
16428      * Initialize and enable QuickTips for first use.  This should be called once before the first attempt to access
16429      * or display QuickTips in a page.
16430      */
16431        init : function(){
16432           tm = Roo.QuickTips;
16433           cfg = tm.tagConfig;
16434           if(!inited){
16435               if(!Roo.isReady){ // allow calling of init() before onReady
16436                   Roo.onReady(Roo.QuickTips.init, Roo.QuickTips);
16437                   return;
16438               }
16439               el = new Roo.Layer({cls:"x-tip", shadow:"drop", shim: true, constrain:true, shadowOffset:4});
16440               el.fxDefaults = {stopFx: true};
16441               // maximum custom styling
16442               //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>');
16443               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>');              
16444               tipTitle = el.child('h3');
16445               tipTitle.enableDisplayMode("block");
16446               tipBody = el.child('div.x-tip-bd');
16447               tipBodyText = el.child('div.x-tip-bd-inner');
16448               //bdLeft = el.child('div.x-tip-bd-left');
16449               //bdRight = el.child('div.x-tip-bd-right');
16450               close = el.child('div.x-tip-close');
16451               close.enableDisplayMode("block");
16452               close.on("click", hide);
16453               var d = Roo.get(document);
16454               d.on("mousedown", onDown);
16455               d.on("mouseover", onOver);
16456               d.on("mouseout", onOut);
16457               d.on("mousemove", onMove);
16458               esc = d.addKeyListener(27, hide);
16459               esc.disable();
16460               if(Roo.dd.DD){
16461                   dd = el.initDD("default", null, {
16462                       onDrag : function(){
16463                           el.sync();  
16464                       }
16465                   });
16466                   dd.setHandleElId(tipTitle.id);
16467                   dd.lock();
16468               }
16469               inited = true;
16470           }
16471           this.enable(); 
16472        },
16473
16474     /**
16475      * Configures a new quick tip instance and assigns it to a target element.  The following config options
16476      * are supported:
16477      * <pre>
16478 Property    Type                   Description
16479 ----------  ---------------------  ------------------------------------------------------------------------
16480 target      Element/String/Array   An Element, id or array of ids that this quick tip should be tied to
16481      * </ul>
16482      * @param {Object} config The config object
16483      */
16484        register : function(config){
16485            var cs = config instanceof Array ? config : arguments;
16486            for(var i = 0, len = cs.length; i < len; i++) {
16487                var c = cs[i];
16488                var target = c.target;
16489                if(target){
16490                    if(target instanceof Array){
16491                        for(var j = 0, jlen = target.length; j < jlen; j++){
16492                            tagEls[target[j]] = c;
16493                        }
16494                    }else{
16495                        tagEls[typeof target == 'string' ? target : Roo.id(target)] = c;
16496                    }
16497                }
16498            }
16499        },
16500
16501     /**
16502      * Removes this quick tip from its element and destroys it.
16503      * @param {String/HTMLElement/Element} el The element from which the quick tip is to be removed.
16504      */
16505        unregister : function(el){
16506            delete tagEls[Roo.id(el)];
16507        },
16508
16509     /**
16510      * Enable this quick tip.
16511      */
16512        enable : function(){
16513            if(inited && disabled){
16514                locks.pop();
16515                if(locks.length < 1){
16516                    disabled = false;
16517                }
16518            }
16519        },
16520
16521     /**
16522      * Disable this quick tip.
16523      */
16524        disable : function(){
16525           disabled = true;
16526           clearTimeout(showProc);
16527           clearTimeout(hideProc);
16528           clearTimeout(dismissProc);
16529           if(ce){
16530               hide(true);
16531           }
16532           locks.push(1);
16533        },
16534
16535     /**
16536      * Returns true if the quick tip is enabled, else false.
16537      */
16538        isEnabled : function(){
16539             return !disabled;
16540        },
16541
16542         // private
16543        tagConfig : {
16544            namespace : "ext",
16545            attribute : "qtip",
16546            width : "width",
16547            target : "target",
16548            title : "qtitle",
16549            hide : "hide",
16550            cls : "qclass"
16551        }
16552    };
16553 }();
16554
16555 // backwards compat
16556 Roo.QuickTips.tips = Roo.QuickTips.register;/*
16557  * Based on:
16558  * Ext JS Library 1.1.1
16559  * Copyright(c) 2006-2007, Ext JS, LLC.
16560  *
16561  * Originally Released Under LGPL - original licence link has changed is not relivant.
16562  *
16563  * Fork - LGPL
16564  * <script type="text/javascript">
16565  */
16566  
16567
16568 /**
16569  * @class Roo.tree.TreePanel
16570  * @extends Roo.data.Tree
16571
16572  * @cfg {Boolean} rootVisible false to hide the root node (defaults to true)
16573  * @cfg {Boolean} lines false to disable tree lines (defaults to true)
16574  * @cfg {Boolean} enableDD true to enable drag and drop
16575  * @cfg {Boolean} enableDrag true to enable just drag
16576  * @cfg {Boolean} enableDrop true to enable just drop
16577  * @cfg {Object} dragConfig Custom config to pass to the {@link Roo.tree.TreeDragZone} instance
16578  * @cfg {Object} dropConfig Custom config to pass to the {@link Roo.tree.TreeDropZone} instance
16579  * @cfg {String} ddGroup The DD group this TreePanel belongs to
16580  * @cfg {String} ddAppendOnly True if the tree should only allow append drops (use for trees which are sorted)
16581  * @cfg {Boolean} ddScroll true to enable YUI body scrolling
16582  * @cfg {Boolean} containerScroll true to register this container with ScrollManager
16583  * @cfg {Boolean} hlDrop false to disable node highlight on drop (defaults to the value of Roo.enableFx)
16584  * @cfg {String} hlColor The color of the node highlight (defaults to C3DAF9)
16585  * @cfg {Boolean} animate true to enable animated expand/collapse (defaults to the value of Roo.enableFx)
16586  * @cfg {Boolean} singleExpand true if only 1 node per branch may be expanded
16587  * @cfg {Boolean} selModel A tree selection model to use with this TreePanel (defaults to a {@link Roo.tree.DefaultSelectionModel})
16588  * @cfg {Boolean} loader A TreeLoader for use with this TreePanel
16589  * @cfg {Object|Roo.tree.TreeEditor} editor The TreeEditor or xtype data to display when clicked.
16590  * @cfg {String} pathSeparator The token used to separate sub-paths in path strings (defaults to '/')
16591  * @cfg {Function} renderer DEPRECATED - use TreeLoader:create event / Sets the rendering (formatting) function for the nodes. to return HTML markup for the tree view. The render function is called with  the following parameters:<ul><li>The {Object} The data for the node.</li></ul>
16592  * @cfg {Function} rendererTip DEPRECATED - use TreeLoader:create event / Sets the rendering (formatting) function for the nodes hovertip to return HTML markup for the tree view. The render function is called with  the following parameters:<ul><li>The {Object} The data for the node.</li></ul>
16593  * 
16594  * @constructor
16595  * @param {String/HTMLElement/Element} el The container element
16596  * @param {Object} config
16597  */
16598 Roo.tree.TreePanel = function(el, config){
16599     var root = false;
16600     var loader = false;
16601     if (config.root) {
16602         root = config.root;
16603         delete config.root;
16604     }
16605     if (config.loader) {
16606         loader = config.loader;
16607         delete config.loader;
16608     }
16609     
16610     Roo.apply(this, config);
16611     Roo.tree.TreePanel.superclass.constructor.call(this);
16612     this.el = Roo.get(el);
16613     this.el.addClass('x-tree');
16614     //console.log(root);
16615     if (root) {
16616         this.setRootNode( Roo.factory(root, Roo.tree));
16617     }
16618     if (loader) {
16619         this.loader = Roo.factory(loader, Roo.tree);
16620     }
16621    /**
16622     * Read-only. The id of the container element becomes this TreePanel's id.
16623     */
16624     this.id = this.el.id;
16625     this.addEvents({
16626         /**
16627         * @event beforeload
16628         * Fires before a node is loaded, return false to cancel
16629         * @param {Node} node The node being loaded
16630         */
16631         "beforeload" : true,
16632         /**
16633         * @event load
16634         * Fires when a node is loaded
16635         * @param {Node} node The node that was loaded
16636         */
16637         "load" : true,
16638         /**
16639         * @event textchange
16640         * Fires when the text for a node is changed
16641         * @param {Node} node The node
16642         * @param {String} text The new text
16643         * @param {String} oldText The old text
16644         */
16645         "textchange" : true,
16646         /**
16647         * @event beforeexpand
16648         * Fires before a node is expanded, return false to cancel.
16649         * @param {Node} node The node
16650         * @param {Boolean} deep
16651         * @param {Boolean} anim
16652         */
16653         "beforeexpand" : true,
16654         /**
16655         * @event beforecollapse
16656         * Fires before a node is collapsed, return false to cancel.
16657         * @param {Node} node The node
16658         * @param {Boolean} deep
16659         * @param {Boolean} anim
16660         */
16661         "beforecollapse" : true,
16662         /**
16663         * @event expand
16664         * Fires when a node is expanded
16665         * @param {Node} node The node
16666         */
16667         "expand" : true,
16668         /**
16669         * @event disabledchange
16670         * Fires when the disabled status of a node changes
16671         * @param {Node} node The node
16672         * @param {Boolean} disabled
16673         */
16674         "disabledchange" : true,
16675         /**
16676         * @event collapse
16677         * Fires when a node is collapsed
16678         * @param {Node} node The node
16679         */
16680         "collapse" : true,
16681         /**
16682         * @event beforeclick
16683         * Fires before click processing on a node. Return false to cancel the default action.
16684         * @param {Node} node The node
16685         * @param {Roo.EventObject} e The event object
16686         */
16687         "beforeclick":true,
16688         /**
16689         * @event checkchange
16690         * Fires when a node with a checkbox's checked property changes
16691         * @param {Node} this This node
16692         * @param {Boolean} checked
16693         */
16694         "checkchange":true,
16695         /**
16696         * @event click
16697         * Fires when a node is clicked
16698         * @param {Node} node The node
16699         * @param {Roo.EventObject} e The event object
16700         */
16701         "click":true,
16702         /**
16703         * @event dblclick
16704         * Fires when a node is double clicked
16705         * @param {Node} node The node
16706         * @param {Roo.EventObject} e The event object
16707         */
16708         "dblclick":true,
16709         /**
16710         * @event contextmenu
16711         * Fires when a node is right clicked
16712         * @param {Node} node The node
16713         * @param {Roo.EventObject} e The event object
16714         */
16715         "contextmenu":true,
16716         /**
16717         * @event beforechildrenrendered
16718         * Fires right before the child nodes for a node are rendered
16719         * @param {Node} node The node
16720         */
16721         "beforechildrenrendered":true,
16722         /**
16723         * @event startdrag
16724         * Fires when a node starts being dragged
16725         * @param {Roo.tree.TreePanel} this
16726         * @param {Roo.tree.TreeNode} node
16727         * @param {event} e The raw browser event
16728         */ 
16729        "startdrag" : true,
16730        /**
16731         * @event enddrag
16732         * Fires when a drag operation is complete
16733         * @param {Roo.tree.TreePanel} this
16734         * @param {Roo.tree.TreeNode} node
16735         * @param {event} e The raw browser event
16736         */
16737        "enddrag" : true,
16738        /**
16739         * @event dragdrop
16740         * Fires when a dragged node is dropped on a valid DD target
16741         * @param {Roo.tree.TreePanel} this
16742         * @param {Roo.tree.TreeNode} node
16743         * @param {DD} dd The dd it was dropped on
16744         * @param {event} e The raw browser event
16745         */
16746        "dragdrop" : true,
16747        /**
16748         * @event beforenodedrop
16749         * Fires when a DD object is dropped on a node in this tree for preprocessing. Return false to cancel the drop. The dropEvent
16750         * passed to handlers has the following properties:<br />
16751         * <ul style="padding:5px;padding-left:16px;">
16752         * <li>tree - The TreePanel</li>
16753         * <li>target - The node being targeted for the drop</li>
16754         * <li>data - The drag data from the drag source</li>
16755         * <li>point - The point of the drop - append, above or below</li>
16756         * <li>source - The drag source</li>
16757         * <li>rawEvent - Raw mouse event</li>
16758         * <li>dropNode - Drop node(s) provided by the source <b>OR</b> you can supply node(s)
16759         * to be inserted by setting them on this object.</li>
16760         * <li>cancel - Set this to true to cancel the drop.</li>
16761         * </ul>
16762         * @param {Object} dropEvent
16763         */
16764        "beforenodedrop" : true,
16765        /**
16766         * @event nodedrop
16767         * Fires after a DD object is dropped on a node in this tree. The dropEvent
16768         * passed to handlers has the following properties:<br />
16769         * <ul style="padding:5px;padding-left:16px;">
16770         * <li>tree - The TreePanel</li>
16771         * <li>target - The node being targeted for the drop</li>
16772         * <li>data - The drag data from the drag source</li>
16773         * <li>point - The point of the drop - append, above or below</li>
16774         * <li>source - The drag source</li>
16775         * <li>rawEvent - Raw mouse event</li>
16776         * <li>dropNode - Dropped node(s).</li>
16777         * </ul>
16778         * @param {Object} dropEvent
16779         */
16780        "nodedrop" : true,
16781         /**
16782         * @event nodedragover
16783         * Fires when a tree node is being targeted for a drag drop, return false to signal drop not allowed. The dragOverEvent
16784         * passed to handlers has the following properties:<br />
16785         * <ul style="padding:5px;padding-left:16px;">
16786         * <li>tree - The TreePanel</li>
16787         * <li>target - The node being targeted for the drop</li>
16788         * <li>data - The drag data from the drag source</li>
16789         * <li>point - The point of the drop - append, above or below</li>
16790         * <li>source - The drag source</li>
16791         * <li>rawEvent - Raw mouse event</li>
16792         * <li>dropNode - Drop node(s) provided by the source.</li>
16793         * <li>cancel - Set this to true to signal drop not allowed.</li>
16794         * </ul>
16795         * @param {Object} dragOverEvent
16796         */
16797        "nodedragover" : true
16798         
16799     });
16800     if(this.singleExpand){
16801        this.on("beforeexpand", this.restrictExpand, this);
16802     }
16803     if (this.editor) {
16804         this.editor.tree = this;
16805         this.editor = Roo.factory(this.editor, Roo.tree);
16806     }
16807     
16808     if (this.selModel) {
16809         this.selModel = Roo.factory(this.selModel, Roo.tree);
16810     }
16811    
16812 };
16813 Roo.extend(Roo.tree.TreePanel, Roo.data.Tree, {
16814     rootVisible : true,
16815     animate: Roo.enableFx,
16816     lines : true,
16817     enableDD : false,
16818     hlDrop : Roo.enableFx,
16819   
16820     renderer: false,
16821     
16822     rendererTip: false,
16823     // private
16824     restrictExpand : function(node){
16825         var p = node.parentNode;
16826         if(p){
16827             if(p.expandedChild && p.expandedChild.parentNode == p){
16828                 p.expandedChild.collapse();
16829             }
16830             p.expandedChild = node;
16831         }
16832     },
16833
16834     // private override
16835     setRootNode : function(node){
16836         Roo.tree.TreePanel.superclass.setRootNode.call(this, node);
16837         if(!this.rootVisible){
16838             node.ui = new Roo.tree.RootTreeNodeUI(node);
16839         }
16840         return node;
16841     },
16842
16843     /**
16844      * Returns the container element for this TreePanel
16845      */
16846     getEl : function(){
16847         return this.el;
16848     },
16849
16850     /**
16851      * Returns the default TreeLoader for this TreePanel
16852      */
16853     getLoader : function(){
16854         return this.loader;
16855     },
16856
16857     /**
16858      * Expand all nodes
16859      */
16860     expandAll : function(){
16861         this.root.expand(true);
16862     },
16863
16864     /**
16865      * Collapse all nodes
16866      */
16867     collapseAll : function(){
16868         this.root.collapse(true);
16869     },
16870
16871     /**
16872      * Returns the selection model used by this TreePanel
16873      */
16874     getSelectionModel : function(){
16875         if(!this.selModel){
16876             this.selModel = new Roo.tree.DefaultSelectionModel();
16877         }
16878         return this.selModel;
16879     },
16880
16881     /**
16882      * Retrieve an array of checked nodes, or an array of a specific attribute of checked nodes (e.g. "id")
16883      * @param {String} attribute (optional) Defaults to null (return the actual nodes)
16884      * @param {TreeNode} startNode (optional) The node to start from, defaults to the root
16885      * @return {Array}
16886      */
16887     getChecked : function(a, startNode){
16888         startNode = startNode || this.root;
16889         var r = [];
16890         var f = function(){
16891             if(this.attributes.checked){
16892                 r.push(!a ? this : (a == 'id' ? this.id : this.attributes[a]));
16893             }
16894         }
16895         startNode.cascade(f);
16896         return r;
16897     },
16898
16899     /**
16900      * Expands a specified path in this TreePanel. A path can be retrieved from a node with {@link Roo.data.Node#getPath}
16901      * @param {String} path
16902      * @param {String} attr (optional) The attribute used in the path (see {@link Roo.data.Node#getPath} for more info)
16903      * @param {Function} callback (optional) The callback to call when the expand is complete. The callback will be called with
16904      * (bSuccess, oLastNode) where bSuccess is if the expand was successful and oLastNode is the last node that was expanded.
16905      */
16906     expandPath : function(path, attr, callback){
16907         attr = attr || "id";
16908         var keys = path.split(this.pathSeparator);
16909         var curNode = this.root;
16910         if(curNode.attributes[attr] != keys[1]){ // invalid root
16911             if(callback){
16912                 callback(false, null);
16913             }
16914             return;
16915         }
16916         var index = 1;
16917         var f = function(){
16918             if(++index == keys.length){
16919                 if(callback){
16920                     callback(true, curNode);
16921                 }
16922                 return;
16923             }
16924             var c = curNode.findChild(attr, keys[index]);
16925             if(!c){
16926                 if(callback){
16927                     callback(false, curNode);
16928                 }
16929                 return;
16930             }
16931             curNode = c;
16932             c.expand(false, false, f);
16933         };
16934         curNode.expand(false, false, f);
16935     },
16936
16937     /**
16938      * Selects the node in this tree at the specified path. A path can be retrieved from a node with {@link Roo.data.Node#getPath}
16939      * @param {String} path
16940      * @param {String} attr (optional) The attribute used in the path (see {@link Roo.data.Node#getPath} for more info)
16941      * @param {Function} callback (optional) The callback to call when the selection is complete. The callback will be called with
16942      * (bSuccess, oSelNode) where bSuccess is if the selection was successful and oSelNode is the selected node.
16943      */
16944     selectPath : function(path, attr, callback){
16945         attr = attr || "id";
16946         var keys = path.split(this.pathSeparator);
16947         var v = keys.pop();
16948         if(keys.length > 0){
16949             var f = function(success, node){
16950                 if(success && node){
16951                     var n = node.findChild(attr, v);
16952                     if(n){
16953                         n.select();
16954                         if(callback){
16955                             callback(true, n);
16956                         }
16957                     }else if(callback){
16958                         callback(false, n);
16959                     }
16960                 }else{
16961                     if(callback){
16962                         callback(false, n);
16963                     }
16964                 }
16965             };
16966             this.expandPath(keys.join(this.pathSeparator), attr, f);
16967         }else{
16968             this.root.select();
16969             if(callback){
16970                 callback(true, this.root);
16971             }
16972         }
16973     },
16974
16975     getTreeEl : function(){
16976         return this.el;
16977     },
16978
16979     /**
16980      * Trigger rendering of this TreePanel
16981      */
16982     render : function(){
16983         if (this.innerCt) {
16984             return this; // stop it rendering more than once!!
16985         }
16986         
16987         this.innerCt = this.el.createChild({tag:"ul",
16988                cls:"x-tree-root-ct " +
16989                (this.lines ? "x-tree-lines" : "x-tree-no-lines")});
16990
16991         if(this.containerScroll){
16992             Roo.dd.ScrollManager.register(this.el);
16993         }
16994         if((this.enableDD || this.enableDrop) && !this.dropZone){
16995            /**
16996             * The dropZone used by this tree if drop is enabled
16997             * @type Roo.tree.TreeDropZone
16998             */
16999              this.dropZone = new Roo.tree.TreeDropZone(this, this.dropConfig || {
17000                ddGroup: this.ddGroup || "TreeDD", appendOnly: this.ddAppendOnly === true
17001            });
17002         }
17003         if((this.enableDD || this.enableDrag) && !this.dragZone){
17004            /**
17005             * The dragZone used by this tree if drag is enabled
17006             * @type Roo.tree.TreeDragZone
17007             */
17008             this.dragZone = new Roo.tree.TreeDragZone(this, this.dragConfig || {
17009                ddGroup: this.ddGroup || "TreeDD",
17010                scroll: this.ddScroll
17011            });
17012         }
17013         this.getSelectionModel().init(this);
17014         if (!this.root) {
17015             Roo.log("ROOT not set in tree");
17016             return this;
17017         }
17018         this.root.render();
17019         if(!this.rootVisible){
17020             this.root.renderChildren();
17021         }
17022         return this;
17023     }
17024 });/*
17025  * Based on:
17026  * Ext JS Library 1.1.1
17027  * Copyright(c) 2006-2007, Ext JS, LLC.
17028  *
17029  * Originally Released Under LGPL - original licence link has changed is not relivant.
17030  *
17031  * Fork - LGPL
17032  * <script type="text/javascript">
17033  */
17034  
17035
17036 /**
17037  * @class Roo.tree.DefaultSelectionModel
17038  * @extends Roo.util.Observable
17039  * The default single selection for a TreePanel.
17040  * @param {Object} cfg Configuration
17041  */
17042 Roo.tree.DefaultSelectionModel = function(cfg){
17043    this.selNode = null;
17044    
17045    
17046    
17047    this.addEvents({
17048        /**
17049         * @event selectionchange
17050         * Fires when the selected node changes
17051         * @param {DefaultSelectionModel} this
17052         * @param {TreeNode} node the new selection
17053         */
17054        "selectionchange" : true,
17055
17056        /**
17057         * @event beforeselect
17058         * Fires before the selected node changes, return false to cancel the change
17059         * @param {DefaultSelectionModel} this
17060         * @param {TreeNode} node the new selection
17061         * @param {TreeNode} node the old selection
17062         */
17063        "beforeselect" : true
17064    });
17065    
17066     Roo.tree.DefaultSelectionModel.superclass.constructor.call(this,cfg);
17067 };
17068
17069 Roo.extend(Roo.tree.DefaultSelectionModel, Roo.util.Observable, {
17070     init : function(tree){
17071         this.tree = tree;
17072         tree.getTreeEl().on("keydown", this.onKeyDown, this);
17073         tree.on("click", this.onNodeClick, this);
17074     },
17075     
17076     onNodeClick : function(node, e){
17077         if (e.ctrlKey && this.selNode == node)  {
17078             this.unselect(node);
17079             return;
17080         }
17081         this.select(node);
17082     },
17083     
17084     /**
17085      * Select a node.
17086      * @param {TreeNode} node The node to select
17087      * @return {TreeNode} The selected node
17088      */
17089     select : function(node){
17090         var last = this.selNode;
17091         if(last != node && this.fireEvent('beforeselect', this, node, last) !== false){
17092             if(last){
17093                 last.ui.onSelectedChange(false);
17094             }
17095             this.selNode = node;
17096             node.ui.onSelectedChange(true);
17097             this.fireEvent("selectionchange", this, node, last);
17098         }
17099         return node;
17100     },
17101     
17102     /**
17103      * Deselect a node.
17104      * @param {TreeNode} node The node to unselect
17105      */
17106     unselect : function(node){
17107         if(this.selNode == node){
17108             this.clearSelections();
17109         }    
17110     },
17111     
17112     /**
17113      * Clear all selections
17114      */
17115     clearSelections : function(){
17116         var n = this.selNode;
17117         if(n){
17118             n.ui.onSelectedChange(false);
17119             this.selNode = null;
17120             this.fireEvent("selectionchange", this, null);
17121         }
17122         return n;
17123     },
17124     
17125     /**
17126      * Get the selected node
17127      * @return {TreeNode} The selected node
17128      */
17129     getSelectedNode : function(){
17130         return this.selNode;    
17131     },
17132     
17133     /**
17134      * Returns true if the node is selected
17135      * @param {TreeNode} node The node to check
17136      * @return {Boolean}
17137      */
17138     isSelected : function(node){
17139         return this.selNode == node;  
17140     },
17141
17142     /**
17143      * Selects the node above the selected node in the tree, intelligently walking the nodes
17144      * @return TreeNode The new selection
17145      */
17146     selectPrevious : function(){
17147         var s = this.selNode || this.lastSelNode;
17148         if(!s){
17149             return null;
17150         }
17151         var ps = s.previousSibling;
17152         if(ps){
17153             if(!ps.isExpanded() || ps.childNodes.length < 1){
17154                 return this.select(ps);
17155             } else{
17156                 var lc = ps.lastChild;
17157                 while(lc && lc.isExpanded() && lc.childNodes.length > 0){
17158                     lc = lc.lastChild;
17159                 }
17160                 return this.select(lc);
17161             }
17162         } else if(s.parentNode && (this.tree.rootVisible || !s.parentNode.isRoot)){
17163             return this.select(s.parentNode);
17164         }
17165         return null;
17166     },
17167
17168     /**
17169      * Selects the node above the selected node in the tree, intelligently walking the nodes
17170      * @return TreeNode The new selection
17171      */
17172     selectNext : function(){
17173         var s = this.selNode || this.lastSelNode;
17174         if(!s){
17175             return null;
17176         }
17177         if(s.firstChild && s.isExpanded()){
17178              return this.select(s.firstChild);
17179          }else if(s.nextSibling){
17180              return this.select(s.nextSibling);
17181          }else if(s.parentNode){
17182             var newS = null;
17183             s.parentNode.bubble(function(){
17184                 if(this.nextSibling){
17185                     newS = this.getOwnerTree().selModel.select(this.nextSibling);
17186                     return false;
17187                 }
17188             });
17189             return newS;
17190          }
17191         return null;
17192     },
17193
17194     onKeyDown : function(e){
17195         var s = this.selNode || this.lastSelNode;
17196         // undesirable, but required
17197         var sm = this;
17198         if(!s){
17199             return;
17200         }
17201         var k = e.getKey();
17202         switch(k){
17203              case e.DOWN:
17204                  e.stopEvent();
17205                  this.selectNext();
17206              break;
17207              case e.UP:
17208                  e.stopEvent();
17209                  this.selectPrevious();
17210              break;
17211              case e.RIGHT:
17212                  e.preventDefault();
17213                  if(s.hasChildNodes()){
17214                      if(!s.isExpanded()){
17215                          s.expand();
17216                      }else if(s.firstChild){
17217                          this.select(s.firstChild, e);
17218                      }
17219                  }
17220              break;
17221              case e.LEFT:
17222                  e.preventDefault();
17223                  if(s.hasChildNodes() && s.isExpanded()){
17224                      s.collapse();
17225                  }else if(s.parentNode && (this.tree.rootVisible || s.parentNode != this.tree.getRootNode())){
17226                      this.select(s.parentNode, e);
17227                  }
17228              break;
17229         };
17230     }
17231 });
17232
17233 /**
17234  * @class Roo.tree.MultiSelectionModel
17235  * @extends Roo.util.Observable
17236  * Multi selection for a TreePanel.
17237  * @param {Object} cfg Configuration
17238  */
17239 Roo.tree.MultiSelectionModel = function(){
17240    this.selNodes = [];
17241    this.selMap = {};
17242    this.addEvents({
17243        /**
17244         * @event selectionchange
17245         * Fires when the selected nodes change
17246         * @param {MultiSelectionModel} this
17247         * @param {Array} nodes Array of the selected nodes
17248         */
17249        "selectionchange" : true
17250    });
17251    Roo.tree.MultiSelectionModel.superclass.constructor.call(this,cfg);
17252    
17253 };
17254
17255 Roo.extend(Roo.tree.MultiSelectionModel, Roo.util.Observable, {
17256     init : function(tree){
17257         this.tree = tree;
17258         tree.getTreeEl().on("keydown", this.onKeyDown, this);
17259         tree.on("click", this.onNodeClick, this);
17260     },
17261     
17262     onNodeClick : function(node, e){
17263         this.select(node, e, e.ctrlKey);
17264     },
17265     
17266     /**
17267      * Select a node.
17268      * @param {TreeNode} node The node to select
17269      * @param {EventObject} e (optional) An event associated with the selection
17270      * @param {Boolean} keepExisting True to retain existing selections
17271      * @return {TreeNode} The selected node
17272      */
17273     select : function(node, e, keepExisting){
17274         if(keepExisting !== true){
17275             this.clearSelections(true);
17276         }
17277         if(this.isSelected(node)){
17278             this.lastSelNode = node;
17279             return node;
17280         }
17281         this.selNodes.push(node);
17282         this.selMap[node.id] = node;
17283         this.lastSelNode = node;
17284         node.ui.onSelectedChange(true);
17285         this.fireEvent("selectionchange", this, this.selNodes);
17286         return node;
17287     },
17288     
17289     /**
17290      * Deselect a node.
17291      * @param {TreeNode} node The node to unselect
17292      */
17293     unselect : function(node){
17294         if(this.selMap[node.id]){
17295             node.ui.onSelectedChange(false);
17296             var sn = this.selNodes;
17297             var index = -1;
17298             if(sn.indexOf){
17299                 index = sn.indexOf(node);
17300             }else{
17301                 for(var i = 0, len = sn.length; i < len; i++){
17302                     if(sn[i] == node){
17303                         index = i;
17304                         break;
17305                     }
17306                 }
17307             }
17308             if(index != -1){
17309                 this.selNodes.splice(index, 1);
17310             }
17311             delete this.selMap[node.id];
17312             this.fireEvent("selectionchange", this, this.selNodes);
17313         }
17314     },
17315     
17316     /**
17317      * Clear all selections
17318      */
17319     clearSelections : function(suppressEvent){
17320         var sn = this.selNodes;
17321         if(sn.length > 0){
17322             for(var i = 0, len = sn.length; i < len; i++){
17323                 sn[i].ui.onSelectedChange(false);
17324             }
17325             this.selNodes = [];
17326             this.selMap = {};
17327             if(suppressEvent !== true){
17328                 this.fireEvent("selectionchange", this, this.selNodes);
17329             }
17330         }
17331     },
17332     
17333     /**
17334      * Returns true if the node is selected
17335      * @param {TreeNode} node The node to check
17336      * @return {Boolean}
17337      */
17338     isSelected : function(node){
17339         return this.selMap[node.id] ? true : false;  
17340     },
17341     
17342     /**
17343      * Returns an array of the selected nodes
17344      * @return {Array}
17345      */
17346     getSelectedNodes : function(){
17347         return this.selNodes;    
17348     },
17349
17350     onKeyDown : Roo.tree.DefaultSelectionModel.prototype.onKeyDown,
17351
17352     selectNext : Roo.tree.DefaultSelectionModel.prototype.selectNext,
17353
17354     selectPrevious : Roo.tree.DefaultSelectionModel.prototype.selectPrevious
17355 });/*
17356  * Based on:
17357  * Ext JS Library 1.1.1
17358  * Copyright(c) 2006-2007, Ext JS, LLC.
17359  *
17360  * Originally Released Under LGPL - original licence link has changed is not relivant.
17361  *
17362  * Fork - LGPL
17363  * <script type="text/javascript">
17364  */
17365  
17366 /**
17367  * @class Roo.tree.TreeNode
17368  * @extends Roo.data.Node
17369  * @cfg {String} text The text for this node
17370  * @cfg {Boolean} expanded true to start the node expanded
17371  * @cfg {Boolean} allowDrag false to make this node undraggable if DD is on (defaults to true)
17372  * @cfg {Boolean} allowDrop false if this node cannot be drop on
17373  * @cfg {Boolean} disabled true to start the node disabled
17374  * @cfg {String} icon The path to an icon for the node. The preferred way to do this
17375  * is to use the cls or iconCls attributes and add the icon via a CSS background image.
17376  * @cfg {String} cls A css class to be added to the node
17377  * @cfg {String} iconCls A css class to be added to the nodes icon element for applying css background images
17378  * @cfg {String} href URL of the link used for the node (defaults to #)
17379  * @cfg {String} hrefTarget target frame for the link
17380  * @cfg {String} qtip An Ext QuickTip for the node
17381  * @cfg {String} qtipCfg An Ext QuickTip config for the node (used instead of qtip)
17382  * @cfg {Boolean} singleClickExpand True for single click expand on this node
17383  * @cfg {Function} uiProvider A UI <b>class</b> to use for this node (defaults to Roo.tree.TreeNodeUI)
17384  * @cfg {Boolean} checked True to render a checked checkbox for this node, false to render an unchecked checkbox
17385  * (defaults to undefined with no checkbox rendered)
17386  * @constructor
17387  * @param {Object/String} attributes The attributes/config for the node or just a string with the text for the node
17388  */
17389 Roo.tree.TreeNode = function(attributes){
17390     attributes = attributes || {};
17391     if(typeof attributes == "string"){
17392         attributes = {text: attributes};
17393     }
17394     this.childrenRendered = false;
17395     this.rendered = false;
17396     Roo.tree.TreeNode.superclass.constructor.call(this, attributes);
17397     this.expanded = attributes.expanded === true;
17398     this.isTarget = attributes.isTarget !== false;
17399     this.draggable = attributes.draggable !== false && attributes.allowDrag !== false;
17400     this.allowChildren = attributes.allowChildren !== false && attributes.allowDrop !== false;
17401
17402     /**
17403      * Read-only. The text for this node. To change it use setText().
17404      * @type String
17405      */
17406     this.text = attributes.text;
17407     /**
17408      * True if this node is disabled.
17409      * @type Boolean
17410      */
17411     this.disabled = attributes.disabled === true;
17412
17413     this.addEvents({
17414         /**
17415         * @event textchange
17416         * Fires when the text for this node is changed
17417         * @param {Node} this This node
17418         * @param {String} text The new text
17419         * @param {String} oldText The old text
17420         */
17421         "textchange" : true,
17422         /**
17423         * @event beforeexpand
17424         * Fires before this node is expanded, return false to cancel.
17425         * @param {Node} this This node
17426         * @param {Boolean} deep
17427         * @param {Boolean} anim
17428         */
17429         "beforeexpand" : true,
17430         /**
17431         * @event beforecollapse
17432         * Fires before this node is collapsed, return false to cancel.
17433         * @param {Node} this This node
17434         * @param {Boolean} deep
17435         * @param {Boolean} anim
17436         */
17437         "beforecollapse" : true,
17438         /**
17439         * @event expand
17440         * Fires when this node is expanded
17441         * @param {Node} this This node
17442         */
17443         "expand" : true,
17444         /**
17445         * @event disabledchange
17446         * Fires when the disabled status of this node changes
17447         * @param {Node} this This node
17448         * @param {Boolean} disabled
17449         */
17450         "disabledchange" : true,
17451         /**
17452         * @event collapse
17453         * Fires when this node is collapsed
17454         * @param {Node} this This node
17455         */
17456         "collapse" : true,
17457         /**
17458         * @event beforeclick
17459         * Fires before click processing. Return false to cancel the default action.
17460         * @param {Node} this This node
17461         * @param {Roo.EventObject} e The event object
17462         */
17463         "beforeclick":true,
17464         /**
17465         * @event checkchange
17466         * Fires when a node with a checkbox's checked property changes
17467         * @param {Node} this This node
17468         * @param {Boolean} checked
17469         */
17470         "checkchange":true,
17471         /**
17472         * @event click
17473         * Fires when this node is clicked
17474         * @param {Node} this This node
17475         * @param {Roo.EventObject} e The event object
17476         */
17477         "click":true,
17478         /**
17479         * @event dblclick
17480         * Fires when this node is double clicked
17481         * @param {Node} this This node
17482         * @param {Roo.EventObject} e The event object
17483         */
17484         "dblclick":true,
17485         /**
17486         * @event contextmenu
17487         * Fires when this node is right clicked
17488         * @param {Node} this This node
17489         * @param {Roo.EventObject} e The event object
17490         */
17491         "contextmenu":true,
17492         /**
17493         * @event beforechildrenrendered
17494         * Fires right before the child nodes for this node are rendered
17495         * @param {Node} this This node
17496         */
17497         "beforechildrenrendered":true
17498     });
17499
17500     var uiClass = this.attributes.uiProvider || Roo.tree.TreeNodeUI;
17501
17502     /**
17503      * Read-only. The UI for this node
17504      * @type TreeNodeUI
17505      */
17506     this.ui = new uiClass(this);
17507     
17508     // finally support items[]
17509     if (typeof(this.attributes.items) == 'undefined' || !this.attributes.items) {
17510         return;
17511     }
17512     
17513     
17514     Roo.each(this.attributes.items, function(c) {
17515         this.appendChild(Roo.factory(c,Roo.Tree));
17516     }, this);
17517     delete this.attributes.items;
17518     
17519     
17520     
17521 };
17522 Roo.extend(Roo.tree.TreeNode, Roo.data.Node, {
17523     preventHScroll: true,
17524     /**
17525      * Returns true if this node is expanded
17526      * @return {Boolean}
17527      */
17528     isExpanded : function(){
17529         return this.expanded;
17530     },
17531
17532     /**
17533      * Returns the UI object for this node
17534      * @return {TreeNodeUI}
17535      */
17536     getUI : function(){
17537         return this.ui;
17538     },
17539
17540     // private override
17541     setFirstChild : function(node){
17542         var of = this.firstChild;
17543         Roo.tree.TreeNode.superclass.setFirstChild.call(this, node);
17544         if(this.childrenRendered && of && node != of){
17545             of.renderIndent(true, true);
17546         }
17547         if(this.rendered){
17548             this.renderIndent(true, true);
17549         }
17550     },
17551
17552     // private override
17553     setLastChild : function(node){
17554         var ol = this.lastChild;
17555         Roo.tree.TreeNode.superclass.setLastChild.call(this, node);
17556         if(this.childrenRendered && ol && node != ol){
17557             ol.renderIndent(true, true);
17558         }
17559         if(this.rendered){
17560             this.renderIndent(true, true);
17561         }
17562     },
17563
17564     // these methods are overridden to provide lazy rendering support
17565     // private override
17566     appendChild : function()
17567     {
17568         var node = Roo.tree.TreeNode.superclass.appendChild.apply(this, arguments);
17569         if(node && this.childrenRendered){
17570             node.render();
17571         }
17572         this.ui.updateExpandIcon();
17573         return node;
17574     },
17575
17576     // private override
17577     removeChild : function(node){
17578         this.ownerTree.getSelectionModel().unselect(node);
17579         Roo.tree.TreeNode.superclass.removeChild.apply(this, arguments);
17580         // if it's been rendered remove dom node
17581         if(this.childrenRendered){
17582             node.ui.remove();
17583         }
17584         if(this.childNodes.length < 1){
17585             this.collapse(false, false);
17586         }else{
17587             this.ui.updateExpandIcon();
17588         }
17589         if(!this.firstChild) {
17590             this.childrenRendered = false;
17591         }
17592         return node;
17593     },
17594
17595     // private override
17596     insertBefore : function(node, refNode){
17597         var newNode = Roo.tree.TreeNode.superclass.insertBefore.apply(this, arguments);
17598         if(newNode && refNode && this.childrenRendered){
17599             node.render();
17600         }
17601         this.ui.updateExpandIcon();
17602         return newNode;
17603     },
17604
17605     /**
17606      * Sets the text for this node
17607      * @param {String} text
17608      */
17609     setText : function(text){
17610         var oldText = this.text;
17611         this.text = text;
17612         this.attributes.text = text;
17613         if(this.rendered){ // event without subscribing
17614             this.ui.onTextChange(this, text, oldText);
17615         }
17616         this.fireEvent("textchange", this, text, oldText);
17617     },
17618
17619     /**
17620      * Triggers selection of this node
17621      */
17622     select : function(){
17623         this.getOwnerTree().getSelectionModel().select(this);
17624     },
17625
17626     /**
17627      * Triggers deselection of this node
17628      */
17629     unselect : function(){
17630         this.getOwnerTree().getSelectionModel().unselect(this);
17631     },
17632
17633     /**
17634      * Returns true if this node is selected
17635      * @return {Boolean}
17636      */
17637     isSelected : function(){
17638         return this.getOwnerTree().getSelectionModel().isSelected(this);
17639     },
17640
17641     /**
17642      * Expand this node.
17643      * @param {Boolean} deep (optional) True to expand all children as well
17644      * @param {Boolean} anim (optional) false to cancel the default animation
17645      * @param {Function} callback (optional) A callback to be called when
17646      * expanding this node completes (does not wait for deep expand to complete).
17647      * Called with 1 parameter, this node.
17648      */
17649     expand : function(deep, anim, callback){
17650         if(!this.expanded){
17651             if(this.fireEvent("beforeexpand", this, deep, anim) === false){
17652                 return;
17653             }
17654             if(!this.childrenRendered){
17655                 this.renderChildren();
17656             }
17657             this.expanded = true;
17658             if(!this.isHiddenRoot() && (this.getOwnerTree().animate && anim !== false) || anim){
17659                 this.ui.animExpand(function(){
17660                     this.fireEvent("expand", this);
17661                     if(typeof callback == "function"){
17662                         callback(this);
17663                     }
17664                     if(deep === true){
17665                         this.expandChildNodes(true);
17666                     }
17667                 }.createDelegate(this));
17668                 return;
17669             }else{
17670                 this.ui.expand();
17671                 this.fireEvent("expand", this);
17672                 if(typeof callback == "function"){
17673                     callback(this);
17674                 }
17675             }
17676         }else{
17677            if(typeof callback == "function"){
17678                callback(this);
17679            }
17680         }
17681         if(deep === true){
17682             this.expandChildNodes(true);
17683         }
17684     },
17685
17686     isHiddenRoot : function(){
17687         return this.isRoot && !this.getOwnerTree().rootVisible;
17688     },
17689
17690     /**
17691      * Collapse this node.
17692      * @param {Boolean} deep (optional) True to collapse all children as well
17693      * @param {Boolean} anim (optional) false to cancel the default animation
17694      */
17695     collapse : function(deep, anim){
17696         if(this.expanded && !this.isHiddenRoot()){
17697             if(this.fireEvent("beforecollapse", this, deep, anim) === false){
17698                 return;
17699             }
17700             this.expanded = false;
17701             if((this.getOwnerTree().animate && anim !== false) || anim){
17702                 this.ui.animCollapse(function(){
17703                     this.fireEvent("collapse", this);
17704                     if(deep === true){
17705                         this.collapseChildNodes(true);
17706                     }
17707                 }.createDelegate(this));
17708                 return;
17709             }else{
17710                 this.ui.collapse();
17711                 this.fireEvent("collapse", this);
17712             }
17713         }
17714         if(deep === true){
17715             var cs = this.childNodes;
17716             for(var i = 0, len = cs.length; i < len; i++) {
17717                 cs[i].collapse(true, false);
17718             }
17719         }
17720     },
17721
17722     // private
17723     delayedExpand : function(delay){
17724         if(!this.expandProcId){
17725             this.expandProcId = this.expand.defer(delay, this);
17726         }
17727     },
17728
17729     // private
17730     cancelExpand : function(){
17731         if(this.expandProcId){
17732             clearTimeout(this.expandProcId);
17733         }
17734         this.expandProcId = false;
17735     },
17736
17737     /**
17738      * Toggles expanded/collapsed state of the node
17739      */
17740     toggle : function(){
17741         if(this.expanded){
17742             this.collapse();
17743         }else{
17744             this.expand();
17745         }
17746     },
17747
17748     /**
17749      * Ensures all parent nodes are expanded
17750      */
17751     ensureVisible : function(callback){
17752         var tree = this.getOwnerTree();
17753         tree.expandPath(this.parentNode.getPath(), false, function(){
17754             tree.getTreeEl().scrollChildIntoView(this.ui.anchor);
17755             Roo.callback(callback);
17756         }.createDelegate(this));
17757     },
17758
17759     /**
17760      * Expand all child nodes
17761      * @param {Boolean} deep (optional) true if the child nodes should also expand their child nodes
17762      */
17763     expandChildNodes : function(deep){
17764         var cs = this.childNodes;
17765         for(var i = 0, len = cs.length; i < len; i++) {
17766                 cs[i].expand(deep);
17767         }
17768     },
17769
17770     /**
17771      * Collapse all child nodes
17772      * @param {Boolean} deep (optional) true if the child nodes should also collapse their child nodes
17773      */
17774     collapseChildNodes : function(deep){
17775         var cs = this.childNodes;
17776         for(var i = 0, len = cs.length; i < len; i++) {
17777                 cs[i].collapse(deep);
17778         }
17779     },
17780
17781     /**
17782      * Disables this node
17783      */
17784     disable : function(){
17785         this.disabled = true;
17786         this.unselect();
17787         if(this.rendered && this.ui.onDisableChange){ // event without subscribing
17788             this.ui.onDisableChange(this, true);
17789         }
17790         this.fireEvent("disabledchange", this, true);
17791     },
17792
17793     /**
17794      * Enables this node
17795      */
17796     enable : function(){
17797         this.disabled = false;
17798         if(this.rendered && this.ui.onDisableChange){ // event without subscribing
17799             this.ui.onDisableChange(this, false);
17800         }
17801         this.fireEvent("disabledchange", this, false);
17802     },
17803
17804     // private
17805     renderChildren : function(suppressEvent){
17806         if(suppressEvent !== false){
17807             this.fireEvent("beforechildrenrendered", this);
17808         }
17809         var cs = this.childNodes;
17810         for(var i = 0, len = cs.length; i < len; i++){
17811             cs[i].render(true);
17812         }
17813         this.childrenRendered = true;
17814     },
17815
17816     // private
17817     sort : function(fn, scope){
17818         Roo.tree.TreeNode.superclass.sort.apply(this, arguments);
17819         if(this.childrenRendered){
17820             var cs = this.childNodes;
17821             for(var i = 0, len = cs.length; i < len; i++){
17822                 cs[i].render(true);
17823             }
17824         }
17825     },
17826
17827     // private
17828     render : function(bulkRender){
17829         this.ui.render(bulkRender);
17830         if(!this.rendered){
17831             this.rendered = true;
17832             if(this.expanded){
17833                 this.expanded = false;
17834                 this.expand(false, false);
17835             }
17836         }
17837     },
17838
17839     // private
17840     renderIndent : function(deep, refresh){
17841         if(refresh){
17842             this.ui.childIndent = null;
17843         }
17844         this.ui.renderIndent();
17845         if(deep === true && this.childrenRendered){
17846             var cs = this.childNodes;
17847             for(var i = 0, len = cs.length; i < len; i++){
17848                 cs[i].renderIndent(true, refresh);
17849             }
17850         }
17851     }
17852 });/*
17853  * Based on:
17854  * Ext JS Library 1.1.1
17855  * Copyright(c) 2006-2007, Ext JS, LLC.
17856  *
17857  * Originally Released Under LGPL - original licence link has changed is not relivant.
17858  *
17859  * Fork - LGPL
17860  * <script type="text/javascript">
17861  */
17862  
17863 /**
17864  * @class Roo.tree.AsyncTreeNode
17865  * @extends Roo.tree.TreeNode
17866  * @cfg {TreeLoader} loader A TreeLoader to be used by this node (defaults to the loader defined on the tree)
17867  * @constructor
17868  * @param {Object/String} attributes The attributes/config for the node or just a string with the text for the node 
17869  */
17870  Roo.tree.AsyncTreeNode = function(config){
17871     this.loaded = false;
17872     this.loading = false;
17873     Roo.tree.AsyncTreeNode.superclass.constructor.apply(this, arguments);
17874     /**
17875     * @event beforeload
17876     * Fires before this node is loaded, return false to cancel
17877     * @param {Node} this This node
17878     */
17879     this.addEvents({'beforeload':true, 'load': true});
17880     /**
17881     * @event load
17882     * Fires when this node is loaded
17883     * @param {Node} this This node
17884     */
17885     /**
17886      * The loader used by this node (defaults to using the tree's defined loader)
17887      * @type TreeLoader
17888      * @property loader
17889      */
17890 };
17891 Roo.extend(Roo.tree.AsyncTreeNode, Roo.tree.TreeNode, {
17892     expand : function(deep, anim, callback){
17893         if(this.loading){ // if an async load is already running, waiting til it's done
17894             var timer;
17895             var f = function(){
17896                 if(!this.loading){ // done loading
17897                     clearInterval(timer);
17898                     this.expand(deep, anim, callback);
17899                 }
17900             }.createDelegate(this);
17901             timer = setInterval(f, 200);
17902             return;
17903         }
17904         if(!this.loaded){
17905             if(this.fireEvent("beforeload", this) === false){
17906                 return;
17907             }
17908             this.loading = true;
17909             this.ui.beforeLoad(this);
17910             var loader = this.loader || this.attributes.loader || this.getOwnerTree().getLoader();
17911             if(loader){
17912                 loader.load(this, this.loadComplete.createDelegate(this, [deep, anim, callback]));
17913                 return;
17914             }
17915         }
17916         Roo.tree.AsyncTreeNode.superclass.expand.call(this, deep, anim, callback);
17917     },
17918     
17919     /**
17920      * Returns true if this node is currently loading
17921      * @return {Boolean}
17922      */
17923     isLoading : function(){
17924         return this.loading;  
17925     },
17926     
17927     loadComplete : function(deep, anim, callback){
17928         this.loading = false;
17929         this.loaded = true;
17930         this.ui.afterLoad(this);
17931         this.fireEvent("load", this);
17932         this.expand(deep, anim, callback);
17933     },
17934     
17935     /**
17936      * Returns true if this node has been loaded
17937      * @return {Boolean}
17938      */
17939     isLoaded : function(){
17940         return this.loaded;
17941     },
17942     
17943     hasChildNodes : function(){
17944         if(!this.isLeaf() && !this.loaded){
17945             return true;
17946         }else{
17947             return Roo.tree.AsyncTreeNode.superclass.hasChildNodes.call(this);
17948         }
17949     },
17950
17951     /**
17952      * Trigger a reload for this node
17953      * @param {Function} callback
17954      */
17955     reload : function(callback){
17956         this.collapse(false, false);
17957         while(this.firstChild){
17958             this.removeChild(this.firstChild);
17959         }
17960         this.childrenRendered = false;
17961         this.loaded = false;
17962         if(this.isHiddenRoot()){
17963             this.expanded = false;
17964         }
17965         this.expand(false, false, callback);
17966     }
17967 });/*
17968  * Based on:
17969  * Ext JS Library 1.1.1
17970  * Copyright(c) 2006-2007, Ext JS, LLC.
17971  *
17972  * Originally Released Under LGPL - original licence link has changed is not relivant.
17973  *
17974  * Fork - LGPL
17975  * <script type="text/javascript">
17976  */
17977  
17978 /**
17979  * @class Roo.tree.TreeNodeUI
17980  * @constructor
17981  * @param {Object} node The node to render
17982  * The TreeNode UI implementation is separate from the
17983  * tree implementation. Unless you are customizing the tree UI,
17984  * you should never have to use this directly.
17985  */
17986 Roo.tree.TreeNodeUI = function(node){
17987     this.node = node;
17988     this.rendered = false;
17989     this.animating = false;
17990     this.emptyIcon = Roo.BLANK_IMAGE_URL;
17991 };
17992
17993 Roo.tree.TreeNodeUI.prototype = {
17994     removeChild : function(node){
17995         if(this.rendered){
17996             this.ctNode.removeChild(node.ui.getEl());
17997         }
17998     },
17999
18000     beforeLoad : function(){
18001          this.addClass("x-tree-node-loading");
18002     },
18003
18004     afterLoad : function(){
18005          this.removeClass("x-tree-node-loading");
18006     },
18007
18008     onTextChange : function(node, text, oldText){
18009         if(this.rendered){
18010             this.textNode.innerHTML = text;
18011         }
18012     },
18013
18014     onDisableChange : function(node, state){
18015         this.disabled = state;
18016         if(state){
18017             this.addClass("x-tree-node-disabled");
18018         }else{
18019             this.removeClass("x-tree-node-disabled");
18020         }
18021     },
18022
18023     onSelectedChange : function(state){
18024         if(state){
18025             this.focus();
18026             this.addClass("x-tree-selected");
18027         }else{
18028             //this.blur();
18029             this.removeClass("x-tree-selected");
18030         }
18031     },
18032
18033     onMove : function(tree, node, oldParent, newParent, index, refNode){
18034         this.childIndent = null;
18035         if(this.rendered){
18036             var targetNode = newParent.ui.getContainer();
18037             if(!targetNode){//target not rendered
18038                 this.holder = document.createElement("div");
18039                 this.holder.appendChild(this.wrap);
18040                 return;
18041             }
18042             var insertBefore = refNode ? refNode.ui.getEl() : null;
18043             if(insertBefore){
18044                 targetNode.insertBefore(this.wrap, insertBefore);
18045             }else{
18046                 targetNode.appendChild(this.wrap);
18047             }
18048             this.node.renderIndent(true);
18049         }
18050     },
18051
18052     addClass : function(cls){
18053         if(this.elNode){
18054             Roo.fly(this.elNode).addClass(cls);
18055         }
18056     },
18057
18058     removeClass : function(cls){
18059         if(this.elNode){
18060             Roo.fly(this.elNode).removeClass(cls);
18061         }
18062     },
18063
18064     remove : function(){
18065         if(this.rendered){
18066             this.holder = document.createElement("div");
18067             this.holder.appendChild(this.wrap);
18068         }
18069     },
18070
18071     fireEvent : function(){
18072         return this.node.fireEvent.apply(this.node, arguments);
18073     },
18074
18075     initEvents : function(){
18076         this.node.on("move", this.onMove, this);
18077         var E = Roo.EventManager;
18078         var a = this.anchor;
18079
18080         var el = Roo.fly(a, '_treeui');
18081
18082         if(Roo.isOpera){ // opera render bug ignores the CSS
18083             el.setStyle("text-decoration", "none");
18084         }
18085
18086         el.on("click", this.onClick, this);
18087         el.on("dblclick", this.onDblClick, this);
18088
18089         if(this.checkbox){
18090             Roo.EventManager.on(this.checkbox,
18091                     Roo.isIE ? 'click' : 'change', this.onCheckChange, this);
18092         }
18093
18094         el.on("contextmenu", this.onContextMenu, this);
18095
18096         var icon = Roo.fly(this.iconNode);
18097         icon.on("click", this.onClick, this);
18098         icon.on("dblclick", this.onDblClick, this);
18099         icon.on("contextmenu", this.onContextMenu, this);
18100         E.on(this.ecNode, "click", this.ecClick, this, true);
18101
18102         if(this.node.disabled){
18103             this.addClass("x-tree-node-disabled");
18104         }
18105         if(this.node.hidden){
18106             this.addClass("x-tree-node-disabled");
18107         }
18108         var ot = this.node.getOwnerTree();
18109         var dd = ot.enableDD || ot.enableDrag || ot.enableDrop;
18110         if(dd && (!this.node.isRoot || ot.rootVisible)){
18111             Roo.dd.Registry.register(this.elNode, {
18112                 node: this.node,
18113                 handles: this.getDDHandles(),
18114                 isHandle: false
18115             });
18116         }
18117     },
18118
18119     getDDHandles : function(){
18120         return [this.iconNode, this.textNode];
18121     },
18122
18123     hide : function(){
18124         if(this.rendered){
18125             this.wrap.style.display = "none";
18126         }
18127     },
18128
18129     show : function(){
18130         if(this.rendered){
18131             this.wrap.style.display = "";
18132         }
18133     },
18134
18135     onContextMenu : function(e){
18136         if (this.node.hasListener("contextmenu") || this.node.getOwnerTree().hasListener("contextmenu")) {
18137             e.preventDefault();
18138             this.focus();
18139             this.fireEvent("contextmenu", this.node, e);
18140         }
18141     },
18142
18143     onClick : function(e){
18144         if(this.dropping){
18145             e.stopEvent();
18146             return;
18147         }
18148         if(this.fireEvent("beforeclick", this.node, e) !== false){
18149             if(!this.disabled && this.node.attributes.href){
18150                 this.fireEvent("click", this.node, e);
18151                 return;
18152             }
18153             e.preventDefault();
18154             if(this.disabled){
18155                 return;
18156             }
18157
18158             if(this.node.attributes.singleClickExpand && !this.animating && this.node.hasChildNodes()){
18159                 this.node.toggle();
18160             }
18161
18162             this.fireEvent("click", this.node, e);
18163         }else{
18164             e.stopEvent();
18165         }
18166     },
18167
18168     onDblClick : function(e){
18169         e.preventDefault();
18170         if(this.disabled){
18171             return;
18172         }
18173         if(this.checkbox){
18174             this.toggleCheck();
18175         }
18176         if(!this.animating && this.node.hasChildNodes()){
18177             this.node.toggle();
18178         }
18179         this.fireEvent("dblclick", this.node, e);
18180     },
18181
18182     onCheckChange : function(){
18183         var checked = this.checkbox.checked;
18184         this.node.attributes.checked = checked;
18185         this.fireEvent('checkchange', this.node, checked);
18186     },
18187
18188     ecClick : function(e){
18189         if(!this.animating && this.node.hasChildNodes()){
18190             this.node.toggle();
18191         }
18192     },
18193
18194     startDrop : function(){
18195         this.dropping = true;
18196     },
18197
18198     // delayed drop so the click event doesn't get fired on a drop
18199     endDrop : function(){
18200        setTimeout(function(){
18201            this.dropping = false;
18202        }.createDelegate(this), 50);
18203     },
18204
18205     expand : function(){
18206         this.updateExpandIcon();
18207         this.ctNode.style.display = "";
18208     },
18209
18210     focus : function(){
18211         if(!this.node.preventHScroll){
18212             try{this.anchor.focus();
18213             }catch(e){}
18214         }else if(!Roo.isIE){
18215             try{
18216                 var noscroll = this.node.getOwnerTree().getTreeEl().dom;
18217                 var l = noscroll.scrollLeft;
18218                 this.anchor.focus();
18219                 noscroll.scrollLeft = l;
18220             }catch(e){}
18221         }
18222     },
18223
18224     toggleCheck : function(value){
18225         var cb = this.checkbox;
18226         if(cb){
18227             cb.checked = (value === undefined ? !cb.checked : value);
18228         }
18229     },
18230
18231     blur : function(){
18232         try{
18233             this.anchor.blur();
18234         }catch(e){}
18235     },
18236
18237     animExpand : function(callback){
18238         var ct = Roo.get(this.ctNode);
18239         ct.stopFx();
18240         if(!this.node.hasChildNodes()){
18241             this.updateExpandIcon();
18242             this.ctNode.style.display = "";
18243             Roo.callback(callback);
18244             return;
18245         }
18246         this.animating = true;
18247         this.updateExpandIcon();
18248
18249         ct.slideIn('t', {
18250            callback : function(){
18251                this.animating = false;
18252                Roo.callback(callback);
18253             },
18254             scope: this,
18255             duration: this.node.ownerTree.duration || .25
18256         });
18257     },
18258
18259     highlight : function(){
18260         var tree = this.node.getOwnerTree();
18261         Roo.fly(this.wrap).highlight(
18262             tree.hlColor || "C3DAF9",
18263             {endColor: tree.hlBaseColor}
18264         );
18265     },
18266
18267     collapse : function(){
18268         this.updateExpandIcon();
18269         this.ctNode.style.display = "none";
18270     },
18271
18272     animCollapse : function(callback){
18273         var ct = Roo.get(this.ctNode);
18274         ct.enableDisplayMode('block');
18275         ct.stopFx();
18276
18277         this.animating = true;
18278         this.updateExpandIcon();
18279
18280         ct.slideOut('t', {
18281             callback : function(){
18282                this.animating = false;
18283                Roo.callback(callback);
18284             },
18285             scope: this,
18286             duration: this.node.ownerTree.duration || .25
18287         });
18288     },
18289
18290     getContainer : function(){
18291         return this.ctNode;
18292     },
18293
18294     getEl : function(){
18295         return this.wrap;
18296     },
18297
18298     appendDDGhost : function(ghostNode){
18299         ghostNode.appendChild(this.elNode.cloneNode(true));
18300     },
18301
18302     getDDRepairXY : function(){
18303         return Roo.lib.Dom.getXY(this.iconNode);
18304     },
18305
18306     onRender : function(){
18307         this.render();
18308     },
18309
18310     render : function(bulkRender){
18311         var n = this.node, a = n.attributes;
18312         var targetNode = n.parentNode ?
18313               n.parentNode.ui.getContainer() : n.ownerTree.innerCt.dom;
18314
18315         if(!this.rendered){
18316             this.rendered = true;
18317
18318             this.renderElements(n, a, targetNode, bulkRender);
18319
18320             if(a.qtip){
18321                if(this.textNode.setAttributeNS){
18322                    this.textNode.setAttributeNS("ext", "qtip", a.qtip);
18323                    if(a.qtipTitle){
18324                        this.textNode.setAttributeNS("ext", "qtitle", a.qtipTitle);
18325                    }
18326                }else{
18327                    this.textNode.setAttribute("ext:qtip", a.qtip);
18328                    if(a.qtipTitle){
18329                        this.textNode.setAttribute("ext:qtitle", a.qtipTitle);
18330                    }
18331                }
18332             }else if(a.qtipCfg){
18333                 a.qtipCfg.target = Roo.id(this.textNode);
18334                 Roo.QuickTips.register(a.qtipCfg);
18335             }
18336             this.initEvents();
18337             if(!this.node.expanded){
18338                 this.updateExpandIcon();
18339             }
18340         }else{
18341             if(bulkRender === true) {
18342                 targetNode.appendChild(this.wrap);
18343             }
18344         }
18345     },
18346
18347     renderElements : function(n, a, targetNode, bulkRender)
18348     {
18349         // add some indent caching, this helps performance when rendering a large tree
18350         this.indentMarkup = n.parentNode ? n.parentNode.ui.getChildIndent() : '';
18351         var t = n.getOwnerTree();
18352         var txt = t.renderer ? t.renderer(n.attributes) : Roo.util.Format.htmlEncode(n.text);
18353         if (typeof(n.attributes.html) != 'undefined') {
18354             txt = n.attributes.html;
18355         }
18356         var tip = t.rendererTip ? t.rendererTip(n.attributes) : txt;
18357         var cb = typeof a.checked == 'boolean';
18358         var href = a.href ? a.href : Roo.isGecko ? "" : "#";
18359         var buf = ['<li class="x-tree-node"><div class="x-tree-node-el ', a.cls,'">',
18360             '<span class="x-tree-node-indent">',this.indentMarkup,"</span>",
18361             '<img src="', this.emptyIcon, '" class="x-tree-ec-icon" />',
18362             '<img src="', a.icon || this.emptyIcon, '" class="x-tree-node-icon',(a.icon ? " x-tree-node-inline-icon" : ""),(a.iconCls ? " "+a.iconCls : ""),'" unselectable="on" />',
18363             cb ? ('<input class="x-tree-node-cb" type="checkbox" ' + (a.checked ? 'checked="checked" />' : ' />')) : '',
18364             '<a hidefocus="on" href="',href,'" tabIndex="1" ',
18365              a.hrefTarget ? ' target="'+a.hrefTarget+'"' : "", 
18366                 '><span unselectable="on" qtip="' , tip ,'">',txt,"</span></a></div>",
18367             '<ul class="x-tree-node-ct" style="display:none;"></ul>',
18368             "</li>"];
18369
18370         if(bulkRender !== true && n.nextSibling && n.nextSibling.ui.getEl()){
18371             this.wrap = Roo.DomHelper.insertHtml("beforeBegin",
18372                                 n.nextSibling.ui.getEl(), buf.join(""));
18373         }else{
18374             this.wrap = Roo.DomHelper.insertHtml("beforeEnd", targetNode, buf.join(""));
18375         }
18376
18377         this.elNode = this.wrap.childNodes[0];
18378         this.ctNode = this.wrap.childNodes[1];
18379         var cs = this.elNode.childNodes;
18380         this.indentNode = cs[0];
18381         this.ecNode = cs[1];
18382         this.iconNode = cs[2];
18383         var index = 3;
18384         if(cb){
18385             this.checkbox = cs[3];
18386             index++;
18387         }
18388         this.anchor = cs[index];
18389         this.textNode = cs[index].firstChild;
18390     },
18391
18392     getAnchor : function(){
18393         return this.anchor;
18394     },
18395
18396     getTextEl : function(){
18397         return this.textNode;
18398     },
18399
18400     getIconEl : function(){
18401         return this.iconNode;
18402     },
18403
18404     isChecked : function(){
18405         return this.checkbox ? this.checkbox.checked : false;
18406     },
18407
18408     updateExpandIcon : function(){
18409         if(this.rendered){
18410             var n = this.node, c1, c2;
18411             var cls = n.isLast() ? "x-tree-elbow-end" : "x-tree-elbow";
18412             var hasChild = n.hasChildNodes();
18413             if(hasChild){
18414                 if(n.expanded){
18415                     cls += "-minus";
18416                     c1 = "x-tree-node-collapsed";
18417                     c2 = "x-tree-node-expanded";
18418                 }else{
18419                     cls += "-plus";
18420                     c1 = "x-tree-node-expanded";
18421                     c2 = "x-tree-node-collapsed";
18422                 }
18423                 if(this.wasLeaf){
18424                     this.removeClass("x-tree-node-leaf");
18425                     this.wasLeaf = false;
18426                 }
18427                 if(this.c1 != c1 || this.c2 != c2){
18428                     Roo.fly(this.elNode).replaceClass(c1, c2);
18429                     this.c1 = c1; this.c2 = c2;
18430                 }
18431             }else{
18432                 // this changes non-leafs into leafs if they have no children.
18433                 // it's not very rational behaviour..
18434                 
18435                 if(!this.wasLeaf && this.node.leaf){
18436                     Roo.fly(this.elNode).replaceClass("x-tree-node-expanded", "x-tree-node-leaf");
18437                     delete this.c1;
18438                     delete this.c2;
18439                     this.wasLeaf = true;
18440                 }
18441             }
18442             var ecc = "x-tree-ec-icon "+cls;
18443             if(this.ecc != ecc){
18444                 this.ecNode.className = ecc;
18445                 this.ecc = ecc;
18446             }
18447         }
18448     },
18449
18450     getChildIndent : function(){
18451         if(!this.childIndent){
18452             var buf = [];
18453             var p = this.node;
18454             while(p){
18455                 if(!p.isRoot || (p.isRoot && p.ownerTree.rootVisible)){
18456                     if(!p.isLast()) {
18457                         buf.unshift('<img src="'+this.emptyIcon+'" class="x-tree-elbow-line" />');
18458                     } else {
18459                         buf.unshift('<img src="'+this.emptyIcon+'" class="x-tree-icon" />');
18460                     }
18461                 }
18462                 p = p.parentNode;
18463             }
18464             this.childIndent = buf.join("");
18465         }
18466         return this.childIndent;
18467     },
18468
18469     renderIndent : function(){
18470         if(this.rendered){
18471             var indent = "";
18472             var p = this.node.parentNode;
18473             if(p){
18474                 indent = p.ui.getChildIndent();
18475             }
18476             if(this.indentMarkup != indent){ // don't rerender if not required
18477                 this.indentNode.innerHTML = indent;
18478                 this.indentMarkup = indent;
18479             }
18480             this.updateExpandIcon();
18481         }
18482     }
18483 };
18484
18485 Roo.tree.RootTreeNodeUI = function(){
18486     Roo.tree.RootTreeNodeUI.superclass.constructor.apply(this, arguments);
18487 };
18488 Roo.extend(Roo.tree.RootTreeNodeUI, Roo.tree.TreeNodeUI, {
18489     render : function(){
18490         if(!this.rendered){
18491             var targetNode = this.node.ownerTree.innerCt.dom;
18492             this.node.expanded = true;
18493             targetNode.innerHTML = '<div class="x-tree-root-node"></div>';
18494             this.wrap = this.ctNode = targetNode.firstChild;
18495         }
18496     },
18497     collapse : function(){
18498     },
18499     expand : function(){
18500     }
18501 });/*
18502  * Based on:
18503  * Ext JS Library 1.1.1
18504  * Copyright(c) 2006-2007, Ext JS, LLC.
18505  *
18506  * Originally Released Under LGPL - original licence link has changed is not relivant.
18507  *
18508  * Fork - LGPL
18509  * <script type="text/javascript">
18510  */
18511 /**
18512  * @class Roo.tree.TreeLoader
18513  * @extends Roo.util.Observable
18514  * A TreeLoader provides for lazy loading of an {@link Roo.tree.TreeNode}'s child
18515  * nodes from a specified URL. The response must be a javascript Array definition
18516  * who's elements are node definition objects. eg:
18517  * <pre><code>
18518 {  success : true,
18519    data :      [
18520    
18521     { 'id': 1, 'text': 'A folder Node', 'leaf': false },
18522     { 'id': 2, 'text': 'A leaf Node', 'leaf': true }
18523     ]
18524 }
18525
18526
18527 </code></pre>
18528  * <br><br>
18529  * The old style respose with just an array is still supported, but not recommended.
18530  * <br><br>
18531  *
18532  * A server request is sent, and child nodes are loaded only when a node is expanded.
18533  * The loading node's id is passed to the server under the parameter name "node" to
18534  * enable the server to produce the correct child nodes.
18535  * <br><br>
18536  * To pass extra parameters, an event handler may be attached to the "beforeload"
18537  * event, and the parameters specified in the TreeLoader's baseParams property:
18538  * <pre><code>
18539     myTreeLoader.on("beforeload", function(treeLoader, node) {
18540         this.baseParams.category = node.attributes.category;
18541     }, this);
18542 </code></pre><
18543  * This would pass an HTTP parameter called "category" to the server containing
18544  * the value of the Node's "category" attribute.
18545  * @constructor
18546  * Creates a new Treeloader.
18547  * @param {Object} config A config object containing config properties.
18548  */
18549 Roo.tree.TreeLoader = function(config){
18550     this.baseParams = {};
18551     this.requestMethod = "POST";
18552     Roo.apply(this, config);
18553
18554     this.addEvents({
18555     
18556         /**
18557          * @event beforeload
18558          * Fires before a network request is made to retrieve the Json text which specifies a node's children.
18559          * @param {Object} This TreeLoader object.
18560          * @param {Object} node The {@link Roo.tree.TreeNode} object being loaded.
18561          * @param {Object} callback The callback function specified in the {@link #load} call.
18562          */
18563         beforeload : true,
18564         /**
18565          * @event load
18566          * Fires when the node has been successfuly loaded.
18567          * @param {Object} This TreeLoader object.
18568          * @param {Object} node The {@link Roo.tree.TreeNode} object being loaded.
18569          * @param {Object} response The response object containing the data from the server.
18570          */
18571         load : true,
18572         /**
18573          * @event loadexception
18574          * Fires if the network request failed.
18575          * @param {Object} This TreeLoader object.
18576          * @param {Object} node The {@link Roo.tree.TreeNode} object being loaded.
18577          * @param {Object} response The response object containing the data from the server.
18578          */
18579         loadexception : true,
18580         /**
18581          * @event create
18582          * Fires before a node is created, enabling you to return custom Node types 
18583          * @param {Object} This TreeLoader object.
18584          * @param {Object} attr - the data returned from the AJAX call (modify it to suit)
18585          */
18586         create : true
18587     });
18588
18589     Roo.tree.TreeLoader.superclass.constructor.call(this);
18590 };
18591
18592 Roo.extend(Roo.tree.TreeLoader, Roo.util.Observable, {
18593     /**
18594     * @cfg {String} dataUrl The URL from which to request a Json string which
18595     * specifies an array of node definition object representing the child nodes
18596     * to be loaded.
18597     */
18598     /**
18599     * @cfg {String} requestMethod either GET or POST
18600     * defaults to POST (due to BC)
18601     * to be loaded.
18602     */
18603     /**
18604     * @cfg {Object} baseParams (optional) An object containing properties which
18605     * specify HTTP parameters to be passed to each request for child nodes.
18606     */
18607     /**
18608     * @cfg {Object} baseAttrs (optional) An object containing attributes to be added to all nodes
18609     * created by this loader. If the attributes sent by the server have an attribute in this object,
18610     * they take priority.
18611     */
18612     /**
18613     * @cfg {Object} uiProviders (optional) An object containing properties which
18614     * 
18615     * DEPRECATED - use 'create' event handler to modify attributes - which affect creation.
18616     * specify custom {@link Roo.tree.TreeNodeUI} implementations. If the optional
18617     * <i>uiProvider</i> attribute of a returned child node is a string rather
18618     * than a reference to a TreeNodeUI implementation, this that string value
18619     * is used as a property name in the uiProviders object. You can define the provider named
18620     * 'default' , and this will be used for all nodes (if no uiProvider is delivered by the node data)
18621     */
18622     uiProviders : {},
18623
18624     /**
18625     * @cfg {Boolean} clearOnLoad (optional) Default to true. Remove previously existing
18626     * child nodes before loading.
18627     */
18628     clearOnLoad : true,
18629
18630     /**
18631     * @cfg {String} root (optional) Default to false. Use this to read data from an object 
18632     * property on loading, rather than expecting an array. (eg. more compatible to a standard
18633     * Grid query { data : [ .....] }
18634     */
18635     
18636     root : false,
18637      /**
18638     * @cfg {String} queryParam (optional) 
18639     * Name of the query as it will be passed on the querystring (defaults to 'node')
18640     * eg. the request will be ?node=[id]
18641     */
18642     
18643     
18644     queryParam: false,
18645     
18646     /**
18647      * Load an {@link Roo.tree.TreeNode} from the URL specified in the constructor.
18648      * This is called automatically when a node is expanded, but may be used to reload
18649      * a node (or append new children if the {@link #clearOnLoad} option is false.)
18650      * @param {Roo.tree.TreeNode} node
18651      * @param {Function} callback
18652      */
18653     load : function(node, callback){
18654         if(this.clearOnLoad){
18655             while(node.firstChild){
18656                 node.removeChild(node.firstChild);
18657             }
18658         }
18659         if(node.attributes.children){ // preloaded json children
18660             var cs = node.attributes.children;
18661             for(var i = 0, len = cs.length; i < len; i++){
18662                 node.appendChild(this.createNode(cs[i]));
18663             }
18664             if(typeof callback == "function"){
18665                 callback();
18666             }
18667         }else if(this.dataUrl){
18668             this.requestData(node, callback);
18669         }
18670     },
18671
18672     getParams: function(node){
18673         var buf = [], bp = this.baseParams;
18674         for(var key in bp){
18675             if(typeof bp[key] != "function"){
18676                 buf.push(encodeURIComponent(key), "=", encodeURIComponent(bp[key]), "&");
18677             }
18678         }
18679         var n = this.queryParam === false ? 'node' : this.queryParam;
18680         buf.push(n + "=", encodeURIComponent(node.id));
18681         return buf.join("");
18682     },
18683
18684     requestData : function(node, callback){
18685         if(this.fireEvent("beforeload", this, node, callback) !== false){
18686             this.transId = Roo.Ajax.request({
18687                 method:this.requestMethod,
18688                 url: this.dataUrl||this.url,
18689                 success: this.handleResponse,
18690                 failure: this.handleFailure,
18691                 scope: this,
18692                 argument: {callback: callback, node: node},
18693                 params: this.getParams(node)
18694             });
18695         }else{
18696             // if the load is cancelled, make sure we notify
18697             // the node that we are done
18698             if(typeof callback == "function"){
18699                 callback();
18700             }
18701         }
18702     },
18703
18704     isLoading : function(){
18705         return this.transId ? true : false;
18706     },
18707
18708     abort : function(){
18709         if(this.isLoading()){
18710             Roo.Ajax.abort(this.transId);
18711         }
18712     },
18713
18714     // private
18715     createNode : function(attr)
18716     {
18717         // apply baseAttrs, nice idea Corey!
18718         if(this.baseAttrs){
18719             Roo.applyIf(attr, this.baseAttrs);
18720         }
18721         if(this.applyLoader !== false){
18722             attr.loader = this;
18723         }
18724         // uiProvider = depreciated..
18725         
18726         if(typeof(attr.uiProvider) == 'string'){
18727            attr.uiProvider = this.uiProviders[attr.uiProvider] || 
18728                 /**  eval:var:attr */ eval(attr.uiProvider);
18729         }
18730         if(typeof(this.uiProviders['default']) != 'undefined') {
18731             attr.uiProvider = this.uiProviders['default'];
18732         }
18733         
18734         this.fireEvent('create', this, attr);
18735         
18736         attr.leaf  = typeof(attr.leaf) == 'string' ? attr.leaf * 1 : attr.leaf;
18737         return(attr.leaf ?
18738                         new Roo.tree.TreeNode(attr) :
18739                         new Roo.tree.AsyncTreeNode(attr));
18740     },
18741
18742     processResponse : function(response, node, callback)
18743     {
18744         var json = response.responseText;
18745         try {
18746             
18747             var o = Roo.decode(json);
18748             
18749             if (this.root === false && typeof(o.success) != undefined) {
18750                 this.root = 'data'; // the default behaviour for list like data..
18751                 }
18752                 
18753             if (this.root !== false &&  !o.success) {
18754                 // it's a failure condition.
18755                 var a = response.argument;
18756                 this.fireEvent("loadexception", this, a.node, response);
18757                 Roo.log("Load failed - should have a handler really");
18758                 return;
18759             }
18760             
18761             
18762             
18763             if (this.root !== false) {
18764                  o = o[this.root];
18765             }
18766             
18767             for(var i = 0, len = o.length; i < len; i++){
18768                 var n = this.createNode(o[i]);
18769                 if(n){
18770                     node.appendChild(n);
18771                 }
18772             }
18773             if(typeof callback == "function"){
18774                 callback(this, node);
18775             }
18776         }catch(e){
18777             this.handleFailure(response);
18778         }
18779     },
18780
18781     handleResponse : function(response){
18782         this.transId = false;
18783         var a = response.argument;
18784         this.processResponse(response, a.node, a.callback);
18785         this.fireEvent("load", this, a.node, response);
18786     },
18787
18788     handleFailure : function(response)
18789     {
18790         // should handle failure better..
18791         this.transId = false;
18792         var a = response.argument;
18793         this.fireEvent("loadexception", this, a.node, response);
18794         if(typeof a.callback == "function"){
18795             a.callback(this, a.node);
18796         }
18797     }
18798 });/*
18799  * Based on:
18800  * Ext JS Library 1.1.1
18801  * Copyright(c) 2006-2007, Ext JS, LLC.
18802  *
18803  * Originally Released Under LGPL - original licence link has changed is not relivant.
18804  *
18805  * Fork - LGPL
18806  * <script type="text/javascript">
18807  */
18808
18809 /**
18810 * @class Roo.tree.TreeFilter
18811 * Note this class is experimental and doesn't update the indent (lines) or expand collapse icons of the nodes
18812 * @param {TreePanel} tree
18813 * @param {Object} config (optional)
18814  */
18815 Roo.tree.TreeFilter = function(tree, config){
18816     this.tree = tree;
18817     this.filtered = {};
18818     Roo.apply(this, config);
18819 };
18820
18821 Roo.tree.TreeFilter.prototype = {
18822     clearBlank:false,
18823     reverse:false,
18824     autoClear:false,
18825     remove:false,
18826
18827      /**
18828      * Filter the data by a specific attribute.
18829      * @param {String/RegExp} value Either string that the attribute value
18830      * should start with or a RegExp to test against the attribute
18831      * @param {String} attr (optional) The attribute passed in your node's attributes collection. Defaults to "text".
18832      * @param {TreeNode} startNode (optional) The node to start the filter at.
18833      */
18834     filter : function(value, attr, startNode){
18835         attr = attr || "text";
18836         var f;
18837         if(typeof value == "string"){
18838             var vlen = value.length;
18839             // auto clear empty filter
18840             if(vlen == 0 && this.clearBlank){
18841                 this.clear();
18842                 return;
18843             }
18844             value = value.toLowerCase();
18845             f = function(n){
18846                 return n.attributes[attr].substr(0, vlen).toLowerCase() == value;
18847             };
18848         }else if(value.exec){ // regex?
18849             f = function(n){
18850                 return value.test(n.attributes[attr]);
18851             };
18852         }else{
18853             throw 'Illegal filter type, must be string or regex';
18854         }
18855         this.filterBy(f, null, startNode);
18856         },
18857
18858     /**
18859      * Filter by a function. The passed function will be called with each
18860      * node in the tree (or from the startNode). If the function returns true, the node is kept
18861      * otherwise it is filtered. If a node is filtered, its children are also filtered.
18862      * @param {Function} fn The filter function
18863      * @param {Object} scope (optional) The scope of the function (defaults to the current node)
18864      */
18865     filterBy : function(fn, scope, startNode){
18866         startNode = startNode || this.tree.root;
18867         if(this.autoClear){
18868             this.clear();
18869         }
18870         var af = this.filtered, rv = this.reverse;
18871         var f = function(n){
18872             if(n == startNode){
18873                 return true;
18874             }
18875             if(af[n.id]){
18876                 return false;
18877             }
18878             var m = fn.call(scope || n, n);
18879             if(!m || rv){
18880                 af[n.id] = n;
18881                 n.ui.hide();
18882                 return false;
18883             }
18884             return true;
18885         };
18886         startNode.cascade(f);
18887         if(this.remove){
18888            for(var id in af){
18889                if(typeof id != "function"){
18890                    var n = af[id];
18891                    if(n && n.parentNode){
18892                        n.parentNode.removeChild(n);
18893                    }
18894                }
18895            }
18896         }
18897     },
18898
18899     /**
18900      * Clears the current filter. Note: with the "remove" option
18901      * set a filter cannot be cleared.
18902      */
18903     clear : function(){
18904         var t = this.tree;
18905         var af = this.filtered;
18906         for(var id in af){
18907             if(typeof id != "function"){
18908                 var n = af[id];
18909                 if(n){
18910                     n.ui.show();
18911                 }
18912             }
18913         }
18914         this.filtered = {};
18915     }
18916 };
18917 /*
18918  * Based on:
18919  * Ext JS Library 1.1.1
18920  * Copyright(c) 2006-2007, Ext JS, LLC.
18921  *
18922  * Originally Released Under LGPL - original licence link has changed is not relivant.
18923  *
18924  * Fork - LGPL
18925  * <script type="text/javascript">
18926  */
18927  
18928
18929 /**
18930  * @class Roo.tree.TreeSorter
18931  * Provides sorting of nodes in a TreePanel
18932  * 
18933  * @cfg {Boolean} folderSort True to sort leaf nodes under non leaf nodes
18934  * @cfg {String} property The named attribute on the node to sort by (defaults to text)
18935  * @cfg {String} dir The direction to sort (asc or desc) (defaults to asc)
18936  * @cfg {String} leafAttr The attribute used to determine leaf nodes in folder sort (defaults to "leaf")
18937  * @cfg {Boolean} caseSensitive true for case sensitive sort (defaults to false)
18938  * @cfg {Function} sortType A custom "casting" function used to convert node values before sorting
18939  * @constructor
18940  * @param {TreePanel} tree
18941  * @param {Object} config
18942  */
18943 Roo.tree.TreeSorter = function(tree, config){
18944     Roo.apply(this, config);
18945     tree.on("beforechildrenrendered", this.doSort, this);
18946     tree.on("append", this.updateSort, this);
18947     tree.on("insert", this.updateSort, this);
18948     
18949     var dsc = this.dir && this.dir.toLowerCase() == "desc";
18950     var p = this.property || "text";
18951     var sortType = this.sortType;
18952     var fs = this.folderSort;
18953     var cs = this.caseSensitive === true;
18954     var leafAttr = this.leafAttr || 'leaf';
18955
18956     this.sortFn = function(n1, n2){
18957         if(fs){
18958             if(n1.attributes[leafAttr] && !n2.attributes[leafAttr]){
18959                 return 1;
18960             }
18961             if(!n1.attributes[leafAttr] && n2.attributes[leafAttr]){
18962                 return -1;
18963             }
18964         }
18965         var v1 = sortType ? sortType(n1) : (cs ? n1.attributes[p] : n1.attributes[p].toUpperCase());
18966         var v2 = sortType ? sortType(n2) : (cs ? n2.attributes[p] : n2.attributes[p].toUpperCase());
18967         if(v1 < v2){
18968                         return dsc ? +1 : -1;
18969                 }else if(v1 > v2){
18970                         return dsc ? -1 : +1;
18971         }else{
18972                 return 0;
18973         }
18974     };
18975 };
18976
18977 Roo.tree.TreeSorter.prototype = {
18978     doSort : function(node){
18979         node.sort(this.sortFn);
18980     },
18981     
18982     compareNodes : function(n1, n2){
18983         return (n1.text.toUpperCase() > n2.text.toUpperCase() ? 1 : -1);
18984     },
18985     
18986     updateSort : function(tree, node){
18987         if(node.childrenRendered){
18988             this.doSort.defer(1, this, [node]);
18989         }
18990     }
18991 };/*
18992  * Based on:
18993  * Ext JS Library 1.1.1
18994  * Copyright(c) 2006-2007, Ext JS, LLC.
18995  *
18996  * Originally Released Under LGPL - original licence link has changed is not relivant.
18997  *
18998  * Fork - LGPL
18999  * <script type="text/javascript">
19000  */
19001
19002 if(Roo.dd.DropZone){
19003     
19004 Roo.tree.TreeDropZone = function(tree, config){
19005     this.allowParentInsert = false;
19006     this.allowContainerDrop = false;
19007     this.appendOnly = false;
19008     Roo.tree.TreeDropZone.superclass.constructor.call(this, tree.innerCt, config);
19009     this.tree = tree;
19010     this.lastInsertClass = "x-tree-no-status";
19011     this.dragOverData = {};
19012 };
19013
19014 Roo.extend(Roo.tree.TreeDropZone, Roo.dd.DropZone, {
19015     ddGroup : "TreeDD",
19016     scroll:  true,
19017     
19018     expandDelay : 1000,
19019     
19020     expandNode : function(node){
19021         if(node.hasChildNodes() && !node.isExpanded()){
19022             node.expand(false, null, this.triggerCacheRefresh.createDelegate(this));
19023         }
19024     },
19025     
19026     queueExpand : function(node){
19027         this.expandProcId = this.expandNode.defer(this.expandDelay, this, [node]);
19028     },
19029     
19030     cancelExpand : function(){
19031         if(this.expandProcId){
19032             clearTimeout(this.expandProcId);
19033             this.expandProcId = false;
19034         }
19035     },
19036     
19037     isValidDropPoint : function(n, pt, dd, e, data){
19038         if(!n || !data){ return false; }
19039         var targetNode = n.node;
19040         var dropNode = data.node;
19041         // default drop rules
19042         if(!(targetNode && targetNode.isTarget && pt)){
19043             return false;
19044         }
19045         if(pt == "append" && targetNode.allowChildren === false){
19046             return false;
19047         }
19048         if((pt == "above" || pt == "below") && (targetNode.parentNode && targetNode.parentNode.allowChildren === false)){
19049             return false;
19050         }
19051         if(dropNode && (targetNode == dropNode || dropNode.contains(targetNode))){
19052             return false;
19053         }
19054         // reuse the object
19055         var overEvent = this.dragOverData;
19056         overEvent.tree = this.tree;
19057         overEvent.target = targetNode;
19058         overEvent.data = data;
19059         overEvent.point = pt;
19060         overEvent.source = dd;
19061         overEvent.rawEvent = e;
19062         overEvent.dropNode = dropNode;
19063         overEvent.cancel = false;  
19064         var result = this.tree.fireEvent("nodedragover", overEvent);
19065         return overEvent.cancel === false && result !== false;
19066     },
19067     
19068     getDropPoint : function(e, n, dd)
19069     {
19070         var tn = n.node;
19071         if(tn.isRoot){
19072             return tn.allowChildren !== false ? "append" : false; // always append for root
19073         }
19074         var dragEl = n.ddel;
19075         var t = Roo.lib.Dom.getY(dragEl), b = t + dragEl.offsetHeight;
19076         var y = Roo.lib.Event.getPageY(e);
19077         //var noAppend = tn.allowChildren === false || tn.isLeaf();
19078         
19079         // we may drop nodes anywhere, as long as allowChildren has not been set to false..
19080         var noAppend = tn.allowChildren === false;
19081         if(this.appendOnly || tn.parentNode.allowChildren === false){
19082             return noAppend ? false : "append";
19083         }
19084         var noBelow = false;
19085         if(!this.allowParentInsert){
19086             noBelow = tn.hasChildNodes() && tn.isExpanded();
19087         }
19088         var q = (b - t) / (noAppend ? 2 : 3);
19089         if(y >= t && y < (t + q)){
19090             return "above";
19091         }else if(!noBelow && (noAppend || y >= b-q && y <= b)){
19092             return "below";
19093         }else{
19094             return "append";
19095         }
19096     },
19097     
19098     onNodeEnter : function(n, dd, e, data)
19099     {
19100         this.cancelExpand();
19101     },
19102     
19103     onNodeOver : function(n, dd, e, data)
19104     {
19105        
19106         var pt = this.getDropPoint(e, n, dd);
19107         var node = n.node;
19108         
19109         // auto node expand check
19110         if(!this.expandProcId && pt == "append" && node.hasChildNodes() && !n.node.isExpanded()){
19111             this.queueExpand(node);
19112         }else if(pt != "append"){
19113             this.cancelExpand();
19114         }
19115         
19116         // set the insert point style on the target node
19117         var returnCls = this.dropNotAllowed;
19118         if(this.isValidDropPoint(n, pt, dd, e, data)){
19119            if(pt){
19120                var el = n.ddel;
19121                var cls;
19122                if(pt == "above"){
19123                    returnCls = n.node.isFirst() ? "x-tree-drop-ok-above" : "x-tree-drop-ok-between";
19124                    cls = "x-tree-drag-insert-above";
19125                }else if(pt == "below"){
19126                    returnCls = n.node.isLast() ? "x-tree-drop-ok-below" : "x-tree-drop-ok-between";
19127                    cls = "x-tree-drag-insert-below";
19128                }else{
19129                    returnCls = "x-tree-drop-ok-append";
19130                    cls = "x-tree-drag-append";
19131                }
19132                if(this.lastInsertClass != cls){
19133                    Roo.fly(el).replaceClass(this.lastInsertClass, cls);
19134                    this.lastInsertClass = cls;
19135                }
19136            }
19137        }
19138        return returnCls;
19139     },
19140     
19141     onNodeOut : function(n, dd, e, data){
19142         
19143         this.cancelExpand();
19144         this.removeDropIndicators(n);
19145     },
19146     
19147     onNodeDrop : function(n, dd, e, data){
19148         var point = this.getDropPoint(e, n, dd);
19149         var targetNode = n.node;
19150         targetNode.ui.startDrop();
19151         if(!this.isValidDropPoint(n, point, dd, e, data)){
19152             targetNode.ui.endDrop();
19153             return false;
19154         }
19155         // first try to find the drop node
19156         var dropNode = data.node || (dd.getTreeNode ? dd.getTreeNode(data, targetNode, point, e) : null);
19157         var dropEvent = {
19158             tree : this.tree,
19159             target: targetNode,
19160             data: data,
19161             point: point,
19162             source: dd,
19163             rawEvent: e,
19164             dropNode: dropNode,
19165             cancel: !dropNode   
19166         };
19167         var retval = this.tree.fireEvent("beforenodedrop", dropEvent);
19168         if(retval === false || dropEvent.cancel === true || !dropEvent.dropNode){
19169             targetNode.ui.endDrop();
19170             return false;
19171         }
19172         // allow target changing
19173         targetNode = dropEvent.target;
19174         if(point == "append" && !targetNode.isExpanded()){
19175             targetNode.expand(false, null, function(){
19176                 this.completeDrop(dropEvent);
19177             }.createDelegate(this));
19178         }else{
19179             this.completeDrop(dropEvent);
19180         }
19181         return true;
19182     },
19183     
19184     completeDrop : function(de){
19185         var ns = de.dropNode, p = de.point, t = de.target;
19186         if(!(ns instanceof Array)){
19187             ns = [ns];
19188         }
19189         var n;
19190         for(var i = 0, len = ns.length; i < len; i++){
19191             n = ns[i];
19192             if(p == "above"){
19193                 t.parentNode.insertBefore(n, t);
19194             }else if(p == "below"){
19195                 t.parentNode.insertBefore(n, t.nextSibling);
19196             }else{
19197                 t.appendChild(n);
19198             }
19199         }
19200         n.ui.focus();
19201         if(this.tree.hlDrop){
19202             n.ui.highlight();
19203         }
19204         t.ui.endDrop();
19205         this.tree.fireEvent("nodedrop", de);
19206     },
19207     
19208     afterNodeMoved : function(dd, data, e, targetNode, dropNode){
19209         if(this.tree.hlDrop){
19210             dropNode.ui.focus();
19211             dropNode.ui.highlight();
19212         }
19213         this.tree.fireEvent("nodedrop", this.tree, targetNode, data, dd, e);
19214     },
19215     
19216     getTree : function(){
19217         return this.tree;
19218     },
19219     
19220     removeDropIndicators : function(n){
19221         if(n && n.ddel){
19222             var el = n.ddel;
19223             Roo.fly(el).removeClass([
19224                     "x-tree-drag-insert-above",
19225                     "x-tree-drag-insert-below",
19226                     "x-tree-drag-append"]);
19227             this.lastInsertClass = "_noclass";
19228         }
19229     },
19230     
19231     beforeDragDrop : function(target, e, id){
19232         this.cancelExpand();
19233         return true;
19234     },
19235     
19236     afterRepair : function(data){
19237         if(data && Roo.enableFx){
19238             data.node.ui.highlight();
19239         }
19240         this.hideProxy();
19241     } 
19242     
19243 });
19244
19245 }
19246 /*
19247  * Based on:
19248  * Ext JS Library 1.1.1
19249  * Copyright(c) 2006-2007, Ext JS, LLC.
19250  *
19251  * Originally Released Under LGPL - original licence link has changed is not relivant.
19252  *
19253  * Fork - LGPL
19254  * <script type="text/javascript">
19255  */
19256  
19257
19258 if(Roo.dd.DragZone){
19259 Roo.tree.TreeDragZone = function(tree, config){
19260     Roo.tree.TreeDragZone.superclass.constructor.call(this, tree.getTreeEl(), config);
19261     this.tree = tree;
19262 };
19263
19264 Roo.extend(Roo.tree.TreeDragZone, Roo.dd.DragZone, {
19265     ddGroup : "TreeDD",
19266    
19267     onBeforeDrag : function(data, e){
19268         var n = data.node;
19269         return n && n.draggable && !n.disabled;
19270     },
19271      
19272     
19273     onInitDrag : function(e){
19274         var data = this.dragData;
19275         this.tree.getSelectionModel().select(data.node);
19276         this.proxy.update("");
19277         data.node.ui.appendDDGhost(this.proxy.ghost.dom);
19278         this.tree.fireEvent("startdrag", this.tree, data.node, e);
19279     },
19280     
19281     getRepairXY : function(e, data){
19282         return data.node.ui.getDDRepairXY();
19283     },
19284     
19285     onEndDrag : function(data, e){
19286         this.tree.fireEvent("enddrag", this.tree, data.node, e);
19287         
19288         
19289     },
19290     
19291     onValidDrop : function(dd, e, id){
19292         this.tree.fireEvent("dragdrop", this.tree, this.dragData.node, dd, e);
19293         this.hideProxy();
19294     },
19295     
19296     beforeInvalidDrop : function(e, id){
19297         // this scrolls the original position back into view
19298         var sm = this.tree.getSelectionModel();
19299         sm.clearSelections();
19300         sm.select(this.dragData.node);
19301     }
19302 });
19303 }/*
19304  * Based on:
19305  * Ext JS Library 1.1.1
19306  * Copyright(c) 2006-2007, Ext JS, LLC.
19307  *
19308  * Originally Released Under LGPL - original licence link has changed is not relivant.
19309  *
19310  * Fork - LGPL
19311  * <script type="text/javascript">
19312  */
19313 /**
19314  * @class Roo.tree.TreeEditor
19315  * @extends Roo.Editor
19316  * Provides editor functionality for inline tree node editing.  Any valid {@link Roo.form.Field} can be used
19317  * as the editor field.
19318  * @constructor
19319  * @param {Object} config (used to be the tree panel.)
19320  * @param {Object} oldconfig DEPRECIATED Either a prebuilt {@link Roo.form.Field} instance or a Field config object
19321  * 
19322  * @cfg {Roo.tree.TreePanel} tree The tree to bind to.
19323  * @cfg {Roo.form.TextField|Object} field The field configuration
19324  *
19325  * 
19326  */
19327 Roo.tree.TreeEditor = function(config, oldconfig) { // was -- (tree, config){
19328     var tree = config;
19329     var field;
19330     if (oldconfig) { // old style..
19331         field = oldconfig.events ? oldconfig : new Roo.form.TextField(oldconfig);
19332     } else {
19333         // new style..
19334         tree = config.tree;
19335         config.field = config.field  || {};
19336         config.field.xtype = 'TextField';
19337         field = Roo.factory(config.field, Roo.form);
19338     }
19339     config = config || {};
19340     
19341     
19342     this.addEvents({
19343         /**
19344          * @event beforenodeedit
19345          * Fires when editing is initiated, but before the value changes.  Editing can be canceled by returning
19346          * false from the handler of this event.
19347          * @param {Editor} this
19348          * @param {Roo.tree.Node} node 
19349          */
19350         "beforenodeedit" : true
19351     });
19352     
19353     //Roo.log(config);
19354     Roo.tree.TreeEditor.superclass.constructor.call(this, field, config);
19355
19356     this.tree = tree;
19357
19358     tree.on('beforeclick', this.beforeNodeClick, this);
19359     tree.getTreeEl().on('mousedown', this.hide, this);
19360     this.on('complete', this.updateNode, this);
19361     this.on('beforestartedit', this.fitToTree, this);
19362     this.on('startedit', this.bindScroll, this, {delay:10});
19363     this.on('specialkey', this.onSpecialKey, this);
19364 };
19365
19366 Roo.extend(Roo.tree.TreeEditor, Roo.Editor, {
19367     /**
19368      * @cfg {String} alignment
19369      * The position to align to (see {@link Roo.Element#alignTo} for more details, defaults to "l-l").
19370      */
19371     alignment: "l-l",
19372     // inherit
19373     autoSize: false,
19374     /**
19375      * @cfg {Boolean} hideEl
19376      * True to hide the bound element while the editor is displayed (defaults to false)
19377      */
19378     hideEl : false,
19379     /**
19380      * @cfg {String} cls
19381      * CSS class to apply to the editor (defaults to "x-small-editor x-tree-editor")
19382      */
19383     cls: "x-small-editor x-tree-editor",
19384     /**
19385      * @cfg {Boolean} shim
19386      * True to shim the editor if selects/iframes could be displayed beneath it (defaults to false)
19387      */
19388     shim:false,
19389     // inherit
19390     shadow:"frame",
19391     /**
19392      * @cfg {Number} maxWidth
19393      * The maximum width in pixels of the editor field (defaults to 250).  Note that if the maxWidth would exceed
19394      * the containing tree element's size, it will be automatically limited for you to the container width, taking
19395      * scroll and client offsets into account prior to each edit.
19396      */
19397     maxWidth: 250,
19398
19399     editDelay : 350,
19400
19401     // private
19402     fitToTree : function(ed, el){
19403         var td = this.tree.getTreeEl().dom, nd = el.dom;
19404         if(td.scrollLeft >  nd.offsetLeft){ // ensure the node left point is visible
19405             td.scrollLeft = nd.offsetLeft;
19406         }
19407         var w = Math.min(
19408                 this.maxWidth,
19409                 (td.clientWidth > 20 ? td.clientWidth : td.offsetWidth) - Math.max(0, nd.offsetLeft-td.scrollLeft) - /*cushion*/5);
19410         this.setSize(w, '');
19411         
19412         return this.fireEvent('beforenodeedit', this, this.editNode);
19413         
19414     },
19415
19416     // private
19417     triggerEdit : function(node){
19418         this.completeEdit();
19419         this.editNode = node;
19420         this.startEdit(node.ui.textNode, node.text);
19421     },
19422
19423     // private
19424     bindScroll : function(){
19425         this.tree.getTreeEl().on('scroll', this.cancelEdit, this);
19426     },
19427
19428     // private
19429     beforeNodeClick : function(node, e){
19430         var sinceLast = (this.lastClick ? this.lastClick.getElapsed() : 0);
19431         this.lastClick = new Date();
19432         if(sinceLast > this.editDelay && this.tree.getSelectionModel().isSelected(node)){
19433             e.stopEvent();
19434             this.triggerEdit(node);
19435             return false;
19436         }
19437         return true;
19438     },
19439
19440     // private
19441     updateNode : function(ed, value){
19442         this.tree.getTreeEl().un('scroll', this.cancelEdit, this);
19443         this.editNode.setText(value);
19444     },
19445
19446     // private
19447     onHide : function(){
19448         Roo.tree.TreeEditor.superclass.onHide.call(this);
19449         if(this.editNode){
19450             this.editNode.ui.focus();
19451         }
19452     },
19453
19454     // private
19455     onSpecialKey : function(field, e){
19456         var k = e.getKey();
19457         if(k == e.ESC){
19458             e.stopEvent();
19459             this.cancelEdit();
19460         }else if(k == e.ENTER && !e.hasModifier()){
19461             e.stopEvent();
19462             this.completeEdit();
19463         }
19464     }
19465 });//<Script type="text/javascript">
19466 /*
19467  * Based on:
19468  * Ext JS Library 1.1.1
19469  * Copyright(c) 2006-2007, Ext JS, LLC.
19470  *
19471  * Originally Released Under LGPL - original licence link has changed is not relivant.
19472  *
19473  * Fork - LGPL
19474  * <script type="text/javascript">
19475  */
19476  
19477 /**
19478  * Not documented??? - probably should be...
19479  */
19480
19481 Roo.tree.ColumnNodeUI = Roo.extend(Roo.tree.TreeNodeUI, {
19482     //focus: Roo.emptyFn, // prevent odd scrolling behavior
19483     
19484     renderElements : function(n, a, targetNode, bulkRender){
19485         //consel.log("renderElements?");
19486         this.indentMarkup = n.parentNode ? n.parentNode.ui.getChildIndent() : '';
19487
19488         var t = n.getOwnerTree();
19489         var tid = Pman.Tab.Document_TypesTree.tree.el.id;
19490         
19491         var cols = t.columns;
19492         var bw = t.borderWidth;
19493         var c = cols[0];
19494         var href = a.href ? a.href : Roo.isGecko ? "" : "#";
19495          var cb = typeof a.checked == "boolean";
19496         var tx = String.format('{0}',n.text || (c.renderer ? c.renderer(a[c.dataIndex], n, a) : a[c.dataIndex]));
19497         var colcls = 'x-t-' + tid + '-c0';
19498         var buf = [
19499             '<li class="x-tree-node">',
19500             
19501                 
19502                 '<div class="x-tree-node-el ', a.cls,'">',
19503                     // extran...
19504                     '<div class="x-tree-col ', colcls, '" style="width:', c.width-bw, 'px;">',
19505                 
19506                 
19507                         '<span class="x-tree-node-indent">',this.indentMarkup,'</span>',
19508                         '<img src="', this.emptyIcon, '" class="x-tree-ec-icon  " />',
19509                         '<img src="', a.icon || this.emptyIcon, '" class="x-tree-node-icon',
19510                            (a.icon ? ' x-tree-node-inline-icon' : ''),
19511                            (a.iconCls ? ' '+a.iconCls : ''),
19512                            '" unselectable="on" />',
19513                         (cb ? ('<input class="x-tree-node-cb" type="checkbox" ' + 
19514                              (a.checked ? 'checked="checked" />' : ' />')) : ''),
19515                              
19516                         '<a class="x-tree-node-anchor" hidefocus="on" href="',href,'" tabIndex="1" ',
19517                             (a.hrefTarget ? ' target="' +a.hrefTarget + '"' : ''), '>',
19518                             '<span unselectable="on" qtip="' + tx + '">',
19519                              tx,
19520                              '</span></a>' ,
19521                     '</div>',
19522                      '<a class="x-tree-node-anchor" hidefocus="on" href="',href,'" tabIndex="1" ',
19523                             (a.hrefTarget ? ' target="' +a.hrefTarget + '"' : ''), '>'
19524                  ];
19525         for(var i = 1, len = cols.length; i < len; i++){
19526             c = cols[i];
19527             colcls = 'x-t-' + tid + '-c' +i;
19528             tx = String.format('{0}', (c.renderer ? c.renderer(a[c.dataIndex], n, a) : a[c.dataIndex]));
19529             buf.push('<div class="x-tree-col ', colcls, ' ' ,(c.cls?c.cls:''),'" style="width:',c.width-bw,'px;">',
19530                         '<div class="x-tree-col-text" qtip="' + tx +'">',tx,"</div>",
19531                       "</div>");
19532          }
19533          
19534          buf.push(
19535             '</a>',
19536             '<div class="x-clear"></div></div>',
19537             '<ul class="x-tree-node-ct" style="display:none;"></ul>',
19538             "</li>");
19539         
19540         if(bulkRender !== true && n.nextSibling && n.nextSibling.ui.getEl()){
19541             this.wrap = Roo.DomHelper.insertHtml("beforeBegin",
19542                                 n.nextSibling.ui.getEl(), buf.join(""));
19543         }else{
19544             this.wrap = Roo.DomHelper.insertHtml("beforeEnd", targetNode, buf.join(""));
19545         }
19546         var el = this.wrap.firstChild;
19547         this.elRow = el;
19548         this.elNode = el.firstChild;
19549         this.ranchor = el.childNodes[1];
19550         this.ctNode = this.wrap.childNodes[1];
19551         var cs = el.firstChild.childNodes;
19552         this.indentNode = cs[0];
19553         this.ecNode = cs[1];
19554         this.iconNode = cs[2];
19555         var index = 3;
19556         if(cb){
19557             this.checkbox = cs[3];
19558             index++;
19559         }
19560         this.anchor = cs[index];
19561         
19562         this.textNode = cs[index].firstChild;
19563         
19564         //el.on("click", this.onClick, this);
19565         //el.on("dblclick", this.onDblClick, this);
19566         
19567         
19568        // console.log(this);
19569     },
19570     initEvents : function(){
19571         Roo.tree.ColumnNodeUI.superclass.initEvents.call(this);
19572         
19573             
19574         var a = this.ranchor;
19575
19576         var el = Roo.get(a);
19577
19578         if(Roo.isOpera){ // opera render bug ignores the CSS
19579             el.setStyle("text-decoration", "none");
19580         }
19581
19582         el.on("click", this.onClick, this);
19583         el.on("dblclick", this.onDblClick, this);
19584         el.on("contextmenu", this.onContextMenu, this);
19585         
19586     },
19587     
19588     /*onSelectedChange : function(state){
19589         if(state){
19590             this.focus();
19591             this.addClass("x-tree-selected");
19592         }else{
19593             //this.blur();
19594             this.removeClass("x-tree-selected");
19595         }
19596     },*/
19597     addClass : function(cls){
19598         if(this.elRow){
19599             Roo.fly(this.elRow).addClass(cls);
19600         }
19601         
19602     },
19603     
19604     
19605     removeClass : function(cls){
19606         if(this.elRow){
19607             Roo.fly(this.elRow).removeClass(cls);
19608         }
19609     }
19610
19611     
19612     
19613 });//<Script type="text/javascript">
19614
19615 /*
19616  * Based on:
19617  * Ext JS Library 1.1.1
19618  * Copyright(c) 2006-2007, Ext JS, LLC.
19619  *
19620  * Originally Released Under LGPL - original licence link has changed is not relivant.
19621  *
19622  * Fork - LGPL
19623  * <script type="text/javascript">
19624  */
19625  
19626
19627 /**
19628  * @class Roo.tree.ColumnTree
19629  * @extends Roo.data.TreePanel
19630  * @cfg {Object} columns  Including width, header, renderer, cls, dataIndex 
19631  * @cfg {int} borderWidth  compined right/left border allowance
19632  * @constructor
19633  * @param {String/HTMLElement/Element} el The container element
19634  * @param {Object} config
19635  */
19636 Roo.tree.ColumnTree =  function(el, config)
19637 {
19638    Roo.tree.ColumnTree.superclass.constructor.call(this, el , config);
19639    this.addEvents({
19640         /**
19641         * @event resize
19642         * Fire this event on a container when it resizes
19643         * @param {int} w Width
19644         * @param {int} h Height
19645         */
19646        "resize" : true
19647     });
19648     this.on('resize', this.onResize, this);
19649 };
19650
19651 Roo.extend(Roo.tree.ColumnTree, Roo.tree.TreePanel, {
19652     //lines:false,
19653     
19654     
19655     borderWidth: Roo.isBorderBox ? 0 : 2, 
19656     headEls : false,
19657     
19658     render : function(){
19659         // add the header.....
19660        
19661         Roo.tree.ColumnTree.superclass.render.apply(this);
19662         
19663         this.el.addClass('x-column-tree');
19664         
19665         this.headers = this.el.createChild(
19666             {cls:'x-tree-headers'},this.innerCt.dom);
19667    
19668         var cols = this.columns, c;
19669         var totalWidth = 0;
19670         this.headEls = [];
19671         var  len = cols.length;
19672         for(var i = 0; i < len; i++){
19673              c = cols[i];
19674              totalWidth += c.width;
19675             this.headEls.push(this.headers.createChild({
19676                  cls:'x-tree-hd ' + (c.cls?c.cls+'-hd':''),
19677                  cn: {
19678                      cls:'x-tree-hd-text',
19679                      html: c.header
19680                  },
19681                  style:'width:'+(c.width-this.borderWidth)+'px;'
19682              }));
19683         }
19684         this.headers.createChild({cls:'x-clear'});
19685         // prevent floats from wrapping when clipped
19686         this.headers.setWidth(totalWidth);
19687         //this.innerCt.setWidth(totalWidth);
19688         this.innerCt.setStyle({ overflow: 'auto' });
19689         this.onResize(this.width, this.height);
19690              
19691         
19692     },
19693     onResize : function(w,h)
19694     {
19695         this.height = h;
19696         this.width = w;
19697         // resize cols..
19698         this.innerCt.setWidth(this.width);
19699         this.innerCt.setHeight(this.height-20);
19700         
19701         // headers...
19702         var cols = this.columns, c;
19703         var totalWidth = 0;
19704         var expEl = false;
19705         var len = cols.length;
19706         for(var i = 0; i < len; i++){
19707             c = cols[i];
19708             if (this.autoExpandColumn !== false && c.dataIndex == this.autoExpandColumn) {
19709                 // it's the expander..
19710                 expEl  = this.headEls[i];
19711                 continue;
19712             }
19713             totalWidth += c.width;
19714             
19715         }
19716         if (expEl) {
19717             expEl.setWidth(  ((w - totalWidth)-this.borderWidth - 20));
19718         }
19719         this.headers.setWidth(w-20);
19720
19721         
19722         
19723         
19724     }
19725 });
19726 /*
19727  * Based on:
19728  * Ext JS Library 1.1.1
19729  * Copyright(c) 2006-2007, Ext JS, LLC.
19730  *
19731  * Originally Released Under LGPL - original licence link has changed is not relivant.
19732  *
19733  * Fork - LGPL
19734  * <script type="text/javascript">
19735  */
19736  
19737 /**
19738  * @class Roo.menu.Menu
19739  * @extends Roo.util.Observable
19740  * A menu object.  This is the container to which you add all other menu items.  Menu can also serve a as a base class
19741  * when you want a specialzed menu based off of another component (like {@link Roo.menu.DateMenu} for example).
19742  * @constructor
19743  * Creates a new Menu
19744  * @param {Object} config Configuration options
19745  */
19746 Roo.menu.Menu = function(config){
19747     Roo.apply(this, config);
19748     this.id = this.id || Roo.id();
19749     this.addEvents({
19750         /**
19751          * @event beforeshow
19752          * Fires before this menu is displayed
19753          * @param {Roo.menu.Menu} this
19754          */
19755         beforeshow : true,
19756         /**
19757          * @event beforehide
19758          * Fires before this menu is hidden
19759          * @param {Roo.menu.Menu} this
19760          */
19761         beforehide : true,
19762         /**
19763          * @event show
19764          * Fires after this menu is displayed
19765          * @param {Roo.menu.Menu} this
19766          */
19767         show : true,
19768         /**
19769          * @event hide
19770          * Fires after this menu is hidden
19771          * @param {Roo.menu.Menu} this
19772          */
19773         hide : true,
19774         /**
19775          * @event click
19776          * Fires when this menu is clicked (or when the enter key is pressed while it is active)
19777          * @param {Roo.menu.Menu} this
19778          * @param {Roo.menu.Item} menuItem The menu item that was clicked
19779          * @param {Roo.EventObject} e
19780          */
19781         click : true,
19782         /**
19783          * @event mouseover
19784          * Fires when the mouse is hovering over this menu
19785          * @param {Roo.menu.Menu} this
19786          * @param {Roo.EventObject} e
19787          * @param {Roo.menu.Item} menuItem The menu item that was clicked
19788          */
19789         mouseover : true,
19790         /**
19791          * @event mouseout
19792          * Fires when the mouse exits this menu
19793          * @param {Roo.menu.Menu} this
19794          * @param {Roo.EventObject} e
19795          * @param {Roo.menu.Item} menuItem The menu item that was clicked
19796          */
19797         mouseout : true,
19798         /**
19799          * @event itemclick
19800          * Fires when a menu item contained in this menu is clicked
19801          * @param {Roo.menu.BaseItem} baseItem The BaseItem that was clicked
19802          * @param {Roo.EventObject} e
19803          */
19804         itemclick: true
19805     });
19806     if (this.registerMenu) {
19807         Roo.menu.MenuMgr.register(this);
19808     }
19809     
19810     var mis = this.items;
19811     this.items = new Roo.util.MixedCollection();
19812     if(mis){
19813         this.add.apply(this, mis);
19814     }
19815 };
19816
19817 Roo.extend(Roo.menu.Menu, Roo.util.Observable, {
19818     /**
19819      * @cfg {Number} minWidth The minimum width of the menu in pixels (defaults to 120)
19820      */
19821     minWidth : 120,
19822     /**
19823      * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop"
19824      * for bottom-right shadow (defaults to "sides")
19825      */
19826     shadow : "sides",
19827     /**
19828      * @cfg {String} subMenuAlign The {@link Roo.Element#alignTo} anchor position value to use for submenus of
19829      * this menu (defaults to "tl-tr?")
19830      */
19831     subMenuAlign : "tl-tr?",
19832     /**
19833      * @cfg {String} defaultAlign The default {@link Roo.Element#alignTo) anchor position value for this menu
19834      * relative to its element of origin (defaults to "tl-bl?")
19835      */
19836     defaultAlign : "tl-bl?",
19837     /**
19838      * @cfg {Boolean} allowOtherMenus True to allow multiple menus to be displayed at the same time (defaults to false)
19839      */
19840     allowOtherMenus : false,
19841     /**
19842      * @cfg {Boolean} registerMenu True (default) - means that clicking on screen etc. hides it.
19843      */
19844     registerMenu : true,
19845
19846     hidden:true,
19847
19848     // private
19849     render : function(){
19850         if(this.el){
19851             return;
19852         }
19853         var el = this.el = new Roo.Layer({
19854             cls: "x-menu",
19855             shadow:this.shadow,
19856             constrain: false,
19857             parentEl: this.parentEl || document.body,
19858             zindex:15000
19859         });
19860
19861         this.keyNav = new Roo.menu.MenuNav(this);
19862
19863         if(this.plain){
19864             el.addClass("x-menu-plain");
19865         }
19866         if(this.cls){
19867             el.addClass(this.cls);
19868         }
19869         // generic focus element
19870         this.focusEl = el.createChild({
19871             tag: "a", cls: "x-menu-focus", href: "#", onclick: "return false;", tabIndex:"-1"
19872         });
19873         var ul = el.createChild({tag: "ul", cls: "x-menu-list"});
19874         ul.on("click", this.onClick, this);
19875         ul.on("mouseover", this.onMouseOver, this);
19876         ul.on("mouseout", this.onMouseOut, this);
19877         this.items.each(function(item){
19878             if (item.hidden) {
19879                 return;
19880             }
19881             
19882             var li = document.createElement("li");
19883             li.className = "x-menu-list-item";
19884             ul.dom.appendChild(li);
19885             item.render(li, this);
19886         }, this);
19887         this.ul = ul;
19888         this.autoWidth();
19889     },
19890
19891     // private
19892     autoWidth : function(){
19893         var el = this.el, ul = this.ul;
19894         if(!el){
19895             return;
19896         }
19897         var w = this.width;
19898         if(w){
19899             el.setWidth(w);
19900         }else if(Roo.isIE){
19901             el.setWidth(this.minWidth);
19902             var t = el.dom.offsetWidth; // force recalc
19903             el.setWidth(ul.getWidth()+el.getFrameWidth("lr"));
19904         }
19905     },
19906
19907     // private
19908     delayAutoWidth : function(){
19909         if(this.rendered){
19910             if(!this.awTask){
19911                 this.awTask = new Roo.util.DelayedTask(this.autoWidth, this);
19912             }
19913             this.awTask.delay(20);
19914         }
19915     },
19916
19917     // private
19918     findTargetItem : function(e){
19919         var t = e.getTarget(".x-menu-list-item", this.ul,  true);
19920         if(t && t.menuItemId){
19921             return this.items.get(t.menuItemId);
19922         }
19923     },
19924
19925     // private
19926     onClick : function(e){
19927         var t;
19928         if(t = this.findTargetItem(e)){
19929             t.onClick(e);
19930             this.fireEvent("click", this, t, e);
19931         }
19932     },
19933
19934     // private
19935     setActiveItem : function(item, autoExpand){
19936         if(item != this.activeItem){
19937             if(this.activeItem){
19938                 this.activeItem.deactivate();
19939             }
19940             this.activeItem = item;
19941             item.activate(autoExpand);
19942         }else if(autoExpand){
19943             item.expandMenu();
19944         }
19945     },
19946
19947     // private
19948     tryActivate : function(start, step){
19949         var items = this.items;
19950         for(var i = start, len = items.length; i >= 0 && i < len; i+= step){
19951             var item = items.get(i);
19952             if(!item.disabled && item.canActivate){
19953                 this.setActiveItem(item, false);
19954                 return item;
19955             }
19956         }
19957         return false;
19958     },
19959
19960     // private
19961     onMouseOver : function(e){
19962         var t;
19963         if(t = this.findTargetItem(e)){
19964             if(t.canActivate && !t.disabled){
19965                 this.setActiveItem(t, true);
19966             }
19967         }
19968         this.fireEvent("mouseover", this, e, t);
19969     },
19970
19971     // private
19972     onMouseOut : function(e){
19973         var t;
19974         if(t = this.findTargetItem(e)){
19975             if(t == this.activeItem && t.shouldDeactivate(e)){
19976                 this.activeItem.deactivate();
19977                 delete this.activeItem;
19978             }
19979         }
19980         this.fireEvent("mouseout", this, e, t);
19981     },
19982
19983     /**
19984      * Read-only.  Returns true if the menu is currently displayed, else false.
19985      * @type Boolean
19986      */
19987     isVisible : function(){
19988         return this.el && !this.hidden;
19989     },
19990
19991     /**
19992      * Displays this menu relative to another element
19993      * @param {String/HTMLElement/Roo.Element} element The element to align to
19994      * @param {String} position (optional) The {@link Roo.Element#alignTo} anchor position to use in aligning to
19995      * the element (defaults to this.defaultAlign)
19996      * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
19997      */
19998     show : function(el, pos, parentMenu){
19999         this.parentMenu = parentMenu;
20000         if(!this.el){
20001             this.render();
20002         }
20003         this.fireEvent("beforeshow", this);
20004         this.showAt(this.el.getAlignToXY(el, pos || this.defaultAlign), parentMenu, false);
20005     },
20006
20007     /**
20008      * Displays this menu at a specific xy position
20009      * @param {Array} xyPosition Contains X & Y [x, y] values for the position at which to show the menu (coordinates are page-based)
20010      * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
20011      */
20012     showAt : function(xy, parentMenu, /* private: */_e){
20013         this.parentMenu = parentMenu;
20014         if(!this.el){
20015             this.render();
20016         }
20017         if(_e !== false){
20018             this.fireEvent("beforeshow", this);
20019             xy = this.el.adjustForConstraints(xy);
20020         }
20021         this.el.setXY(xy);
20022         this.el.show();
20023         this.hidden = false;
20024         this.focus();
20025         this.fireEvent("show", this);
20026     },
20027
20028     focus : function(){
20029         if(!this.hidden){
20030             this.doFocus.defer(50, this);
20031         }
20032     },
20033
20034     doFocus : function(){
20035         if(!this.hidden){
20036             this.focusEl.focus();
20037         }
20038     },
20039
20040     /**
20041      * Hides this menu and optionally all parent menus
20042      * @param {Boolean} deep (optional) True to hide all parent menus recursively, if any (defaults to false)
20043      */
20044     hide : function(deep){
20045         if(this.el && this.isVisible()){
20046             this.fireEvent("beforehide", this);
20047             if(this.activeItem){
20048                 this.activeItem.deactivate();
20049                 this.activeItem = null;
20050             }
20051             this.el.hide();
20052             this.hidden = true;
20053             this.fireEvent("hide", this);
20054         }
20055         if(deep === true && this.parentMenu){
20056             this.parentMenu.hide(true);
20057         }
20058     },
20059
20060     /**
20061      * Addds one or more items of any type supported by the Menu class, or that can be converted into menu items.
20062      * Any of the following are valid:
20063      * <ul>
20064      * <li>Any menu item object based on {@link Roo.menu.Item}</li>
20065      * <li>An HTMLElement object which will be converted to a menu item</li>
20066      * <li>A menu item config object that will be created as a new menu item</li>
20067      * <li>A string, which can either be '-' or 'separator' to add a menu separator, otherwise
20068      * it will be converted into a {@link Roo.menu.TextItem} and added</li>
20069      * </ul>
20070      * Usage:
20071      * <pre><code>
20072 // Create the menu
20073 var menu = new Roo.menu.Menu();
20074
20075 // Create a menu item to add by reference
20076 var menuItem = new Roo.menu.Item({ text: 'New Item!' });
20077
20078 // Add a bunch of items at once using different methods.
20079 // Only the last item added will be returned.
20080 var item = menu.add(
20081     menuItem,                // add existing item by ref
20082     'Dynamic Item',          // new TextItem
20083     '-',                     // new separator
20084     { text: 'Config Item' }  // new item by config
20085 );
20086 </code></pre>
20087      * @param {Mixed} args One or more menu items, menu item configs or other objects that can be converted to menu items
20088      * @return {Roo.menu.Item} The menu item that was added, or the last one if multiple items were added
20089      */
20090     add : function(){
20091         var a = arguments, l = a.length, item;
20092         for(var i = 0; i < l; i++){
20093             var el = a[i];
20094             if ((typeof(el) == "object") && el.xtype && el.xns) {
20095                 el = Roo.factory(el, Roo.menu);
20096             }
20097             
20098             if(el.render){ // some kind of Item
20099                 item = this.addItem(el);
20100             }else if(typeof el == "string"){ // string
20101                 if(el == "separator" || el == "-"){
20102                     item = this.addSeparator();
20103                 }else{
20104                     item = this.addText(el);
20105                 }
20106             }else if(el.tagName || el.el){ // element
20107                 item = this.addElement(el);
20108             }else if(typeof el == "object"){ // must be menu item config?
20109                 item = this.addMenuItem(el);
20110             }
20111         }
20112         return item;
20113     },
20114
20115     /**
20116      * Returns this menu's underlying {@link Roo.Element} object
20117      * @return {Roo.Element} The element
20118      */
20119     getEl : function(){
20120         if(!this.el){
20121             this.render();
20122         }
20123         return this.el;
20124     },
20125
20126     /**
20127      * Adds a separator bar to the menu
20128      * @return {Roo.menu.Item} The menu item that was added
20129      */
20130     addSeparator : function(){
20131         return this.addItem(new Roo.menu.Separator());
20132     },
20133
20134     /**
20135      * Adds an {@link Roo.Element} object to the menu
20136      * @param {String/HTMLElement/Roo.Element} el The element or DOM node to add, or its id
20137      * @return {Roo.menu.Item} The menu item that was added
20138      */
20139     addElement : function(el){
20140         return this.addItem(new Roo.menu.BaseItem(el));
20141     },
20142
20143     /**
20144      * Adds an existing object based on {@link Roo.menu.Item} to the menu
20145      * @param {Roo.menu.Item} item The menu item to add
20146      * @return {Roo.menu.Item} The menu item that was added
20147      */
20148     addItem : function(item){
20149         this.items.add(item);
20150         if(this.ul){
20151             var li = document.createElement("li");
20152             li.className = "x-menu-list-item";
20153             this.ul.dom.appendChild(li);
20154             item.render(li, this);
20155             this.delayAutoWidth();
20156         }
20157         return item;
20158     },
20159
20160     /**
20161      * Creates a new {@link Roo.menu.Item} based an the supplied config object and adds it to the menu
20162      * @param {Object} config A MenuItem config object
20163      * @return {Roo.menu.Item} The menu item that was added
20164      */
20165     addMenuItem : function(config){
20166         if(!(config instanceof Roo.menu.Item)){
20167             if(typeof config.checked == "boolean"){ // must be check menu item config?
20168                 config = new Roo.menu.CheckItem(config);
20169             }else{
20170                 config = new Roo.menu.Item(config);
20171             }
20172         }
20173         return this.addItem(config);
20174     },
20175
20176     /**
20177      * Creates a new {@link Roo.menu.TextItem} with the supplied text and adds it to the menu
20178      * @param {String} text The text to display in the menu item
20179      * @return {Roo.menu.Item} The menu item that was added
20180      */
20181     addText : function(text){
20182         return this.addItem(new Roo.menu.TextItem({ text : text }));
20183     },
20184
20185     /**
20186      * Inserts an existing object based on {@link Roo.menu.Item} to the menu at a specified index
20187      * @param {Number} index The index in the menu's list of current items where the new item should be inserted
20188      * @param {Roo.menu.Item} item The menu item to add
20189      * @return {Roo.menu.Item} The menu item that was added
20190      */
20191     insert : function(index, item){
20192         this.items.insert(index, item);
20193         if(this.ul){
20194             var li = document.createElement("li");
20195             li.className = "x-menu-list-item";
20196             this.ul.dom.insertBefore(li, this.ul.dom.childNodes[index]);
20197             item.render(li, this);
20198             this.delayAutoWidth();
20199         }
20200         return item;
20201     },
20202
20203     /**
20204      * Removes an {@link Roo.menu.Item} from the menu and destroys the object
20205      * @param {Roo.menu.Item} item The menu item to remove
20206      */
20207     remove : function(item){
20208         this.items.removeKey(item.id);
20209         item.destroy();
20210     },
20211
20212     /**
20213      * Removes and destroys all items in the menu
20214      */
20215     removeAll : function(){
20216         var f;
20217         while(f = this.items.first()){
20218             this.remove(f);
20219         }
20220     }
20221 });
20222
20223 // MenuNav is a private utility class used internally by the Menu
20224 Roo.menu.MenuNav = function(menu){
20225     Roo.menu.MenuNav.superclass.constructor.call(this, menu.el);
20226     this.scope = this.menu = menu;
20227 };
20228
20229 Roo.extend(Roo.menu.MenuNav, Roo.KeyNav, {
20230     doRelay : function(e, h){
20231         var k = e.getKey();
20232         if(!this.menu.activeItem && e.isNavKeyPress() && k != e.SPACE && k != e.RETURN){
20233             this.menu.tryActivate(0, 1);
20234             return false;
20235         }
20236         return h.call(this.scope || this, e, this.menu);
20237     },
20238
20239     up : function(e, m){
20240         if(!m.tryActivate(m.items.indexOf(m.activeItem)-1, -1)){
20241             m.tryActivate(m.items.length-1, -1);
20242         }
20243     },
20244
20245     down : function(e, m){
20246         if(!m.tryActivate(m.items.indexOf(m.activeItem)+1, 1)){
20247             m.tryActivate(0, 1);
20248         }
20249     },
20250
20251     right : function(e, m){
20252         if(m.activeItem){
20253             m.activeItem.expandMenu(true);
20254         }
20255     },
20256
20257     left : function(e, m){
20258         m.hide();
20259         if(m.parentMenu && m.parentMenu.activeItem){
20260             m.parentMenu.activeItem.activate();
20261         }
20262     },
20263
20264     enter : function(e, m){
20265         if(m.activeItem){
20266             e.stopPropagation();
20267             m.activeItem.onClick(e);
20268             m.fireEvent("click", this, m.activeItem);
20269             return true;
20270         }
20271     }
20272 });/*
20273  * Based on:
20274  * Ext JS Library 1.1.1
20275  * Copyright(c) 2006-2007, Ext JS, LLC.
20276  *
20277  * Originally Released Under LGPL - original licence link has changed is not relivant.
20278  *
20279  * Fork - LGPL
20280  * <script type="text/javascript">
20281  */
20282  
20283 /**
20284  * @class Roo.menu.MenuMgr
20285  * Provides a common registry of all menu items on a page so that they can be easily accessed by id.
20286  * @singleton
20287  */
20288 Roo.menu.MenuMgr = function(){
20289    var menus, active, groups = {}, attached = false, lastShow = new Date();
20290
20291    // private - called when first menu is created
20292    function init(){
20293        menus = {};
20294        active = new Roo.util.MixedCollection();
20295        Roo.get(document).addKeyListener(27, function(){
20296            if(active.length > 0){
20297                hideAll();
20298            }
20299        });
20300    }
20301
20302    // private
20303    function hideAll(){
20304        if(active && active.length > 0){
20305            var c = active.clone();
20306            c.each(function(m){
20307                m.hide();
20308            });
20309        }
20310    }
20311
20312    // private
20313    function onHide(m){
20314        active.remove(m);
20315        if(active.length < 1){
20316            Roo.get(document).un("mousedown", onMouseDown);
20317            attached = false;
20318        }
20319    }
20320
20321    // private
20322    function onShow(m){
20323        var last = active.last();
20324        lastShow = new Date();
20325        active.add(m);
20326        if(!attached){
20327            Roo.get(document).on("mousedown", onMouseDown);
20328            attached = true;
20329        }
20330        if(m.parentMenu){
20331           m.getEl().setZIndex(parseInt(m.parentMenu.getEl().getStyle("z-index"), 10) + 3);
20332           m.parentMenu.activeChild = m;
20333        }else if(last && last.isVisible()){
20334           m.getEl().setZIndex(parseInt(last.getEl().getStyle("z-index"), 10) + 3);
20335        }
20336    }
20337
20338    // private
20339    function onBeforeHide(m){
20340        if(m.activeChild){
20341            m.activeChild.hide();
20342        }
20343        if(m.autoHideTimer){
20344            clearTimeout(m.autoHideTimer);
20345            delete m.autoHideTimer;
20346        }
20347    }
20348
20349    // private
20350    function onBeforeShow(m){
20351        var pm = m.parentMenu;
20352        if(!pm && !m.allowOtherMenus){
20353            hideAll();
20354        }else if(pm && pm.activeChild && active != m){
20355            pm.activeChild.hide();
20356        }
20357    }
20358
20359    // private
20360    function onMouseDown(e){
20361        if(lastShow.getElapsed() > 50 && active.length > 0 && !e.getTarget(".x-menu")){
20362            hideAll();
20363        }
20364    }
20365
20366    // private
20367    function onBeforeCheck(mi, state){
20368        if(state){
20369            var g = groups[mi.group];
20370            for(var i = 0, l = g.length; i < l; i++){
20371                if(g[i] != mi){
20372                    g[i].setChecked(false);
20373                }
20374            }
20375        }
20376    }
20377
20378    return {
20379
20380        /**
20381         * Hides all menus that are currently visible
20382         */
20383        hideAll : function(){
20384             hideAll();  
20385        },
20386
20387        // private
20388        register : function(menu){
20389            if(!menus){
20390                init();
20391            }
20392            menus[menu.id] = menu;
20393            menu.on("beforehide", onBeforeHide);
20394            menu.on("hide", onHide);
20395            menu.on("beforeshow", onBeforeShow);
20396            menu.on("show", onShow);
20397            var g = menu.group;
20398            if(g && menu.events["checkchange"]){
20399                if(!groups[g]){
20400                    groups[g] = [];
20401                }
20402                groups[g].push(menu);
20403                menu.on("checkchange", onCheck);
20404            }
20405        },
20406
20407         /**
20408          * Returns a {@link Roo.menu.Menu} object
20409          * @param {String/Object} menu The string menu id, an existing menu object reference, or a Menu config that will
20410          * be used to generate and return a new Menu instance.
20411          */
20412        get : function(menu){
20413            if(typeof menu == "string"){ // menu id
20414                return menus[menu];
20415            }else if(menu.events){  // menu instance
20416                return menu;
20417            }else if(typeof menu.length == 'number'){ // array of menu items?
20418                return new Roo.menu.Menu({items:menu});
20419            }else{ // otherwise, must be a config
20420                return new Roo.menu.Menu(menu);
20421            }
20422        },
20423
20424        // private
20425        unregister : function(menu){
20426            delete menus[menu.id];
20427            menu.un("beforehide", onBeforeHide);
20428            menu.un("hide", onHide);
20429            menu.un("beforeshow", onBeforeShow);
20430            menu.un("show", onShow);
20431            var g = menu.group;
20432            if(g && menu.events["checkchange"]){
20433                groups[g].remove(menu);
20434                menu.un("checkchange", onCheck);
20435            }
20436        },
20437
20438        // private
20439        registerCheckable : function(menuItem){
20440            var g = menuItem.group;
20441            if(g){
20442                if(!groups[g]){
20443                    groups[g] = [];
20444                }
20445                groups[g].push(menuItem);
20446                menuItem.on("beforecheckchange", onBeforeCheck);
20447            }
20448        },
20449
20450        // private
20451        unregisterCheckable : function(menuItem){
20452            var g = menuItem.group;
20453            if(g){
20454                groups[g].remove(menuItem);
20455                menuItem.un("beforecheckchange", onBeforeCheck);
20456            }
20457        }
20458    };
20459 }();/*
20460  * Based on:
20461  * Ext JS Library 1.1.1
20462  * Copyright(c) 2006-2007, Ext JS, LLC.
20463  *
20464  * Originally Released Under LGPL - original licence link has changed is not relivant.
20465  *
20466  * Fork - LGPL
20467  * <script type="text/javascript">
20468  */
20469  
20470
20471 /**
20472  * @class Roo.menu.BaseItem
20473  * @extends Roo.Component
20474  * The base class for all items that render into menus.  BaseItem provides default rendering, activated state
20475  * management and base configuration options shared by all menu components.
20476  * @constructor
20477  * Creates a new BaseItem
20478  * @param {Object} config Configuration options
20479  */
20480 Roo.menu.BaseItem = function(config){
20481     Roo.menu.BaseItem.superclass.constructor.call(this, config);
20482
20483     this.addEvents({
20484         /**
20485          * @event click
20486          * Fires when this item is clicked
20487          * @param {Roo.menu.BaseItem} this
20488          * @param {Roo.EventObject} e
20489          */
20490         click: true,
20491         /**
20492          * @event activate
20493          * Fires when this item is activated
20494          * @param {Roo.menu.BaseItem} this
20495          */
20496         activate : true,
20497         /**
20498          * @event deactivate
20499          * Fires when this item is deactivated
20500          * @param {Roo.menu.BaseItem} this
20501          */
20502         deactivate : true
20503     });
20504
20505     if(this.handler){
20506         this.on("click", this.handler, this.scope, true);
20507     }
20508 };
20509
20510 Roo.extend(Roo.menu.BaseItem, Roo.Component, {
20511     /**
20512      * @cfg {Function} handler
20513      * A function that will handle the click event of this menu item (defaults to undefined)
20514      */
20515     /**
20516      * @cfg {Boolean} canActivate True if this item can be visually activated (defaults to false)
20517      */
20518     canActivate : false,
20519     
20520      /**
20521      * @cfg {Boolean} hidden True to prevent creation of this menu item (defaults to false)
20522      */
20523     hidden: false,
20524     
20525     /**
20526      * @cfg {String} activeClass The CSS class to use when the item becomes activated (defaults to "x-menu-item-active")
20527      */
20528     activeClass : "x-menu-item-active",
20529     /**
20530      * @cfg {Boolean} hideOnClick True to hide the containing menu after this item is clicked (defaults to true)
20531      */
20532     hideOnClick : true,
20533     /**
20534      * @cfg {Number} hideDelay Length of time in milliseconds to wait before hiding after a click (defaults to 100)
20535      */
20536     hideDelay : 100,
20537
20538     // private
20539     ctype: "Roo.menu.BaseItem",
20540
20541     // private
20542     actionMode : "container",
20543
20544     // private
20545     render : function(container, parentMenu){
20546         this.parentMenu = parentMenu;
20547         Roo.menu.BaseItem.superclass.render.call(this, container);
20548         this.container.menuItemId = this.id;
20549     },
20550
20551     // private
20552     onRender : function(container, position){
20553         this.el = Roo.get(this.el);
20554         container.dom.appendChild(this.el.dom);
20555     },
20556
20557     // private
20558     onClick : function(e){
20559         if(!this.disabled && this.fireEvent("click", this, e) !== false
20560                 && this.parentMenu.fireEvent("itemclick", this, e) !== false){
20561             this.handleClick(e);
20562         }else{
20563             e.stopEvent();
20564         }
20565     },
20566
20567     // private
20568     activate : function(){
20569         if(this.disabled){
20570             return false;
20571         }
20572         var li = this.container;
20573         li.addClass(this.activeClass);
20574         this.region = li.getRegion().adjust(2, 2, -2, -2);
20575         this.fireEvent("activate", this);
20576         return true;
20577     },
20578
20579     // private
20580     deactivate : function(){
20581         this.container.removeClass(this.activeClass);
20582         this.fireEvent("deactivate", this);
20583     },
20584
20585     // private
20586     shouldDeactivate : function(e){
20587         return !this.region || !this.region.contains(e.getPoint());
20588     },
20589
20590     // private
20591     handleClick : function(e){
20592         if(this.hideOnClick){
20593             this.parentMenu.hide.defer(this.hideDelay, this.parentMenu, [true]);
20594         }
20595     },
20596
20597     // private
20598     expandMenu : function(autoActivate){
20599         // do nothing
20600     },
20601
20602     // private
20603     hideMenu : function(){
20604         // do nothing
20605     }
20606 });/*
20607  * Based on:
20608  * Ext JS Library 1.1.1
20609  * Copyright(c) 2006-2007, Ext JS, LLC.
20610  *
20611  * Originally Released Under LGPL - original licence link has changed is not relivant.
20612  *
20613  * Fork - LGPL
20614  * <script type="text/javascript">
20615  */
20616  
20617 /**
20618  * @class Roo.menu.Adapter
20619  * @extends Roo.menu.BaseItem
20620  * 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.
20621  * It provides basic rendering, activation management and enable/disable logic required to work in menus.
20622  * @constructor
20623  * Creates a new Adapter
20624  * @param {Object} config Configuration options
20625  */
20626 Roo.menu.Adapter = function(component, config){
20627     Roo.menu.Adapter.superclass.constructor.call(this, config);
20628     this.component = component;
20629 };
20630 Roo.extend(Roo.menu.Adapter, Roo.menu.BaseItem, {
20631     // private
20632     canActivate : true,
20633
20634     // private
20635     onRender : function(container, position){
20636         this.component.render(container);
20637         this.el = this.component.getEl();
20638     },
20639
20640     // private
20641     activate : function(){
20642         if(this.disabled){
20643             return false;
20644         }
20645         this.component.focus();
20646         this.fireEvent("activate", this);
20647         return true;
20648     },
20649
20650     // private
20651     deactivate : function(){
20652         this.fireEvent("deactivate", this);
20653     },
20654
20655     // private
20656     disable : function(){
20657         this.component.disable();
20658         Roo.menu.Adapter.superclass.disable.call(this);
20659     },
20660
20661     // private
20662     enable : function(){
20663         this.component.enable();
20664         Roo.menu.Adapter.superclass.enable.call(this);
20665     }
20666 });/*
20667  * Based on:
20668  * Ext JS Library 1.1.1
20669  * Copyright(c) 2006-2007, Ext JS, LLC.
20670  *
20671  * Originally Released Under LGPL - original licence link has changed is not relivant.
20672  *
20673  * Fork - LGPL
20674  * <script type="text/javascript">
20675  */
20676
20677 /**
20678  * @class Roo.menu.TextItem
20679  * @extends Roo.menu.BaseItem
20680  * Adds a static text string to a menu, usually used as either a heading or group separator.
20681  * Note: old style constructor with text is still supported.
20682  * 
20683  * @constructor
20684  * Creates a new TextItem
20685  * @param {Object} cfg Configuration
20686  */
20687 Roo.menu.TextItem = function(cfg){
20688     if (typeof(cfg) == 'string') {
20689         this.text = cfg;
20690     } else {
20691         Roo.apply(this,cfg);
20692     }
20693     
20694     Roo.menu.TextItem.superclass.constructor.call(this);
20695 };
20696
20697 Roo.extend(Roo.menu.TextItem, Roo.menu.BaseItem, {
20698     /**
20699      * @cfg {Boolean} text Text to show on item.
20700      */
20701     text : '',
20702     
20703     /**
20704      * @cfg {Boolean} hideOnClick True to hide the containing menu after this item is clicked (defaults to false)
20705      */
20706     hideOnClick : false,
20707     /**
20708      * @cfg {String} itemCls The default CSS class to use for text items (defaults to "x-menu-text")
20709      */
20710     itemCls : "x-menu-text",
20711
20712     // private
20713     onRender : function(){
20714         var s = document.createElement("span");
20715         s.className = this.itemCls;
20716         s.innerHTML = this.text;
20717         this.el = s;
20718         Roo.menu.TextItem.superclass.onRender.apply(this, arguments);
20719     }
20720 });/*
20721  * Based on:
20722  * Ext JS Library 1.1.1
20723  * Copyright(c) 2006-2007, Ext JS, LLC.
20724  *
20725  * Originally Released Under LGPL - original licence link has changed is not relivant.
20726  *
20727  * Fork - LGPL
20728  * <script type="text/javascript">
20729  */
20730
20731 /**
20732  * @class Roo.menu.Separator
20733  * @extends Roo.menu.BaseItem
20734  * Adds a separator bar to a menu, used to divide logical groups of menu items. Generally you will
20735  * add one of these by using "-" in you call to add() or in your items config rather than creating one directly.
20736  * @constructor
20737  * @param {Object} config Configuration options
20738  */
20739 Roo.menu.Separator = function(config){
20740     Roo.menu.Separator.superclass.constructor.call(this, config);
20741 };
20742
20743 Roo.extend(Roo.menu.Separator, Roo.menu.BaseItem, {
20744     /**
20745      * @cfg {String} itemCls The default CSS class to use for separators (defaults to "x-menu-sep")
20746      */
20747     itemCls : "x-menu-sep",
20748     /**
20749      * @cfg {Boolean} hideOnClick True to hide the containing menu after this item is clicked (defaults to false)
20750      */
20751     hideOnClick : false,
20752
20753     // private
20754     onRender : function(li){
20755         var s = document.createElement("span");
20756         s.className = this.itemCls;
20757         s.innerHTML = "&#160;";
20758         this.el = s;
20759         li.addClass("x-menu-sep-li");
20760         Roo.menu.Separator.superclass.onRender.apply(this, arguments);
20761     }
20762 });/*
20763  * Based on:
20764  * Ext JS Library 1.1.1
20765  * Copyright(c) 2006-2007, Ext JS, LLC.
20766  *
20767  * Originally Released Under LGPL - original licence link has changed is not relivant.
20768  *
20769  * Fork - LGPL
20770  * <script type="text/javascript">
20771  */
20772 /**
20773  * @class Roo.menu.Item
20774  * @extends Roo.menu.BaseItem
20775  * A base class for all menu items that require menu-related functionality (like sub-menus) and are not static
20776  * display items.  Item extends the base functionality of {@link Roo.menu.BaseItem} by adding menu-specific
20777  * activation and click handling.
20778  * @constructor
20779  * Creates a new Item
20780  * @param {Object} config Configuration options
20781  */
20782 Roo.menu.Item = function(config){
20783     Roo.menu.Item.superclass.constructor.call(this, config);
20784     if(this.menu){
20785         this.menu = Roo.menu.MenuMgr.get(this.menu);
20786     }
20787 };
20788 Roo.extend(Roo.menu.Item, Roo.menu.BaseItem, {
20789     
20790     /**
20791      * @cfg {String} text
20792      * The text to show on the menu item.
20793      */
20794     text: '',
20795      /**
20796      * @cfg {String} HTML to render in menu
20797      * The text to show on the menu item (HTML version).
20798      */
20799     html: '',
20800     /**
20801      * @cfg {String} icon
20802      * The path to an icon to display in this menu item (defaults to Roo.BLANK_IMAGE_URL)
20803      */
20804     icon: undefined,
20805     /**
20806      * @cfg {String} itemCls The default CSS class to use for menu items (defaults to "x-menu-item")
20807      */
20808     itemCls : "x-menu-item",
20809     /**
20810      * @cfg {Boolean} canActivate True if this item can be visually activated (defaults to true)
20811      */
20812     canActivate : true,
20813     /**
20814      * @cfg {Number} showDelay Length of time in milliseconds to wait before showing this item (defaults to 200)
20815      */
20816     showDelay: 200,
20817     // doc'd in BaseItem
20818     hideDelay: 200,
20819
20820     // private
20821     ctype: "Roo.menu.Item",
20822     
20823     // private
20824     onRender : function(container, position){
20825         var el = document.createElement("a");
20826         el.hideFocus = true;
20827         el.unselectable = "on";
20828         el.href = this.href || "#";
20829         if(this.hrefTarget){
20830             el.target = this.hrefTarget;
20831         }
20832         el.className = this.itemCls + (this.menu ?  " x-menu-item-arrow" : "") + (this.cls ?  " " + this.cls : "");
20833         
20834         var html = this.html.length ? this.html  : String.format('{0}',this.text);
20835         
20836         el.innerHTML = String.format(
20837                 '<img src="{0}" class="x-menu-item-icon {1}" />' + html,
20838                 this.icon || Roo.BLANK_IMAGE_URL, this.iconCls || '');
20839         this.el = el;
20840         Roo.menu.Item.superclass.onRender.call(this, container, position);
20841     },
20842
20843     /**
20844      * Sets the text to display in this menu item
20845      * @param {String} text The text to display
20846      * @param {Boolean} isHTML true to indicate text is pure html.
20847      */
20848     setText : function(text, isHTML){
20849         if (isHTML) {
20850             this.html = text;
20851         } else {
20852             this.text = text;
20853             this.html = '';
20854         }
20855         if(this.rendered){
20856             var html = this.html.length ? this.html  : String.format('{0}',this.text);
20857      
20858             this.el.update(String.format(
20859                 '<img src="{0}" class="x-menu-item-icon {2}">' + html,
20860                 this.icon || Roo.BLANK_IMAGE_URL, this.text, this.iconCls || ''));
20861             this.parentMenu.autoWidth();
20862         }
20863     },
20864
20865     // private
20866     handleClick : function(e){
20867         if(!this.href){ // if no link defined, stop the event automatically
20868             e.stopEvent();
20869         }
20870         Roo.menu.Item.superclass.handleClick.apply(this, arguments);
20871     },
20872
20873     // private
20874     activate : function(autoExpand){
20875         if(Roo.menu.Item.superclass.activate.apply(this, arguments)){
20876             this.focus();
20877             if(autoExpand){
20878                 this.expandMenu();
20879             }
20880         }
20881         return true;
20882     },
20883
20884     // private
20885     shouldDeactivate : function(e){
20886         if(Roo.menu.Item.superclass.shouldDeactivate.call(this, e)){
20887             if(this.menu && this.menu.isVisible()){
20888                 return !this.menu.getEl().getRegion().contains(e.getPoint());
20889             }
20890             return true;
20891         }
20892         return false;
20893     },
20894
20895     // private
20896     deactivate : function(){
20897         Roo.menu.Item.superclass.deactivate.apply(this, arguments);
20898         this.hideMenu();
20899     },
20900
20901     // private
20902     expandMenu : function(autoActivate){
20903         if(!this.disabled && this.menu){
20904             clearTimeout(this.hideTimer);
20905             delete this.hideTimer;
20906             if(!this.menu.isVisible() && !this.showTimer){
20907                 this.showTimer = this.deferExpand.defer(this.showDelay, this, [autoActivate]);
20908             }else if (this.menu.isVisible() && autoActivate){
20909                 this.menu.tryActivate(0, 1);
20910             }
20911         }
20912     },
20913
20914     // private
20915     deferExpand : function(autoActivate){
20916         delete this.showTimer;
20917         this.menu.show(this.container, this.parentMenu.subMenuAlign || "tl-tr?", this.parentMenu);
20918         if(autoActivate){
20919             this.menu.tryActivate(0, 1);
20920         }
20921     },
20922
20923     // private
20924     hideMenu : function(){
20925         clearTimeout(this.showTimer);
20926         delete this.showTimer;
20927         if(!this.hideTimer && this.menu && this.menu.isVisible()){
20928             this.hideTimer = this.deferHide.defer(this.hideDelay, this);
20929         }
20930     },
20931
20932     // private
20933     deferHide : function(){
20934         delete this.hideTimer;
20935         this.menu.hide();
20936     }
20937 });/*
20938  * Based on:
20939  * Ext JS Library 1.1.1
20940  * Copyright(c) 2006-2007, Ext JS, LLC.
20941  *
20942  * Originally Released Under LGPL - original licence link has changed is not relivant.
20943  *
20944  * Fork - LGPL
20945  * <script type="text/javascript">
20946  */
20947  
20948 /**
20949  * @class Roo.menu.CheckItem
20950  * @extends Roo.menu.Item
20951  * Adds a menu item that contains a checkbox by default, but can also be part of a radio group.
20952  * @constructor
20953  * Creates a new CheckItem
20954  * @param {Object} config Configuration options
20955  */
20956 Roo.menu.CheckItem = function(config){
20957     Roo.menu.CheckItem.superclass.constructor.call(this, config);
20958     this.addEvents({
20959         /**
20960          * @event beforecheckchange
20961          * Fires before the checked value is set, providing an opportunity to cancel if needed
20962          * @param {Roo.menu.CheckItem} this
20963          * @param {Boolean} checked The new checked value that will be set
20964          */
20965         "beforecheckchange" : true,
20966         /**
20967          * @event checkchange
20968          * Fires after the checked value has been set
20969          * @param {Roo.menu.CheckItem} this
20970          * @param {Boolean} checked The checked value that was set
20971          */
20972         "checkchange" : true
20973     });
20974     if(this.checkHandler){
20975         this.on('checkchange', this.checkHandler, this.scope);
20976     }
20977 };
20978 Roo.extend(Roo.menu.CheckItem, Roo.menu.Item, {
20979     /**
20980      * @cfg {String} group
20981      * All check items with the same group name will automatically be grouped into a single-select
20982      * radio button group (defaults to '')
20983      */
20984     /**
20985      * @cfg {String} itemCls The default CSS class to use for check items (defaults to "x-menu-item x-menu-check-item")
20986      */
20987     itemCls : "x-menu-item x-menu-check-item",
20988     /**
20989      * @cfg {String} groupClass The default CSS class to use for radio group check items (defaults to "x-menu-group-item")
20990      */
20991     groupClass : "x-menu-group-item",
20992
20993     /**
20994      * @cfg {Boolean} checked True to initialize this checkbox as checked (defaults to false).  Note that
20995      * if this checkbox is part of a radio group (group = true) only the last item in the group that is
20996      * initialized with checked = true will be rendered as checked.
20997      */
20998     checked: false,
20999
21000     // private
21001     ctype: "Roo.menu.CheckItem",
21002
21003     // private
21004     onRender : function(c){
21005         Roo.menu.CheckItem.superclass.onRender.apply(this, arguments);
21006         if(this.group){
21007             this.el.addClass(this.groupClass);
21008         }
21009         Roo.menu.MenuMgr.registerCheckable(this);
21010         if(this.checked){
21011             this.checked = false;
21012             this.setChecked(true, true);
21013         }
21014     },
21015
21016     // private
21017     destroy : function(){
21018         if(this.rendered){
21019             Roo.menu.MenuMgr.unregisterCheckable(this);
21020         }
21021         Roo.menu.CheckItem.superclass.destroy.apply(this, arguments);
21022     },
21023
21024     /**
21025      * Set the checked state of this item
21026      * @param {Boolean} checked The new checked value
21027      * @param {Boolean} suppressEvent (optional) True to prevent the checkchange event from firing (defaults to false)
21028      */
21029     setChecked : function(state, suppressEvent){
21030         if(this.checked != state && this.fireEvent("beforecheckchange", this, state) !== false){
21031             if(this.container){
21032                 this.container[state ? "addClass" : "removeClass"]("x-menu-item-checked");
21033             }
21034             this.checked = state;
21035             if(suppressEvent !== true){
21036                 this.fireEvent("checkchange", this, state);
21037             }
21038         }
21039     },
21040
21041     // private
21042     handleClick : function(e){
21043        if(!this.disabled && !(this.checked && this.group)){// disable unselect on radio item
21044            this.setChecked(!this.checked);
21045        }
21046        Roo.menu.CheckItem.superclass.handleClick.apply(this, arguments);
21047     }
21048 });/*
21049  * Based on:
21050  * Ext JS Library 1.1.1
21051  * Copyright(c) 2006-2007, Ext JS, LLC.
21052  *
21053  * Originally Released Under LGPL - original licence link has changed is not relivant.
21054  *
21055  * Fork - LGPL
21056  * <script type="text/javascript">
21057  */
21058  
21059 /**
21060  * @class Roo.menu.DateItem
21061  * @extends Roo.menu.Adapter
21062  * A menu item that wraps the {@link Roo.DatPicker} component.
21063  * @constructor
21064  * Creates a new DateItem
21065  * @param {Object} config Configuration options
21066  */
21067 Roo.menu.DateItem = function(config){
21068     Roo.menu.DateItem.superclass.constructor.call(this, new Roo.DatePicker(config), config);
21069     /** The Roo.DatePicker object @type Roo.DatePicker */
21070     this.picker = this.component;
21071     this.addEvents({select: true});
21072     
21073     this.picker.on("render", function(picker){
21074         picker.getEl().swallowEvent("click");
21075         picker.container.addClass("x-menu-date-item");
21076     });
21077
21078     this.picker.on("select", this.onSelect, this);
21079 };
21080
21081 Roo.extend(Roo.menu.DateItem, Roo.menu.Adapter, {
21082     // private
21083     onSelect : function(picker, date){
21084         this.fireEvent("select", this, date, picker);
21085         Roo.menu.DateItem.superclass.handleClick.call(this);
21086     }
21087 });/*
21088  * Based on:
21089  * Ext JS Library 1.1.1
21090  * Copyright(c) 2006-2007, Ext JS, LLC.
21091  *
21092  * Originally Released Under LGPL - original licence link has changed is not relivant.
21093  *
21094  * Fork - LGPL
21095  * <script type="text/javascript">
21096  */
21097  
21098 /**
21099  * @class Roo.menu.ColorItem
21100  * @extends Roo.menu.Adapter
21101  * A menu item that wraps the {@link Roo.ColorPalette} component.
21102  * @constructor
21103  * Creates a new ColorItem
21104  * @param {Object} config Configuration options
21105  */
21106 Roo.menu.ColorItem = function(config){
21107     Roo.menu.ColorItem.superclass.constructor.call(this, new Roo.ColorPalette(config), config);
21108     /** The Roo.ColorPalette object @type Roo.ColorPalette */
21109     this.palette = this.component;
21110     this.relayEvents(this.palette, ["select"]);
21111     if(this.selectHandler){
21112         this.on('select', this.selectHandler, this.scope);
21113     }
21114 };
21115 Roo.extend(Roo.menu.ColorItem, Roo.menu.Adapter);/*
21116  * Based on:
21117  * Ext JS Library 1.1.1
21118  * Copyright(c) 2006-2007, Ext JS, LLC.
21119  *
21120  * Originally Released Under LGPL - original licence link has changed is not relivant.
21121  *
21122  * Fork - LGPL
21123  * <script type="text/javascript">
21124  */
21125  
21126
21127 /**
21128  * @class Roo.menu.DateMenu
21129  * @extends Roo.menu.Menu
21130  * A menu containing a {@link Roo.menu.DateItem} component (which provides a date picker).
21131  * @constructor
21132  * Creates a new DateMenu
21133  * @param {Object} config Configuration options
21134  */
21135 Roo.menu.DateMenu = function(config){
21136     Roo.menu.DateMenu.superclass.constructor.call(this, config);
21137     this.plain = true;
21138     var di = new Roo.menu.DateItem(config);
21139     this.add(di);
21140     /**
21141      * The {@link Roo.DatePicker} instance for this DateMenu
21142      * @type DatePicker
21143      */
21144     this.picker = di.picker;
21145     /**
21146      * @event select
21147      * @param {DatePicker} picker
21148      * @param {Date} date
21149      */
21150     this.relayEvents(di, ["select"]);
21151     this.on('beforeshow', function(){
21152         if(this.picker){
21153             this.picker.hideMonthPicker(false);
21154         }
21155     }, this);
21156 };
21157 Roo.extend(Roo.menu.DateMenu, Roo.menu.Menu, {
21158     cls:'x-date-menu'
21159 });/*
21160  * Based on:
21161  * Ext JS Library 1.1.1
21162  * Copyright(c) 2006-2007, Ext JS, LLC.
21163  *
21164  * Originally Released Under LGPL - original licence link has changed is not relivant.
21165  *
21166  * Fork - LGPL
21167  * <script type="text/javascript">
21168  */
21169  
21170
21171 /**
21172  * @class Roo.menu.ColorMenu
21173  * @extends Roo.menu.Menu
21174  * A menu containing a {@link Roo.menu.ColorItem} component (which provides a basic color picker).
21175  * @constructor
21176  * Creates a new ColorMenu
21177  * @param {Object} config Configuration options
21178  */
21179 Roo.menu.ColorMenu = function(config){
21180     Roo.menu.ColorMenu.superclass.constructor.call(this, config);
21181     this.plain = true;
21182     var ci = new Roo.menu.ColorItem(config);
21183     this.add(ci);
21184     /**
21185      * The {@link Roo.ColorPalette} instance for this ColorMenu
21186      * @type ColorPalette
21187      */
21188     this.palette = ci.palette;
21189     /**
21190      * @event select
21191      * @param {ColorPalette} palette
21192      * @param {String} color
21193      */
21194     this.relayEvents(ci, ["select"]);
21195 };
21196 Roo.extend(Roo.menu.ColorMenu, Roo.menu.Menu);/*
21197  * Based on:
21198  * Ext JS Library 1.1.1
21199  * Copyright(c) 2006-2007, Ext JS, LLC.
21200  *
21201  * Originally Released Under LGPL - original licence link has changed is not relivant.
21202  *
21203  * Fork - LGPL
21204  * <script type="text/javascript">
21205  */
21206  
21207 /**
21208  * @class Roo.form.Field
21209  * @extends Roo.BoxComponent
21210  * Base class for form fields that provides default event handling, sizing, value handling and other functionality.
21211  * @constructor
21212  * Creates a new Field
21213  * @param {Object} config Configuration options
21214  */
21215 Roo.form.Field = function(config){
21216     Roo.form.Field.superclass.constructor.call(this, config);
21217 };
21218
21219 Roo.extend(Roo.form.Field, Roo.BoxComponent,  {
21220     /**
21221      * @cfg {String} fieldLabel Label to use when rendering a form.
21222      */
21223        /**
21224      * @cfg {String} qtip Mouse over tip
21225      */
21226      
21227     /**
21228      * @cfg {String} invalidClass The CSS class to use when marking a field invalid (defaults to "x-form-invalid")
21229      */
21230     invalidClass : "x-form-invalid",
21231     /**
21232      * @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")
21233      */
21234     invalidText : "The value in this field is invalid",
21235     /**
21236      * @cfg {String} focusClass The CSS class to use when the field receives focus (defaults to "x-form-focus")
21237      */
21238     focusClass : "x-form-focus",
21239     /**
21240      * @cfg {String/Boolean} validationEvent The event that should initiate field validation. Set to false to disable
21241       automatic validation (defaults to "keyup").
21242      */
21243     validationEvent : "keyup",
21244     /**
21245      * @cfg {Boolean} validateOnBlur Whether the field should validate when it loses focus (defaults to true).
21246      */
21247     validateOnBlur : true,
21248     /**
21249      * @cfg {Number} validationDelay The length of time in milliseconds after user input begins until validation is initiated (defaults to 250)
21250      */
21251     validationDelay : 250,
21252     /**
21253      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
21254      * {tag: "input", type: "text", size: "20", autocomplete: "off"})
21255      */
21256     defaultAutoCreate : {tag: "input", type: "text", size: "20", autocomplete: "off"},
21257     /**
21258      * @cfg {String} fieldClass The default CSS class for the field (defaults to "x-form-field")
21259      */
21260     fieldClass : "x-form-field",
21261     /**
21262      * @cfg {String} msgTarget The location where error text should display.  Should be one of the following values (defaults to 'qtip'):
21263      *<pre>
21264 Value         Description
21265 -----------   ----------------------------------------------------------------------
21266 qtip          Display a quick tip when the user hovers over the field
21267 title         Display a default browser title attribute popup
21268 under         Add a block div beneath the field containing the error text
21269 side          Add an error icon to the right of the field with a popup on hover
21270 [element id]  Add the error text directly to the innerHTML of the specified element
21271 </pre>
21272      */
21273     msgTarget : 'qtip',
21274     /**
21275      * @cfg {String} msgFx <b>Experimental</b> The effect used when displaying a validation message under the field (defaults to 'normal').
21276      */
21277     msgFx : 'normal',
21278
21279     /**
21280      * @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.
21281      */
21282     readOnly : false,
21283
21284     /**
21285      * @cfg {Boolean} disabled True to disable the field (defaults to false).
21286      */
21287     disabled : false,
21288
21289     /**
21290      * @cfg {String} inputType The type attribute for input fields -- e.g. radio, text, password (defaults to "text").
21291      */
21292     inputType : undefined,
21293     
21294     /**
21295      * @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).
21296          */
21297         tabIndex : undefined,
21298         
21299     // private
21300     isFormField : true,
21301
21302     // private
21303     hasFocus : false,
21304     /**
21305      * @property {Roo.Element} fieldEl
21306      * Element Containing the rendered Field (with label etc.)
21307      */
21308     /**
21309      * @cfg {Mixed} value A value to initialize this field with.
21310      */
21311     value : undefined,
21312
21313     /**
21314      * @cfg {String} name The field's HTML name attribute.
21315      */
21316     /**
21317      * @cfg {String} cls A CSS class to apply to the field's underlying element.
21318      */
21319
21320         // private ??
21321         initComponent : function(){
21322         Roo.form.Field.superclass.initComponent.call(this);
21323         this.addEvents({
21324             /**
21325              * @event focus
21326              * Fires when this field receives input focus.
21327              * @param {Roo.form.Field} this
21328              */
21329             focus : true,
21330             /**
21331              * @event blur
21332              * Fires when this field loses input focus.
21333              * @param {Roo.form.Field} this
21334              */
21335             blur : true,
21336             /**
21337              * @event specialkey
21338              * Fires when any key related to navigation (arrows, tab, enter, esc, etc.) is pressed.  You can check
21339              * {@link Roo.EventObject#getKey} to determine which key was pressed.
21340              * @param {Roo.form.Field} this
21341              * @param {Roo.EventObject} e The event object
21342              */
21343             specialkey : true,
21344             /**
21345              * @event change
21346              * Fires just before the field blurs if the field value has changed.
21347              * @param {Roo.form.Field} this
21348              * @param {Mixed} newValue The new value
21349              * @param {Mixed} oldValue The original value
21350              */
21351             change : true,
21352             /**
21353              * @event invalid
21354              * Fires after the field has been marked as invalid.
21355              * @param {Roo.form.Field} this
21356              * @param {String} msg The validation message
21357              */
21358             invalid : true,
21359             /**
21360              * @event valid
21361              * Fires after the field has been validated with no errors.
21362              * @param {Roo.form.Field} this
21363              */
21364             valid : true,
21365              /**
21366              * @event keyup
21367              * Fires after the key up
21368              * @param {Roo.form.Field} this
21369              * @param {Roo.EventObject}  e The event Object
21370              */
21371             keyup : true
21372         });
21373     },
21374
21375     /**
21376      * Returns the name attribute of the field if available
21377      * @return {String} name The field name
21378      */
21379     getName: function(){
21380          return this.rendered && this.el.dom.name ? this.el.dom.name : (this.hiddenName || '');
21381     },
21382
21383     // private
21384     onRender : function(ct, position){
21385         Roo.form.Field.superclass.onRender.call(this, ct, position);
21386         if(!this.el){
21387             var cfg = this.getAutoCreate();
21388             if(!cfg.name){
21389                 cfg.name = typeof(this.name) == 'undefined' ? this.id : this.name;
21390             }
21391             if (!cfg.name.length) {
21392                 delete cfg.name;
21393             }
21394             if(this.inputType){
21395                 cfg.type = this.inputType;
21396             }
21397             this.el = ct.createChild(cfg, position);
21398         }
21399         var type = this.el.dom.type;
21400         if(type){
21401             if(type == 'password'){
21402                 type = 'text';
21403             }
21404             this.el.addClass('x-form-'+type);
21405         }
21406         if(this.readOnly){
21407             this.el.dom.readOnly = true;
21408         }
21409         if(this.tabIndex !== undefined){
21410             this.el.dom.setAttribute('tabIndex', this.tabIndex);
21411         }
21412
21413         this.el.addClass([this.fieldClass, this.cls]);
21414         this.initValue();
21415     },
21416
21417     /**
21418      * Apply the behaviors of this component to an existing element. <b>This is used instead of render().</b>
21419      * @param {String/HTMLElement/Element} el The id of the node, a DOM node or an existing Element
21420      * @return {Roo.form.Field} this
21421      */
21422     applyTo : function(target){
21423         this.allowDomMove = false;
21424         this.el = Roo.get(target);
21425         this.render(this.el.dom.parentNode);
21426         return this;
21427     },
21428
21429     // private
21430     initValue : function(){
21431         if(this.value !== undefined){
21432             this.setValue(this.value);
21433         }else if(this.el.dom.value.length > 0){
21434             this.setValue(this.el.dom.value);
21435         }
21436     },
21437
21438     /**
21439      * Returns true if this field has been changed since it was originally loaded and is not disabled.
21440      */
21441     isDirty : function() {
21442         if(this.disabled) {
21443             return false;
21444         }
21445         return String(this.getValue()) !== String(this.originalValue);
21446     },
21447
21448     // private
21449     afterRender : function(){
21450         Roo.form.Field.superclass.afterRender.call(this);
21451         this.initEvents();
21452     },
21453
21454     // private
21455     fireKey : function(e){
21456         //Roo.log('field ' + e.getKey());
21457         if(e.isNavKeyPress()){
21458             this.fireEvent("specialkey", this, e);
21459         }
21460     },
21461
21462     /**
21463      * Resets the current field value to the originally loaded value and clears any validation messages
21464      */
21465     reset : function(){
21466         this.setValue(this.originalValue);
21467         this.clearInvalid();
21468     },
21469
21470     // private
21471     initEvents : function(){
21472         // safari killled keypress - so keydown is now used..
21473         this.el.on("keydown" , this.fireKey,  this);
21474         this.el.on("focus", this.onFocus,  this);
21475         this.el.on("blur", this.onBlur,  this);
21476         this.el.relayEvent('keyup', this);
21477
21478         // reference to original value for reset
21479         this.originalValue = this.getValue();
21480     },
21481
21482     // private
21483     onFocus : function(){
21484         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
21485             this.el.addClass(this.focusClass);
21486         }
21487         if(!this.hasFocus){
21488             this.hasFocus = true;
21489             this.startValue = this.getValue();
21490             this.fireEvent("focus", this);
21491         }
21492     },
21493
21494     beforeBlur : Roo.emptyFn,
21495
21496     // private
21497     onBlur : function(){
21498         this.beforeBlur();
21499         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
21500             this.el.removeClass(this.focusClass);
21501         }
21502         this.hasFocus = false;
21503         if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
21504             this.validate();
21505         }
21506         var v = this.getValue();
21507         if(String(v) !== String(this.startValue)){
21508             this.fireEvent('change', this, v, this.startValue);
21509         }
21510         this.fireEvent("blur", this);
21511     },
21512
21513     /**
21514      * Returns whether or not the field value is currently valid
21515      * @param {Boolean} preventMark True to disable marking the field invalid
21516      * @return {Boolean} True if the value is valid, else false
21517      */
21518     isValid : function(preventMark){
21519         if(this.disabled){
21520             return true;
21521         }
21522         var restore = this.preventMark;
21523         this.preventMark = preventMark === true;
21524         var v = this.validateValue(this.processValue(this.getRawValue()));
21525         this.preventMark = restore;
21526         return v;
21527     },
21528
21529     /**
21530      * Validates the field value
21531      * @return {Boolean} True if the value is valid, else false
21532      */
21533     validate : function(){
21534         if(this.disabled || this.validateValue(this.processValue(this.getRawValue()))){
21535             this.clearInvalid();
21536             return true;
21537         }
21538         return false;
21539     },
21540
21541     processValue : function(value){
21542         return value;
21543     },
21544
21545     // private
21546     // Subclasses should provide the validation implementation by overriding this
21547     validateValue : function(value){
21548         return true;
21549     },
21550
21551     /**
21552      * Mark this field as invalid
21553      * @param {String} msg The validation message
21554      */
21555     markInvalid : function(msg){
21556         if(!this.rendered || this.preventMark){ // not rendered
21557             return;
21558         }
21559         this.el.addClass(this.invalidClass);
21560         msg = msg || this.invalidText;
21561         switch(this.msgTarget){
21562             case 'qtip':
21563                 this.el.dom.qtip = msg;
21564                 this.el.dom.qclass = 'x-form-invalid-tip';
21565                 if(Roo.QuickTips){ // fix for floating editors interacting with DND
21566                     Roo.QuickTips.enable();
21567                 }
21568                 break;
21569             case 'title':
21570                 this.el.dom.title = msg;
21571                 break;
21572             case 'under':
21573                 if(!this.errorEl){
21574                     var elp = this.el.findParent('.x-form-element', 5, true);
21575                     this.errorEl = elp.createChild({cls:'x-form-invalid-msg'});
21576                     this.errorEl.setWidth(elp.getWidth(true)-20);
21577                 }
21578                 this.errorEl.update(msg);
21579                 Roo.form.Field.msgFx[this.msgFx].show(this.errorEl, this);
21580                 break;
21581             case 'side':
21582                 if(!this.errorIcon){
21583                     var elp = this.el.findParent('.x-form-element', 5, true);
21584                     this.errorIcon = elp.createChild({cls:'x-form-invalid-icon'});
21585                 }
21586                 this.alignErrorIcon();
21587                 this.errorIcon.dom.qtip = msg;
21588                 this.errorIcon.dom.qclass = 'x-form-invalid-tip';
21589                 this.errorIcon.show();
21590                 this.on('resize', this.alignErrorIcon, this);
21591                 break;
21592             default:
21593                 var t = Roo.getDom(this.msgTarget);
21594                 t.innerHTML = msg;
21595                 t.style.display = this.msgDisplay;
21596                 break;
21597         }
21598         this.fireEvent('invalid', this, msg);
21599     },
21600
21601     // private
21602     alignErrorIcon : function(){
21603         this.errorIcon.alignTo(this.el, 'tl-tr', [2, 0]);
21604     },
21605
21606     /**
21607      * Clear any invalid styles/messages for this field
21608      */
21609     clearInvalid : function(){
21610         if(!this.rendered || this.preventMark){ // not rendered
21611             return;
21612         }
21613         this.el.removeClass(this.invalidClass);
21614         switch(this.msgTarget){
21615             case 'qtip':
21616                 this.el.dom.qtip = '';
21617                 break;
21618             case 'title':
21619                 this.el.dom.title = '';
21620                 break;
21621             case 'under':
21622                 if(this.errorEl){
21623                     Roo.form.Field.msgFx[this.msgFx].hide(this.errorEl, this);
21624                 }
21625                 break;
21626             case 'side':
21627                 if(this.errorIcon){
21628                     this.errorIcon.dom.qtip = '';
21629                     this.errorIcon.hide();
21630                     this.un('resize', this.alignErrorIcon, this);
21631                 }
21632                 break;
21633             default:
21634                 var t = Roo.getDom(this.msgTarget);
21635                 t.innerHTML = '';
21636                 t.style.display = 'none';
21637                 break;
21638         }
21639         this.fireEvent('valid', this);
21640     },
21641
21642     /**
21643      * Returns the raw data value which may or may not be a valid, defined value.  To return a normalized value see {@link #getValue}.
21644      * @return {Mixed} value The field value
21645      */
21646     getRawValue : function(){
21647         var v = this.el.getValue();
21648         
21649         return v;
21650     },
21651
21652     /**
21653      * Returns the normalized data value (undefined or emptyText will be returned as '').  To return the raw value see {@link #getRawValue}.
21654      * @return {Mixed} value The field value
21655      */
21656     getValue : function(){
21657         var v = this.el.getValue();
21658          
21659         return v;
21660     },
21661
21662     /**
21663      * Sets the underlying DOM field's value directly, bypassing validation.  To set the value with validation see {@link #setValue}.
21664      * @param {Mixed} value The value to set
21665      */
21666     setRawValue : function(v){
21667         return this.el.dom.value = (v === null || v === undefined ? '' : v);
21668     },
21669
21670     /**
21671      * Sets a data value into the field and validates it.  To set the value directly without validation see {@link #setRawValue}.
21672      * @param {Mixed} value The value to set
21673      */
21674     setValue : function(v){
21675         this.value = v;
21676         if(this.rendered){
21677             this.el.dom.value = (v === null || v === undefined ? '' : v);
21678              this.validate();
21679         }
21680     },
21681
21682     adjustSize : function(w, h){
21683         var s = Roo.form.Field.superclass.adjustSize.call(this, w, h);
21684         s.width = this.adjustWidth(this.el.dom.tagName, s.width);
21685         return s;
21686     },
21687
21688     adjustWidth : function(tag, w){
21689         tag = tag.toLowerCase();
21690         if(typeof w == 'number' && Roo.isStrict && !Roo.isSafari){
21691             if(Roo.isIE && (tag == 'input' || tag == 'textarea')){
21692                 if(tag == 'input'){
21693                     return w + 2;
21694                 }
21695                 if(tag = 'textarea'){
21696                     return w-2;
21697                 }
21698             }else if(Roo.isOpera){
21699                 if(tag == 'input'){
21700                     return w + 2;
21701                 }
21702                 if(tag = 'textarea'){
21703                     return w-2;
21704                 }
21705             }
21706         }
21707         return w;
21708     }
21709 });
21710
21711
21712 // anything other than normal should be considered experimental
21713 Roo.form.Field.msgFx = {
21714     normal : {
21715         show: function(msgEl, f){
21716             msgEl.setDisplayed('block');
21717         },
21718
21719         hide : function(msgEl, f){
21720             msgEl.setDisplayed(false).update('');
21721         }
21722     },
21723
21724     slide : {
21725         show: function(msgEl, f){
21726             msgEl.slideIn('t', {stopFx:true});
21727         },
21728
21729         hide : function(msgEl, f){
21730             msgEl.slideOut('t', {stopFx:true,useDisplay:true});
21731         }
21732     },
21733
21734     slideRight : {
21735         show: function(msgEl, f){
21736             msgEl.fixDisplay();
21737             msgEl.alignTo(f.el, 'tl-tr');
21738             msgEl.slideIn('l', {stopFx:true});
21739         },
21740
21741         hide : function(msgEl, f){
21742             msgEl.slideOut('l', {stopFx:true,useDisplay:true});
21743         }
21744     }
21745 };/*
21746  * Based on:
21747  * Ext JS Library 1.1.1
21748  * Copyright(c) 2006-2007, Ext JS, LLC.
21749  *
21750  * Originally Released Under LGPL - original licence link has changed is not relivant.
21751  *
21752  * Fork - LGPL
21753  * <script type="text/javascript">
21754  */
21755  
21756
21757 /**
21758  * @class Roo.form.TextField
21759  * @extends Roo.form.Field
21760  * Basic text field.  Can be used as a direct replacement for traditional text inputs, or as the base
21761  * class for more sophisticated input controls (like {@link Roo.form.TextArea} and {@link Roo.form.ComboBox}).
21762  * @constructor
21763  * Creates a new TextField
21764  * @param {Object} config Configuration options
21765  */
21766 Roo.form.TextField = function(config){
21767     Roo.form.TextField.superclass.constructor.call(this, config);
21768     this.addEvents({
21769         /**
21770          * @event autosize
21771          * Fires when the autosize function is triggered.  The field may or may not have actually changed size
21772          * according to the default logic, but this event provides a hook for the developer to apply additional
21773          * logic at runtime to resize the field if needed.
21774              * @param {Roo.form.Field} this This text field
21775              * @param {Number} width The new field width
21776              */
21777         autosize : true
21778     });
21779 };
21780
21781 Roo.extend(Roo.form.TextField, Roo.form.Field,  {
21782     /**
21783      * @cfg {Boolean} grow True if this field should automatically grow and shrink to its content
21784      */
21785     grow : false,
21786     /**
21787      * @cfg {Number} growMin The minimum width to allow when grow = true (defaults to 30)
21788      */
21789     growMin : 30,
21790     /**
21791      * @cfg {Number} growMax The maximum width to allow when grow = true (defaults to 800)
21792      */
21793     growMax : 800,
21794     /**
21795      * @cfg {String} vtype A validation type name as defined in {@link Roo.form.VTypes} (defaults to null)
21796      */
21797     vtype : null,
21798     /**
21799      * @cfg {String} maskRe An input mask regular expression that will be used to filter keystrokes that don't match (defaults to null)
21800      */
21801     maskRe : null,
21802     /**
21803      * @cfg {Boolean} disableKeyFilter True to disable input keystroke filtering (defaults to false)
21804      */
21805     disableKeyFilter : false,
21806     /**
21807      * @cfg {Boolean} allowBlank False to validate that the value length > 0 (defaults to true)
21808      */
21809     allowBlank : true,
21810     /**
21811      * @cfg {Number} minLength Minimum input field length required (defaults to 0)
21812      */
21813     minLength : 0,
21814     /**
21815      * @cfg {Number} maxLength Maximum input field length allowed (defaults to Number.MAX_VALUE)
21816      */
21817     maxLength : Number.MAX_VALUE,
21818     /**
21819      * @cfg {String} minLengthText Error text to display if the minimum length validation fails (defaults to "The minimum length for this field is {minLength}")
21820      */
21821     minLengthText : "The minimum length for this field is {0}",
21822     /**
21823      * @cfg {String} maxLengthText Error text to display if the maximum length validation fails (defaults to "The maximum length for this field is {maxLength}")
21824      */
21825     maxLengthText : "The maximum length for this field is {0}",
21826     /**
21827      * @cfg {Boolean} selectOnFocus True to automatically select any existing field text when the field receives input focus (defaults to false)
21828      */
21829     selectOnFocus : false,
21830     /**
21831      * @cfg {String} blankText Error text to display if the allow blank validation fails (defaults to "This field is required")
21832      */
21833     blankText : "This field is required",
21834     /**
21835      * @cfg {Function} validator A custom validation function to be called during field validation (defaults to null).
21836      * If available, this function will be called only after the basic validators all return true, and will be passed the
21837      * current field value and expected to return boolean true if the value is valid or a string error message if invalid.
21838      */
21839     validator : null,
21840     /**
21841      * @cfg {RegExp} regex A JavaScript RegExp object to be tested against the field value during validation (defaults to null).
21842      * If available, this regex will be evaluated only after the basic validators all return true, and will be passed the
21843      * current field value.  If the test fails, the field will be marked invalid using {@link #regexText}.
21844      */
21845     regex : null,
21846     /**
21847      * @cfg {String} regexText The error text to display if {@link #regex} is used and the test fails during validation (defaults to "")
21848      */
21849     regexText : "",
21850     /**
21851      * @cfg {String} emptyText The default text to display in an empty field - placeholder... (defaults to null).
21852      */
21853     emptyText : null,
21854    
21855
21856     // private
21857     initEvents : function()
21858     {
21859         if (this.emptyText) {
21860             this.el.attr('placeholder', this.emptyText);
21861         }
21862         
21863         Roo.form.TextField.superclass.initEvents.call(this);
21864         if(this.validationEvent == 'keyup'){
21865             this.validationTask = new Roo.util.DelayedTask(this.validate, this);
21866             this.el.on('keyup', this.filterValidation, this);
21867         }
21868         else if(this.validationEvent !== false){
21869             this.el.on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
21870         }
21871         
21872         if(this.selectOnFocus){
21873             this.on("focus", this.preFocus, this);
21874             
21875         }
21876         if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
21877             this.el.on("keypress", this.filterKeys, this);
21878         }
21879         if(this.grow){
21880             this.el.on("keyup", this.onKeyUp,  this, {buffer:50});
21881             this.el.on("click", this.autoSize,  this);
21882         }
21883         if(this.el.is('input[type=password]') && Roo.isSafari){
21884             this.el.on('keydown', this.SafariOnKeyDown, this);
21885         }
21886     },
21887
21888     processValue : function(value){
21889         if(this.stripCharsRe){
21890             var newValue = value.replace(this.stripCharsRe, '');
21891             if(newValue !== value){
21892                 this.setRawValue(newValue);
21893                 return newValue;
21894             }
21895         }
21896         return value;
21897     },
21898
21899     filterValidation : function(e){
21900         if(!e.isNavKeyPress()){
21901             this.validationTask.delay(this.validationDelay);
21902         }
21903     },
21904
21905     // private
21906     onKeyUp : function(e){
21907         if(!e.isNavKeyPress()){
21908             this.autoSize();
21909         }
21910     },
21911
21912     /**
21913      * Resets the current field value to the originally-loaded value and clears any validation messages.
21914      *  
21915      */
21916     reset : function(){
21917         Roo.form.TextField.superclass.reset.call(this);
21918        
21919     },
21920
21921     
21922     // private
21923     preFocus : function(){
21924         
21925         if(this.selectOnFocus){
21926             this.el.dom.select();
21927         }
21928     },
21929
21930     
21931     // private
21932     filterKeys : function(e){
21933         var k = e.getKey();
21934         if(!Roo.isIE && (e.isNavKeyPress() || k == e.BACKSPACE || (k == e.DELETE && e.button == -1))){
21935             return;
21936         }
21937         var c = e.getCharCode(), cc = String.fromCharCode(c);
21938         if(Roo.isIE && (e.isSpecialKey() || !cc)){
21939             return;
21940         }
21941         if(!this.maskRe.test(cc)){
21942             e.stopEvent();
21943         }
21944     },
21945
21946     setValue : function(v){
21947         
21948         Roo.form.TextField.superclass.setValue.apply(this, arguments);
21949         
21950         this.autoSize();
21951     },
21952
21953     /**
21954      * Validates a value according to the field's validation rules and marks the field as invalid
21955      * if the validation fails
21956      * @param {Mixed} value The value to validate
21957      * @return {Boolean} True if the value is valid, else false
21958      */
21959     validateValue : function(value){
21960         if(value.length < 1)  { // if it's blank
21961              if(this.allowBlank){
21962                 this.clearInvalid();
21963                 return true;
21964              }else{
21965                 this.markInvalid(this.blankText);
21966                 return false;
21967              }
21968         }
21969         if(value.length < this.minLength){
21970             this.markInvalid(String.format(this.minLengthText, this.minLength));
21971             return false;
21972         }
21973         if(value.length > this.maxLength){
21974             this.markInvalid(String.format(this.maxLengthText, this.maxLength));
21975             return false;
21976         }
21977         if(this.vtype){
21978             var vt = Roo.form.VTypes;
21979             if(!vt[this.vtype](value, this)){
21980                 this.markInvalid(this.vtypeText || vt[this.vtype +'Text']);
21981                 return false;
21982             }
21983         }
21984         if(typeof this.validator == "function"){
21985             var msg = this.validator(value);
21986             if(msg !== true){
21987                 this.markInvalid(msg);
21988                 return false;
21989             }
21990         }
21991         if(this.regex && !this.regex.test(value)){
21992             this.markInvalid(this.regexText);
21993             return false;
21994         }
21995         return true;
21996     },
21997
21998     /**
21999      * Selects text in this field
22000      * @param {Number} start (optional) The index where the selection should start (defaults to 0)
22001      * @param {Number} end (optional) The index where the selection should end (defaults to the text length)
22002      */
22003     selectText : function(start, end){
22004         var v = this.getRawValue();
22005         if(v.length > 0){
22006             start = start === undefined ? 0 : start;
22007             end = end === undefined ? v.length : end;
22008             var d = this.el.dom;
22009             if(d.setSelectionRange){
22010                 d.setSelectionRange(start, end);
22011             }else if(d.createTextRange){
22012                 var range = d.createTextRange();
22013                 range.moveStart("character", start);
22014                 range.moveEnd("character", v.length-end);
22015                 range.select();
22016             }
22017         }
22018     },
22019
22020     /**
22021      * Automatically grows the field to accomodate the width of the text up to the maximum field width allowed.
22022      * This only takes effect if grow = true, and fires the autosize event.
22023      */
22024     autoSize : function(){
22025         if(!this.grow || !this.rendered){
22026             return;
22027         }
22028         if(!this.metrics){
22029             this.metrics = Roo.util.TextMetrics.createInstance(this.el);
22030         }
22031         var el = this.el;
22032         var v = el.dom.value;
22033         var d = document.createElement('div');
22034         d.appendChild(document.createTextNode(v));
22035         v = d.innerHTML;
22036         d = null;
22037         v += "&#160;";
22038         var w = Math.min(this.growMax, Math.max(this.metrics.getWidth(v) + /* add extra padding */ 10, this.growMin));
22039         this.el.setWidth(w);
22040         this.fireEvent("autosize", this, w);
22041     },
22042     
22043     // private
22044     SafariOnKeyDown : function(event)
22045     {
22046         // this is a workaround for a password hang bug on chrome/ webkit.
22047         
22048         var isSelectAll = false;
22049         
22050         if(this.el.dom.selectionEnd > 0){
22051             isSelectAll = (this.el.dom.selectionEnd - this.el.dom.selectionStart - this.getValue().length == 0) ? true : false;
22052         }
22053         if(((event.getKey() == 8 || event.getKey() == 46) && this.getValue().length ==1)){ // backspace and delete key
22054             event.preventDefault();
22055             this.setValue('');
22056             return;
22057         }
22058         
22059         if(isSelectAll){ // backspace and delete key
22060             
22061             event.preventDefault();
22062             // this is very hacky as keydown always get's upper case.
22063             //
22064             var cc = String.fromCharCode(event.getCharCode());
22065             this.setValue( event.shiftKey ?  cc : cc.toLowerCase());
22066             
22067         }
22068         
22069         
22070     }
22071 });/*
22072  * Based on:
22073  * Ext JS Library 1.1.1
22074  * Copyright(c) 2006-2007, Ext JS, LLC.
22075  *
22076  * Originally Released Under LGPL - original licence link has changed is not relivant.
22077  *
22078  * Fork - LGPL
22079  * <script type="text/javascript">
22080  */
22081  
22082 /**
22083  * @class Roo.form.Hidden
22084  * @extends Roo.form.TextField
22085  * Simple Hidden element used on forms 
22086  * 
22087  * usage: form.add(new Roo.form.HiddenField({ 'name' : 'test1' }));
22088  * 
22089  * @constructor
22090  * Creates a new Hidden form element.
22091  * @param {Object} config Configuration options
22092  */
22093
22094
22095
22096 // easy hidden field...
22097 Roo.form.Hidden = function(config){
22098     Roo.form.Hidden.superclass.constructor.call(this, config);
22099 };
22100   
22101 Roo.extend(Roo.form.Hidden, Roo.form.TextField, {
22102     fieldLabel:      '',
22103     inputType:      'hidden',
22104     width:          50,
22105     allowBlank:     true,
22106     labelSeparator: '',
22107     hidden:         true,
22108     itemCls :       'x-form-item-display-none'
22109
22110
22111 });
22112
22113
22114 /*
22115  * Based on:
22116  * Ext JS Library 1.1.1
22117  * Copyright(c) 2006-2007, Ext JS, LLC.
22118  *
22119  * Originally Released Under LGPL - original licence link has changed is not relivant.
22120  *
22121  * Fork - LGPL
22122  * <script type="text/javascript">
22123  */
22124  
22125 /**
22126  * @class Roo.form.TriggerField
22127  * @extends Roo.form.TextField
22128  * Provides a convenient wrapper for TextFields that adds a clickable trigger button (looks like a combobox by default).
22129  * The trigger has no default action, so you must assign a function to implement the trigger click handler by
22130  * overriding {@link #onTriggerClick}. You can create a TriggerField directly, as it renders exactly like a combobox
22131  * for which you can provide a custom implementation.  For example:
22132  * <pre><code>
22133 var trigger = new Roo.form.TriggerField();
22134 trigger.onTriggerClick = myTriggerFn;
22135 trigger.applyTo('my-field');
22136 </code></pre>
22137  *
22138  * However, in general you will most likely want to use TriggerField as the base class for a reusable component.
22139  * {@link Roo.form.DateField} and {@link Roo.form.ComboBox} are perfect examples of this.
22140  * @cfg {String} triggerClass An additional CSS class used to style the trigger button.  The trigger will always get the
22141  * class 'x-form-trigger' by default and triggerClass will be <b>appended</b> if specified.
22142  * @constructor
22143  * Create a new TriggerField.
22144  * @param {Object} config Configuration options (valid {@Roo.form.TextField} config options will also be applied
22145  * to the base TextField)
22146  */
22147 Roo.form.TriggerField = function(config){
22148     this.mimicing = false;
22149     Roo.form.TriggerField.superclass.constructor.call(this, config);
22150 };
22151
22152 Roo.extend(Roo.form.TriggerField, Roo.form.TextField,  {
22153     /**
22154      * @cfg {String} triggerClass A CSS class to apply to the trigger
22155      */
22156     /**
22157      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
22158      * {tag: "input", type: "text", size: "16", autocomplete: "off"})
22159      */
22160     defaultAutoCreate : {tag: "input", type: "text", size: "16", autocomplete: "off"},
22161     /**
22162      * @cfg {Boolean} hideTrigger True to hide the trigger element and display only the base text field (defaults to false)
22163      */
22164     hideTrigger:false,
22165
22166     /** @cfg {Boolean} grow @hide */
22167     /** @cfg {Number} growMin @hide */
22168     /** @cfg {Number} growMax @hide */
22169
22170     /**
22171      * @hide 
22172      * @method
22173      */
22174     autoSize: Roo.emptyFn,
22175     // private
22176     monitorTab : true,
22177     // private
22178     deferHeight : true,
22179
22180     
22181     actionMode : 'wrap',
22182     // private
22183     onResize : function(w, h){
22184         Roo.form.TriggerField.superclass.onResize.apply(this, arguments);
22185         if(typeof w == 'number'){
22186             var x = w - this.trigger.getWidth();
22187             this.el.setWidth(this.adjustWidth('input', x));
22188             this.trigger.setStyle('left', x+'px');
22189         }
22190     },
22191
22192     // private
22193     adjustSize : Roo.BoxComponent.prototype.adjustSize,
22194
22195     // private
22196     getResizeEl : function(){
22197         return this.wrap;
22198     },
22199
22200     // private
22201     getPositionEl : function(){
22202         return this.wrap;
22203     },
22204
22205     // private
22206     alignErrorIcon : function(){
22207         this.errorIcon.alignTo(this.wrap, 'tl-tr', [2, 0]);
22208     },
22209
22210     // private
22211     onRender : function(ct, position){
22212         Roo.form.TriggerField.superclass.onRender.call(this, ct, position);
22213         this.wrap = this.el.wrap({cls: "x-form-field-wrap"});
22214         this.trigger = this.wrap.createChild(this.triggerConfig ||
22215                 {tag: "img", src: Roo.BLANK_IMAGE_URL, cls: "x-form-trigger " + this.triggerClass});
22216         if(this.hideTrigger){
22217             this.trigger.setDisplayed(false);
22218         }
22219         this.initTrigger();
22220         if(!this.width){
22221             this.wrap.setWidth(this.el.getWidth()+this.trigger.getWidth());
22222         }
22223     },
22224
22225     // private
22226     initTrigger : function(){
22227         this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
22228         this.trigger.addClassOnOver('x-form-trigger-over');
22229         this.trigger.addClassOnClick('x-form-trigger-click');
22230     },
22231
22232     // private
22233     onDestroy : function(){
22234         if(this.trigger){
22235             this.trigger.removeAllListeners();
22236             this.trigger.remove();
22237         }
22238         if(this.wrap){
22239             this.wrap.remove();
22240         }
22241         Roo.form.TriggerField.superclass.onDestroy.call(this);
22242     },
22243
22244     // private
22245     onFocus : function(){
22246         Roo.form.TriggerField.superclass.onFocus.call(this);
22247         if(!this.mimicing){
22248             this.wrap.addClass('x-trigger-wrap-focus');
22249             this.mimicing = true;
22250             Roo.get(Roo.isIE ? document.body : document).on("mousedown", this.mimicBlur, this);
22251             if(this.monitorTab){
22252                 this.el.on("keydown", this.checkTab, this);
22253             }
22254         }
22255     },
22256
22257     // private
22258     checkTab : function(e){
22259         if(e.getKey() == e.TAB){
22260             this.triggerBlur();
22261         }
22262     },
22263
22264     // private
22265     onBlur : function(){
22266         // do nothing
22267     },
22268
22269     // private
22270     mimicBlur : function(e, t){
22271         if(!this.wrap.contains(t) && this.validateBlur()){
22272             this.triggerBlur();
22273         }
22274     },
22275
22276     // private
22277     triggerBlur : function(){
22278         this.mimicing = false;
22279         Roo.get(Roo.isIE ? document.body : document).un("mousedown", this.mimicBlur);
22280         if(this.monitorTab){
22281             this.el.un("keydown", this.checkTab, this);
22282         }
22283         this.wrap.removeClass('x-trigger-wrap-focus');
22284         Roo.form.TriggerField.superclass.onBlur.call(this);
22285     },
22286
22287     // private
22288     // This should be overriden by any subclass that needs to check whether or not the field can be blurred.
22289     validateBlur : function(e, t){
22290         return true;
22291     },
22292
22293     // private
22294     onDisable : function(){
22295         Roo.form.TriggerField.superclass.onDisable.call(this);
22296         if(this.wrap){
22297             this.wrap.addClass('x-item-disabled');
22298         }
22299     },
22300
22301     // private
22302     onEnable : function(){
22303         Roo.form.TriggerField.superclass.onEnable.call(this);
22304         if(this.wrap){
22305             this.wrap.removeClass('x-item-disabled');
22306         }
22307     },
22308
22309     // private
22310     onShow : function(){
22311         var ae = this.getActionEl();
22312         
22313         if(ae){
22314             ae.dom.style.display = '';
22315             ae.dom.style.visibility = 'visible';
22316         }
22317     },
22318
22319     // private
22320     
22321     onHide : function(){
22322         var ae = this.getActionEl();
22323         ae.dom.style.display = 'none';
22324     },
22325
22326     /**
22327      * The function that should handle the trigger's click event.  This method does nothing by default until overridden
22328      * by an implementing function.
22329      * @method
22330      * @param {EventObject} e
22331      */
22332     onTriggerClick : Roo.emptyFn
22333 });
22334
22335 // TwinTriggerField is not a public class to be used directly.  It is meant as an abstract base class
22336 // to be extended by an implementing class.  For an example of implementing this class, see the custom
22337 // SearchField implementation here: http://extjs.com/deploy/ext/examples/form/custom.html
22338 Roo.form.TwinTriggerField = Roo.extend(Roo.form.TriggerField, {
22339     initComponent : function(){
22340         Roo.form.TwinTriggerField.superclass.initComponent.call(this);
22341
22342         this.triggerConfig = {
22343             tag:'span', cls:'x-form-twin-triggers', cn:[
22344             {tag: "img", src: Roo.BLANK_IMAGE_URL, cls: "x-form-trigger " + this.trigger1Class},
22345             {tag: "img", src: Roo.BLANK_IMAGE_URL, cls: "x-form-trigger " + this.trigger2Class}
22346         ]};
22347     },
22348
22349     getTrigger : function(index){
22350         return this.triggers[index];
22351     },
22352
22353     initTrigger : function(){
22354         var ts = this.trigger.select('.x-form-trigger', true);
22355         this.wrap.setStyle('overflow', 'hidden');
22356         var triggerField = this;
22357         ts.each(function(t, all, index){
22358             t.hide = function(){
22359                 var w = triggerField.wrap.getWidth();
22360                 this.dom.style.display = 'none';
22361                 triggerField.el.setWidth(w-triggerField.trigger.getWidth());
22362             };
22363             t.show = function(){
22364                 var w = triggerField.wrap.getWidth();
22365                 this.dom.style.display = '';
22366                 triggerField.el.setWidth(w-triggerField.trigger.getWidth());
22367             };
22368             var triggerIndex = 'Trigger'+(index+1);
22369
22370             if(this['hide'+triggerIndex]){
22371                 t.dom.style.display = 'none';
22372             }
22373             t.on("click", this['on'+triggerIndex+'Click'], this, {preventDefault:true});
22374             t.addClassOnOver('x-form-trigger-over');
22375             t.addClassOnClick('x-form-trigger-click');
22376         }, this);
22377         this.triggers = ts.elements;
22378     },
22379
22380     onTrigger1Click : Roo.emptyFn,
22381     onTrigger2Click : Roo.emptyFn
22382 });/*
22383  * Based on:
22384  * Ext JS Library 1.1.1
22385  * Copyright(c) 2006-2007, Ext JS, LLC.
22386  *
22387  * Originally Released Under LGPL - original licence link has changed is not relivant.
22388  *
22389  * Fork - LGPL
22390  * <script type="text/javascript">
22391  */
22392  
22393 /**
22394  * @class Roo.form.TextArea
22395  * @extends Roo.form.TextField
22396  * Multiline text field.  Can be used as a direct replacement for traditional textarea fields, plus adds
22397  * support for auto-sizing.
22398  * @constructor
22399  * Creates a new TextArea
22400  * @param {Object} config Configuration options
22401  */
22402 Roo.form.TextArea = function(config){
22403     Roo.form.TextArea.superclass.constructor.call(this, config);
22404     // these are provided exchanges for backwards compat
22405     // minHeight/maxHeight were replaced by growMin/growMax to be
22406     // compatible with TextField growing config values
22407     if(this.minHeight !== undefined){
22408         this.growMin = this.minHeight;
22409     }
22410     if(this.maxHeight !== undefined){
22411         this.growMax = this.maxHeight;
22412     }
22413 };
22414
22415 Roo.extend(Roo.form.TextArea, Roo.form.TextField,  {
22416     /**
22417      * @cfg {Number} growMin The minimum height to allow when grow = true (defaults to 60)
22418      */
22419     growMin : 60,
22420     /**
22421      * @cfg {Number} growMax The maximum height to allow when grow = true (defaults to 1000)
22422      */
22423     growMax: 1000,
22424     /**
22425      * @cfg {Boolean} preventScrollbars True to prevent scrollbars from appearing regardless of how much text is
22426      * in the field (equivalent to setting overflow: hidden, defaults to false)
22427      */
22428     preventScrollbars: false,
22429     /**
22430      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
22431      * {tag: "textarea", style: "width:300px;height:60px;", autocomplete: "off"})
22432      */
22433
22434     // private
22435     onRender : function(ct, position){
22436         if(!this.el){
22437             this.defaultAutoCreate = {
22438                 tag: "textarea",
22439                 style:"width:300px;height:60px;",
22440                 autocomplete: "off"
22441             };
22442         }
22443         Roo.form.TextArea.superclass.onRender.call(this, ct, position);
22444         if(this.grow){
22445             this.textSizeEl = Roo.DomHelper.append(document.body, {
22446                 tag: "pre", cls: "x-form-grow-sizer"
22447             });
22448             if(this.preventScrollbars){
22449                 this.el.setStyle("overflow", "hidden");
22450             }
22451             this.el.setHeight(this.growMin);
22452         }
22453     },
22454
22455     onDestroy : function(){
22456         if(this.textSizeEl){
22457             this.textSizeEl.parentNode.removeChild(this.textSizeEl);
22458         }
22459         Roo.form.TextArea.superclass.onDestroy.call(this);
22460     },
22461
22462     // private
22463     onKeyUp : function(e){
22464         if(!e.isNavKeyPress() || e.getKey() == e.ENTER){
22465             this.autoSize();
22466         }
22467     },
22468
22469     /**
22470      * Automatically grows the field to accomodate the height of the text up to the maximum field height allowed.
22471      * This only takes effect if grow = true, and fires the autosize event if the height changes.
22472      */
22473     autoSize : function(){
22474         if(!this.grow || !this.textSizeEl){
22475             return;
22476         }
22477         var el = this.el;
22478         var v = el.dom.value;
22479         var ts = this.textSizeEl;
22480
22481         ts.innerHTML = '';
22482         ts.appendChild(document.createTextNode(v));
22483         v = ts.innerHTML;
22484
22485         Roo.fly(ts).setWidth(this.el.getWidth());
22486         if(v.length < 1){
22487             v = "&#160;&#160;";
22488         }else{
22489             if(Roo.isIE){
22490                 v = v.replace(/\n/g, '<p>&#160;</p>');
22491             }
22492             v += "&#160;\n&#160;";
22493         }
22494         ts.innerHTML = v;
22495         var h = Math.min(this.growMax, Math.max(ts.offsetHeight, this.growMin));
22496         if(h != this.lastHeight){
22497             this.lastHeight = h;
22498             this.el.setHeight(h);
22499             this.fireEvent("autosize", this, h);
22500         }
22501     }
22502 });/*
22503  * Based on:
22504  * Ext JS Library 1.1.1
22505  * Copyright(c) 2006-2007, Ext JS, LLC.
22506  *
22507  * Originally Released Under LGPL - original licence link has changed is not relivant.
22508  *
22509  * Fork - LGPL
22510  * <script type="text/javascript">
22511  */
22512  
22513
22514 /**
22515  * @class Roo.form.NumberField
22516  * @extends Roo.form.TextField
22517  * Numeric text field that provides automatic keystroke filtering and numeric validation.
22518  * @constructor
22519  * Creates a new NumberField
22520  * @param {Object} config Configuration options
22521  */
22522 Roo.form.NumberField = function(config){
22523     Roo.form.NumberField.superclass.constructor.call(this, config);
22524 };
22525
22526 Roo.extend(Roo.form.NumberField, Roo.form.TextField,  {
22527     /**
22528      * @cfg {String} fieldClass The default CSS class for the field (defaults to "x-form-field x-form-num-field")
22529      */
22530     fieldClass: "x-form-field x-form-num-field",
22531     /**
22532      * @cfg {Boolean} allowDecimals False to disallow decimal values (defaults to true)
22533      */
22534     allowDecimals : true,
22535     /**
22536      * @cfg {String} decimalSeparator Character(s) to allow as the decimal separator (defaults to '.')
22537      */
22538     decimalSeparator : ".",
22539     /**
22540      * @cfg {Number} decimalPrecision The maximum precision to display after the decimal separator (defaults to 2)
22541      */
22542     decimalPrecision : 2,
22543     /**
22544      * @cfg {Boolean} allowNegative False to prevent entering a negative sign (defaults to true)
22545      */
22546     allowNegative : true,
22547     /**
22548      * @cfg {Number} minValue The minimum allowed value (defaults to Number.NEGATIVE_INFINITY)
22549      */
22550     minValue : Number.NEGATIVE_INFINITY,
22551     /**
22552      * @cfg {Number} maxValue The maximum allowed value (defaults to Number.MAX_VALUE)
22553      */
22554     maxValue : Number.MAX_VALUE,
22555     /**
22556      * @cfg {String} minText Error text to display if the minimum value validation fails (defaults to "The minimum value for this field is {minValue}")
22557      */
22558     minText : "The minimum value for this field is {0}",
22559     /**
22560      * @cfg {String} maxText Error text to display if the maximum value validation fails (defaults to "The maximum value for this field is {maxValue}")
22561      */
22562     maxText : "The maximum value for this field is {0}",
22563     /**
22564      * @cfg {String} nanText Error text to display if the value is not a valid number.  For example, this can happen
22565      * if a valid character like '.' or '-' is left in the field with no number (defaults to "{value} is not a valid number")
22566      */
22567     nanText : "{0} is not a valid number",
22568
22569     // private
22570     initEvents : function(){
22571         Roo.form.NumberField.superclass.initEvents.call(this);
22572         var allowed = "0123456789";
22573         if(this.allowDecimals){
22574             allowed += this.decimalSeparator;
22575         }
22576         if(this.allowNegative){
22577             allowed += "-";
22578         }
22579         this.stripCharsRe = new RegExp('[^'+allowed+']', 'gi');
22580         var keyPress = function(e){
22581             var k = e.getKey();
22582             if(!Roo.isIE && (e.isSpecialKey() || k == e.BACKSPACE || k == e.DELETE)){
22583                 return;
22584             }
22585             var c = e.getCharCode();
22586             if(allowed.indexOf(String.fromCharCode(c)) === -1){
22587                 e.stopEvent();
22588             }
22589         };
22590         this.el.on("keypress", keyPress, this);
22591     },
22592
22593     // private
22594     validateValue : function(value){
22595         if(!Roo.form.NumberField.superclass.validateValue.call(this, value)){
22596             return false;
22597         }
22598         if(value.length < 1){ // if it's blank and textfield didn't flag it then it's valid
22599              return true;
22600         }
22601         var num = this.parseValue(value);
22602         if(isNaN(num)){
22603             this.markInvalid(String.format(this.nanText, value));
22604             return false;
22605         }
22606         if(num < this.minValue){
22607             this.markInvalid(String.format(this.minText, this.minValue));
22608             return false;
22609         }
22610         if(num > this.maxValue){
22611             this.markInvalid(String.format(this.maxText, this.maxValue));
22612             return false;
22613         }
22614         return true;
22615     },
22616
22617     getValue : function(){
22618         return this.fixPrecision(this.parseValue(Roo.form.NumberField.superclass.getValue.call(this)));
22619     },
22620
22621     // private
22622     parseValue : function(value){
22623         value = parseFloat(String(value).replace(this.decimalSeparator, "."));
22624         return isNaN(value) ? '' : value;
22625     },
22626
22627     // private
22628     fixPrecision : function(value){
22629         var nan = isNaN(value);
22630         if(!this.allowDecimals || this.decimalPrecision == -1 || nan || !value){
22631             return nan ? '' : value;
22632         }
22633         return parseFloat(value).toFixed(this.decimalPrecision);
22634     },
22635
22636     setValue : function(v){
22637         v = this.fixPrecision(v);
22638         Roo.form.NumberField.superclass.setValue.call(this, String(v).replace(".", this.decimalSeparator));
22639     },
22640
22641     // private
22642     decimalPrecisionFcn : function(v){
22643         return Math.floor(v);
22644     },
22645
22646     beforeBlur : function(){
22647         var v = this.parseValue(this.getRawValue());
22648         if(v){
22649             this.setValue(v);
22650         }
22651     }
22652 });/*
22653  * Based on:
22654  * Ext JS Library 1.1.1
22655  * Copyright(c) 2006-2007, Ext JS, LLC.
22656  *
22657  * Originally Released Under LGPL - original licence link has changed is not relivant.
22658  *
22659  * Fork - LGPL
22660  * <script type="text/javascript">
22661  */
22662  
22663 /**
22664  * @class Roo.form.DateField
22665  * @extends Roo.form.TriggerField
22666  * Provides a date input field with a {@link Roo.DatePicker} dropdown and automatic date validation.
22667 * @constructor
22668 * Create a new DateField
22669 * @param {Object} config
22670  */
22671 Roo.form.DateField = function(config){
22672     Roo.form.DateField.superclass.constructor.call(this, config);
22673     
22674       this.addEvents({
22675          
22676         /**
22677          * @event select
22678          * Fires when a date is selected
22679              * @param {Roo.form.DateField} combo This combo box
22680              * @param {Date} date The date selected
22681              */
22682         'select' : true
22683          
22684     });
22685     
22686     
22687     if(typeof this.minValue == "string") this.minValue = this.parseDate(this.minValue);
22688     if(typeof this.maxValue == "string") this.maxValue = this.parseDate(this.maxValue);
22689     this.ddMatch = null;
22690     if(this.disabledDates){
22691         var dd = this.disabledDates;
22692         var re = "(?:";
22693         for(var i = 0; i < dd.length; i++){
22694             re += dd[i];
22695             if(i != dd.length-1) re += "|";
22696         }
22697         this.ddMatch = new RegExp(re + ")");
22698     }
22699 };
22700
22701 Roo.extend(Roo.form.DateField, Roo.form.TriggerField,  {
22702     /**
22703      * @cfg {String} format
22704      * The default date format string which can be overriden for localization support.  The format must be
22705      * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
22706      */
22707     format : "m/d/y",
22708     /**
22709      * @cfg {String} altFormats
22710      * Multiple date formats separated by "|" to try when parsing a user input value and it doesn't match the defined
22711      * format (defaults to 'm/d/Y|m-d-y|m-d-Y|m/d|m-d|d').
22712      */
22713     altFormats : "m/d/Y|m-d-y|m-d-Y|m/d|m-d|md|mdy|mdY|d",
22714     /**
22715      * @cfg {Array} disabledDays
22716      * An array of days to disable, 0 based. For example, [0, 6] disables Sunday and Saturday (defaults to null).
22717      */
22718     disabledDays : null,
22719     /**
22720      * @cfg {String} disabledDaysText
22721      * The tooltip to display when the date falls on a disabled day (defaults to 'Disabled')
22722      */
22723     disabledDaysText : "Disabled",
22724     /**
22725      * @cfg {Array} disabledDates
22726      * An array of "dates" to disable, as strings. These strings will be used to build a dynamic regular
22727      * expression so they are very powerful. Some examples:
22728      * <ul>
22729      * <li>["03/08/2003", "09/16/2003"] would disable those exact dates</li>
22730      * <li>["03/08", "09/16"] would disable those days for every year</li>
22731      * <li>["^03/08"] would only match the beginning (useful if you are using short years)</li>
22732      * <li>["03/../2006"] would disable every day in March 2006</li>
22733      * <li>["^03"] would disable every day in every March</li>
22734      * </ul>
22735      * In order to support regular expressions, if you are using a date format that has "." in it, you will have to
22736      * escape the dot when restricting dates. For example: ["03\\.08\\.03"].
22737      */
22738     disabledDates : null,
22739     /**
22740      * @cfg {String} disabledDatesText
22741      * The tooltip text to display when the date falls on a disabled date (defaults to 'Disabled')
22742      */
22743     disabledDatesText : "Disabled",
22744     /**
22745      * @cfg {Date/String} minValue
22746      * The minimum allowed date. Can be either a Javascript date object or a string date in a
22747      * valid format (defaults to null).
22748      */
22749     minValue : null,
22750     /**
22751      * @cfg {Date/String} maxValue
22752      * The maximum allowed date. Can be either a Javascript date object or a string date in a
22753      * valid format (defaults to null).
22754      */
22755     maxValue : null,
22756     /**
22757      * @cfg {String} minText
22758      * The error text to display when the date in the cell is before minValue (defaults to
22759      * 'The date in this field must be after {minValue}').
22760      */
22761     minText : "The date in this field must be equal to or after {0}",
22762     /**
22763      * @cfg {String} maxText
22764      * The error text to display when the date in the cell is after maxValue (defaults to
22765      * 'The date in this field must be before {maxValue}').
22766      */
22767     maxText : "The date in this field must be equal to or before {0}",
22768     /**
22769      * @cfg {String} invalidText
22770      * The error text to display when the date in the field is invalid (defaults to
22771      * '{value} is not a valid date - it must be in the format {format}').
22772      */
22773     invalidText : "{0} is not a valid date - it must be in the format {1}",
22774     /**
22775      * @cfg {String} triggerClass
22776      * An additional CSS class used to style the trigger button.  The trigger will always get the
22777      * class 'x-form-trigger' and triggerClass will be <b>appended</b> if specified (defaults to 'x-form-date-trigger'
22778      * which displays a calendar icon).
22779      */
22780     triggerClass : 'x-form-date-trigger',
22781     
22782
22783     /**
22784      * @cfg {Boolean} useIso
22785      * if enabled, then the date field will use a hidden field to store the 
22786      * real value as iso formated date. default (false)
22787      */ 
22788     useIso : false,
22789     /**
22790      * @cfg {String/Object} autoCreate
22791      * A DomHelper element spec, or true for a default element spec (defaults to
22792      * {tag: "input", type: "text", size: "10", autocomplete: "off"})
22793      */ 
22794     // private
22795     defaultAutoCreate : {tag: "input", type: "text", size: "10", autocomplete: "off"},
22796     
22797     // private
22798     hiddenField: false,
22799     
22800     onRender : function(ct, position)
22801     {
22802         Roo.form.DateField.superclass.onRender.call(this, ct, position);
22803         if (this.useIso) {
22804             //this.el.dom.removeAttribute('name'); 
22805             Roo.log("Changing name?");
22806             this.el.dom.setAttribute('name', this.name + '____hidden___' ); 
22807             this.hiddenField = this.el.insertSibling({ tag:'input', type:'hidden', name: this.name },
22808                     'before', true);
22809             this.hiddenField.value = this.value ? this.formatDate(this.value, 'Y-m-d') : '';
22810             // prevent input submission
22811             this.hiddenName = this.name;
22812         }
22813             
22814             
22815     },
22816     
22817     // private
22818     validateValue : function(value)
22819     {
22820         value = this.formatDate(value);
22821         if(!Roo.form.DateField.superclass.validateValue.call(this, value)){
22822             Roo.log('super failed');
22823             return false;
22824         }
22825         if(value.length < 1){ // if it's blank and textfield didn't flag it then it's valid
22826              return true;
22827         }
22828         var svalue = value;
22829         value = this.parseDate(value);
22830         if(!value){
22831             Roo.log('parse date failed' + svalue);
22832             this.markInvalid(String.format(this.invalidText, svalue, this.format));
22833             return false;
22834         }
22835         var time = value.getTime();
22836         if(this.minValue && time < this.minValue.getTime()){
22837             this.markInvalid(String.format(this.minText, this.formatDate(this.minValue)));
22838             return false;
22839         }
22840         if(this.maxValue && time > this.maxValue.getTime()){
22841             this.markInvalid(String.format(this.maxText, this.formatDate(this.maxValue)));
22842             return false;
22843         }
22844         if(this.disabledDays){
22845             var day = value.getDay();
22846             for(var i = 0; i < this.disabledDays.length; i++) {
22847                 if(day === this.disabledDays[i]){
22848                     this.markInvalid(this.disabledDaysText);
22849                     return false;
22850                 }
22851             }
22852         }
22853         var fvalue = this.formatDate(value);
22854         if(this.ddMatch && this.ddMatch.test(fvalue)){
22855             this.markInvalid(String.format(this.disabledDatesText, fvalue));
22856             return false;
22857         }
22858         return true;
22859     },
22860
22861     // private
22862     // Provides logic to override the default TriggerField.validateBlur which just returns true
22863     validateBlur : function(){
22864         return !this.menu || !this.menu.isVisible();
22865     },
22866     
22867     getName: function()
22868     {
22869         // returns hidden if it's set..
22870         if (!this.rendered) {return ''};
22871         return !this.hiddenName && this.el.dom.name  ? this.el.dom.name : (this.hiddenName || '');
22872         
22873     },
22874
22875     /**
22876      * Returns the current date value of the date field.
22877      * @return {Date} The date value
22878      */
22879     getValue : function(){
22880         
22881         return  this.hiddenField ?
22882                 this.hiddenField.value :
22883                 this.parseDate(Roo.form.DateField.superclass.getValue.call(this)) || "";
22884     },
22885
22886     /**
22887      * Sets the value of the date field.  You can pass a date object or any string that can be parsed into a valid
22888      * date, using DateField.format as the date format, according to the same rules as {@link Date#parseDate}
22889      * (the default format used is "m/d/y").
22890      * <br />Usage:
22891      * <pre><code>
22892 //All of these calls set the same date value (May 4, 2006)
22893
22894 //Pass a date object:
22895 var dt = new Date('5/4/06');
22896 dateField.setValue(dt);
22897
22898 //Pass a date string (default format):
22899 dateField.setValue('5/4/06');
22900
22901 //Pass a date string (custom format):
22902 dateField.format = 'Y-m-d';
22903 dateField.setValue('2006-5-4');
22904 </code></pre>
22905      * @param {String/Date} date The date or valid date string
22906      */
22907     setValue : function(date){
22908         if (this.hiddenField) {
22909             this.hiddenField.value = this.formatDate(this.parseDate(date), 'Y-m-d');
22910         }
22911         Roo.form.DateField.superclass.setValue.call(this, this.formatDate(this.parseDate(date)));
22912         // make sure the value field is always stored as a date..
22913         this.value = this.parseDate(date);
22914         
22915         
22916     },
22917
22918     // private
22919     parseDate : function(value){
22920         if(!value || value instanceof Date){
22921             return value;
22922         }
22923         var v = Date.parseDate(value, this.format);
22924          if (!v && this.useIso) {
22925             v = Date.parseDate(value, 'Y-m-d');
22926         }
22927         if(!v && this.altFormats){
22928             if(!this.altFormatsArray){
22929                 this.altFormatsArray = this.altFormats.split("|");
22930             }
22931             for(var i = 0, len = this.altFormatsArray.length; i < len && !v; i++){
22932                 v = Date.parseDate(value, this.altFormatsArray[i]);
22933             }
22934         }
22935         return v;
22936     },
22937
22938     // private
22939     formatDate : function(date, fmt){
22940         return (!date || !(date instanceof Date)) ?
22941                date : date.dateFormat(fmt || this.format);
22942     },
22943
22944     // private
22945     menuListeners : {
22946         select: function(m, d){
22947             
22948             this.setValue(d);
22949             this.fireEvent('select', this, d);
22950         },
22951         show : function(){ // retain focus styling
22952             this.onFocus();
22953         },
22954         hide : function(){
22955             this.focus.defer(10, this);
22956             var ml = this.menuListeners;
22957             this.menu.un("select", ml.select,  this);
22958             this.menu.un("show", ml.show,  this);
22959             this.menu.un("hide", ml.hide,  this);
22960         }
22961     },
22962
22963     // private
22964     // Implements the default empty TriggerField.onTriggerClick function to display the DatePicker
22965     onTriggerClick : function(){
22966         if(this.disabled){
22967             return;
22968         }
22969         if(this.menu == null){
22970             this.menu = new Roo.menu.DateMenu();
22971         }
22972         Roo.apply(this.menu.picker,  {
22973             showClear: this.allowBlank,
22974             minDate : this.minValue,
22975             maxDate : this.maxValue,
22976             disabledDatesRE : this.ddMatch,
22977             disabledDatesText : this.disabledDatesText,
22978             disabledDays : this.disabledDays,
22979             disabledDaysText : this.disabledDaysText,
22980             format : this.useIso ? 'Y-m-d' : this.format,
22981             minText : String.format(this.minText, this.formatDate(this.minValue)),
22982             maxText : String.format(this.maxText, this.formatDate(this.maxValue))
22983         });
22984         this.menu.on(Roo.apply({}, this.menuListeners, {
22985             scope:this
22986         }));
22987         this.menu.picker.setValue(this.getValue() || new Date());
22988         this.menu.show(this.el, "tl-bl?");
22989     },
22990
22991     beforeBlur : function(){
22992         var v = this.parseDate(this.getRawValue());
22993         if(v){
22994             this.setValue(v);
22995         }
22996     }
22997
22998     /** @cfg {Boolean} grow @hide */
22999     /** @cfg {Number} growMin @hide */
23000     /** @cfg {Number} growMax @hide */
23001     /**
23002      * @hide
23003      * @method autoSize
23004      */
23005 });/*
23006  * Based on:
23007  * Ext JS Library 1.1.1
23008  * Copyright(c) 2006-2007, Ext JS, LLC.
23009  *
23010  * Originally Released Under LGPL - original licence link has changed is not relivant.
23011  *
23012  * Fork - LGPL
23013  * <script type="text/javascript">
23014  */
23015  
23016 /**
23017  * @class Roo.form.MonthField
23018  * @extends Roo.form.TriggerField
23019  * Provides a date input field with a {@link Roo.DatePicker} dropdown and automatic date validation.
23020 * @constructor
23021 * Create a new MonthField
23022 * @param {Object} config
23023  */
23024 Roo.form.MonthField = function(config){
23025     
23026     Roo.form.MonthField.superclass.constructor.call(this, config);
23027     
23028       this.addEvents({
23029          
23030         /**
23031          * @event select
23032          * Fires when a date is selected
23033              * @param {Roo.form.MonthFieeld} combo This combo box
23034              * @param {Date} date The date selected
23035              */
23036         'select' : true
23037          
23038     });
23039     
23040     
23041     if(typeof this.minValue == "string") this.minValue = this.parseDate(this.minValue);
23042     if(typeof this.maxValue == "string") this.maxValue = this.parseDate(this.maxValue);
23043     this.ddMatch = null;
23044     if(this.disabledDates){
23045         var dd = this.disabledDates;
23046         var re = "(?:";
23047         for(var i = 0; i < dd.length; i++){
23048             re += dd[i];
23049             if(i != dd.length-1) re += "|";
23050         }
23051         this.ddMatch = new RegExp(re + ")");
23052     }
23053 };
23054
23055 Roo.extend(Roo.form.MonthField, Roo.form.TriggerField,  {
23056     /**
23057      * @cfg {String} format
23058      * The default date format string which can be overriden for localization support.  The format must be
23059      * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
23060      */
23061     format : "M Y",
23062     /**
23063      * @cfg {String} altFormats
23064      * Multiple date formats separated by "|" to try when parsing a user input value and it doesn't match the defined
23065      * format (defaults to 'm/d/Y|m-d-y|m-d-Y|m/d|m-d|d').
23066      */
23067     altFormats : "M Y|m/Y|m-y|m-Y|my|mY",
23068     /**
23069      * @cfg {Array} disabledDays
23070      * An array of days to disable, 0 based. For example, [0, 6] disables Sunday and Saturday (defaults to null).
23071      */
23072     disabledDays : [0,1,2,3,4,5,6],
23073     /**
23074      * @cfg {String} disabledDaysText
23075      * The tooltip to display when the date falls on a disabled day (defaults to 'Disabled')
23076      */
23077     disabledDaysText : "Disabled",
23078     /**
23079      * @cfg {Array} disabledDates
23080      * An array of "dates" to disable, as strings. These strings will be used to build a dynamic regular
23081      * expression so they are very powerful. Some examples:
23082      * <ul>
23083      * <li>["03/08/2003", "09/16/2003"] would disable those exact dates</li>
23084      * <li>["03/08", "09/16"] would disable those days for every year</li>
23085      * <li>["^03/08"] would only match the beginning (useful if you are using short years)</li>
23086      * <li>["03/../2006"] would disable every day in March 2006</li>
23087      * <li>["^03"] would disable every day in every March</li>
23088      * </ul>
23089      * In order to support regular expressions, if you are using a date format that has "." in it, you will have to
23090      * escape the dot when restricting dates. For example: ["03\\.08\\.03"].
23091      */
23092     disabledDates : null,
23093     /**
23094      * @cfg {String} disabledDatesText
23095      * The tooltip text to display when the date falls on a disabled date (defaults to 'Disabled')
23096      */
23097     disabledDatesText : "Disabled",
23098     /**
23099      * @cfg {Date/String} minValue
23100      * The minimum allowed date. Can be either a Javascript date object or a string date in a
23101      * valid format (defaults to null).
23102      */
23103     minValue : null,
23104     /**
23105      * @cfg {Date/String} maxValue
23106      * The maximum allowed date. Can be either a Javascript date object or a string date in a
23107      * valid format (defaults to null).
23108      */
23109     maxValue : null,
23110     /**
23111      * @cfg {String} minText
23112      * The error text to display when the date in the cell is before minValue (defaults to
23113      * 'The date in this field must be after {minValue}').
23114      */
23115     minText : "The date in this field must be equal to or after {0}",
23116     /**
23117      * @cfg {String} maxTextf
23118      * The error text to display when the date in the cell is after maxValue (defaults to
23119      * 'The date in this field must be before {maxValue}').
23120      */
23121     maxText : "The date in this field must be equal to or before {0}",
23122     /**
23123      * @cfg {String} invalidText
23124      * The error text to display when the date in the field is invalid (defaults to
23125      * '{value} is not a valid date - it must be in the format {format}').
23126      */
23127     invalidText : "{0} is not a valid date - it must be in the format {1}",
23128     /**
23129      * @cfg {String} triggerClass
23130      * An additional CSS class used to style the trigger button.  The trigger will always get the
23131      * class 'x-form-trigger' and triggerClass will be <b>appended</b> if specified (defaults to 'x-form-date-trigger'
23132      * which displays a calendar icon).
23133      */
23134     triggerClass : 'x-form-date-trigger',
23135     
23136
23137     /**
23138      * @cfg {Boolean} useIso
23139      * if enabled, then the date field will use a hidden field to store the 
23140      * real value as iso formated date. default (true)
23141      */ 
23142     useIso : true,
23143     /**
23144      * @cfg {String/Object} autoCreate
23145      * A DomHelper element spec, or true for a default element spec (defaults to
23146      * {tag: "input", type: "text", size: "10", autocomplete: "off"})
23147      */ 
23148     // private
23149     defaultAutoCreate : {tag: "input", type: "text", size: "10", autocomplete: "off"},
23150     
23151     // private
23152     hiddenField: false,
23153     
23154     hideMonthPicker : false,
23155     
23156     onRender : function(ct, position)
23157     {
23158         Roo.form.MonthField.superclass.onRender.call(this, ct, position);
23159         if (this.useIso) {
23160             this.el.dom.removeAttribute('name'); 
23161             this.hiddenField = this.el.insertSibling({ tag:'input', type:'hidden', name: this.name },
23162                     'before', true);
23163             this.hiddenField.value = this.value ? this.formatDate(this.value, 'Y-m-d') : '';
23164             // prevent input submission
23165             this.hiddenName = this.name;
23166         }
23167             
23168             
23169     },
23170     
23171     // private
23172     validateValue : function(value)
23173     {
23174         value = this.formatDate(value);
23175         if(!Roo.form.MonthField.superclass.validateValue.call(this, value)){
23176             return false;
23177         }
23178         if(value.length < 1){ // if it's blank and textfield didn't flag it then it's valid
23179              return true;
23180         }
23181         var svalue = value;
23182         value = this.parseDate(value);
23183         if(!value){
23184             this.markInvalid(String.format(this.invalidText, svalue, this.format));
23185             return false;
23186         }
23187         var time = value.getTime();
23188         if(this.minValue && time < this.minValue.getTime()){
23189             this.markInvalid(String.format(this.minText, this.formatDate(this.minValue)));
23190             return false;
23191         }
23192         if(this.maxValue && time > this.maxValue.getTime()){
23193             this.markInvalid(String.format(this.maxText, this.formatDate(this.maxValue)));
23194             return false;
23195         }
23196         /*if(this.disabledDays){
23197             var day = value.getDay();
23198             for(var i = 0; i < this.disabledDays.length; i++) {
23199                 if(day === this.disabledDays[i]){
23200                     this.markInvalid(this.disabledDaysText);
23201                     return false;
23202                 }
23203             }
23204         }
23205         */
23206         var fvalue = this.formatDate(value);
23207         /*if(this.ddMatch && this.ddMatch.test(fvalue)){
23208             this.markInvalid(String.format(this.disabledDatesText, fvalue));
23209             return false;
23210         }
23211         */
23212         return true;
23213     },
23214
23215     // private
23216     // Provides logic to override the default TriggerField.validateBlur which just returns true
23217     validateBlur : function(){
23218         return !this.menu || !this.menu.isVisible();
23219     },
23220
23221     /**
23222      * Returns the current date value of the date field.
23223      * @return {Date} The date value
23224      */
23225     getValue : function(){
23226         
23227         
23228         
23229         return  this.hiddenField ?
23230                 this.hiddenField.value :
23231                 this.parseDate(Roo.form.MonthField.superclass.getValue.call(this)) || "";
23232     },
23233
23234     /**
23235      * Sets the value of the date field.  You can pass a date object or any string that can be parsed into a valid
23236      * date, using MonthField.format as the date format, according to the same rules as {@link Date#parseDate}
23237      * (the default format used is "m/d/y").
23238      * <br />Usage:
23239      * <pre><code>
23240 //All of these calls set the same date value (May 4, 2006)
23241
23242 //Pass a date object:
23243 var dt = new Date('5/4/06');
23244 monthField.setValue(dt);
23245
23246 //Pass a date string (default format):
23247 monthField.setValue('5/4/06');
23248
23249 //Pass a date string (custom format):
23250 monthField.format = 'Y-m-d';
23251 monthField.setValue('2006-5-4');
23252 </code></pre>
23253      * @param {String/Date} date The date or valid date string
23254      */
23255     setValue : function(date){
23256         Roo.log('month setValue' + date);
23257         // can only be first of month..
23258         
23259         var val = this.parseDate(date);
23260         
23261         if (this.hiddenField) {
23262             this.hiddenField.value = this.formatDate(this.parseDate(date), 'Y-m-d');
23263         }
23264         Roo.form.MonthField.superclass.setValue.call(this, this.formatDate(this.parseDate(date)));
23265         this.value = this.parseDate(date);
23266     },
23267
23268     // private
23269     parseDate : function(value){
23270         if(!value || value instanceof Date){
23271             value = value ? Date.parseDate(value.format('Y-m') + '-01', 'Y-m-d') : null;
23272             return value;
23273         }
23274         var v = Date.parseDate(value, this.format);
23275         if (!v && this.useIso) {
23276             v = Date.parseDate(value, 'Y-m-d');
23277         }
23278         if (v) {
23279             // 
23280             v = Date.parseDate(v.format('Y-m') +'-01', 'Y-m-d');
23281         }
23282         
23283         
23284         if(!v && this.altFormats){
23285             if(!this.altFormatsArray){
23286                 this.altFormatsArray = this.altFormats.split("|");
23287             }
23288             for(var i = 0, len = this.altFormatsArray.length; i < len && !v; i++){
23289                 v = Date.parseDate(value, this.altFormatsArray[i]);
23290             }
23291         }
23292         return v;
23293     },
23294
23295     // private
23296     formatDate : function(date, fmt){
23297         return (!date || !(date instanceof Date)) ?
23298                date : date.dateFormat(fmt || this.format);
23299     },
23300
23301     // private
23302     menuListeners : {
23303         select: function(m, d){
23304             this.setValue(d);
23305             this.fireEvent('select', this, d);
23306         },
23307         show : function(){ // retain focus styling
23308             this.onFocus();
23309         },
23310         hide : function(){
23311             this.focus.defer(10, this);
23312             var ml = this.menuListeners;
23313             this.menu.un("select", ml.select,  this);
23314             this.menu.un("show", ml.show,  this);
23315             this.menu.un("hide", ml.hide,  this);
23316         }
23317     },
23318     // private
23319     // Implements the default empty TriggerField.onTriggerClick function to display the DatePicker
23320     onTriggerClick : function(){
23321         if(this.disabled){
23322             return;
23323         }
23324         if(this.menu == null){
23325             this.menu = new Roo.menu.DateMenu();
23326            
23327         }
23328         
23329         Roo.apply(this.menu.picker,  {
23330             
23331             showClear: this.allowBlank,
23332             minDate : this.minValue,
23333             maxDate : this.maxValue,
23334             disabledDatesRE : this.ddMatch,
23335             disabledDatesText : this.disabledDatesText,
23336             
23337             format : this.useIso ? 'Y-m-d' : this.format,
23338             minText : String.format(this.minText, this.formatDate(this.minValue)),
23339             maxText : String.format(this.maxText, this.formatDate(this.maxValue))
23340             
23341         });
23342          this.menu.on(Roo.apply({}, this.menuListeners, {
23343             scope:this
23344         }));
23345        
23346         
23347         var m = this.menu;
23348         var p = m.picker;
23349         
23350         // hide month picker get's called when we called by 'before hide';
23351         
23352         var ignorehide = true;
23353         p.hideMonthPicker  = function(disableAnim){
23354             if (ignorehide) {
23355                 return;
23356             }
23357              if(this.monthPicker){
23358                 Roo.log("hideMonthPicker called");
23359                 if(disableAnim === true){
23360                     this.monthPicker.hide();
23361                 }else{
23362                     this.monthPicker.slideOut('t', {duration:.2});
23363                     p.setValue(new Date(m.picker.mpSelYear, m.picker.mpSelMonth, 1));
23364                     p.fireEvent("select", this, this.value);
23365                     m.hide();
23366                 }
23367             }
23368         }
23369         
23370         Roo.log('picker set value');
23371         Roo.log(this.getValue());
23372         p.setValue(this.getValue() ? this.parseDate(this.getValue()) : new Date());
23373         m.show(this.el, 'tl-bl?');
23374         ignorehide  = false;
23375         // this will trigger hideMonthPicker..
23376         
23377         
23378         // hidden the day picker
23379         Roo.select('.x-date-picker table', true).first().dom.style.visibility = "hidden";
23380         
23381         
23382         
23383       
23384         
23385         p.showMonthPicker.defer(100, p);
23386     
23387         
23388        
23389     },
23390
23391     beforeBlur : function(){
23392         var v = this.parseDate(this.getRawValue());
23393         if(v){
23394             this.setValue(v);
23395         }
23396     }
23397
23398     /** @cfg {Boolean} grow @hide */
23399     /** @cfg {Number} growMin @hide */
23400     /** @cfg {Number} growMax @hide */
23401     /**
23402      * @hide
23403      * @method autoSize
23404      */
23405 });/*
23406  * Based on:
23407  * Ext JS Library 1.1.1
23408  * Copyright(c) 2006-2007, Ext JS, LLC.
23409  *
23410  * Originally Released Under LGPL - original licence link has changed is not relivant.
23411  *
23412  * Fork - LGPL
23413  * <script type="text/javascript">
23414  */
23415  
23416
23417 /**
23418  * @class Roo.form.ComboBox
23419  * @extends Roo.form.TriggerField
23420  * A combobox control with support for autocomplete, remote-loading, paging and many other features.
23421  * @constructor
23422  * Create a new ComboBox.
23423  * @param {Object} config Configuration options
23424  */
23425 Roo.form.ComboBox = function(config){
23426     Roo.form.ComboBox.superclass.constructor.call(this, config);
23427     this.addEvents({
23428         /**
23429          * @event expand
23430          * Fires when the dropdown list is expanded
23431              * @param {Roo.form.ComboBox} combo This combo box
23432              */
23433         'expand' : true,
23434         /**
23435          * @event collapse
23436          * Fires when the dropdown list is collapsed
23437              * @param {Roo.form.ComboBox} combo This combo box
23438              */
23439         'collapse' : true,
23440         /**
23441          * @event beforeselect
23442          * Fires before a list item is selected. Return false to cancel the selection.
23443              * @param {Roo.form.ComboBox} combo This combo box
23444              * @param {Roo.data.Record} record The data record returned from the underlying store
23445              * @param {Number} index The index of the selected item in the dropdown list
23446              */
23447         'beforeselect' : true,
23448         /**
23449          * @event select
23450          * Fires when a list item is selected
23451              * @param {Roo.form.ComboBox} combo This combo box
23452              * @param {Roo.data.Record} record The data record returned from the underlying store (or false on clear)
23453              * @param {Number} index The index of the selected item in the dropdown list
23454              */
23455         'select' : true,
23456         /**
23457          * @event beforequery
23458          * Fires before all queries are processed. Return false to cancel the query or set cancel to true.
23459          * The event object passed has these properties:
23460              * @param {Roo.form.ComboBox} combo This combo box
23461              * @param {String} query The query
23462              * @param {Boolean} forceAll true to force "all" query
23463              * @param {Boolean} cancel true to cancel the query
23464              * @param {Object} e The query event object
23465              */
23466         'beforequery': true,
23467          /**
23468          * @event add
23469          * Fires when the 'add' icon is pressed (add a listener to enable add button)
23470              * @param {Roo.form.ComboBox} combo This combo box
23471              */
23472         'add' : true,
23473         /**
23474          * @event edit
23475          * Fires when the 'edit' icon is pressed (add a listener to enable add button)
23476              * @param {Roo.form.ComboBox} combo This combo box
23477              * @param {Roo.data.Record|false} record The data record returned from the underlying store (or false on nothing selected)
23478              */
23479         'edit' : true
23480         
23481         
23482     });
23483     if(this.transform){
23484         this.allowDomMove = false;
23485         var s = Roo.getDom(this.transform);
23486         if(!this.hiddenName){
23487             this.hiddenName = s.name;
23488         }
23489         if(!this.store){
23490             this.mode = 'local';
23491             var d = [], opts = s.options;
23492             for(var i = 0, len = opts.length;i < len; i++){
23493                 var o = opts[i];
23494                 var value = (Roo.isIE ? o.getAttributeNode('value').specified : o.hasAttribute('value')) ? o.value : o.text;
23495                 if(o.selected) {
23496                     this.value = value;
23497                 }
23498                 d.push([value, o.text]);
23499             }
23500             this.store = new Roo.data.SimpleStore({
23501                 'id': 0,
23502                 fields: ['value', 'text'],
23503                 data : d
23504             });
23505             this.valueField = 'value';
23506             this.displayField = 'text';
23507         }
23508         s.name = Roo.id(); // wipe out the name in case somewhere else they have a reference
23509         if(!this.lazyRender){
23510             this.target = true;
23511             this.el = Roo.DomHelper.insertBefore(s, this.autoCreate || this.defaultAutoCreate);
23512             s.parentNode.removeChild(s); // remove it
23513             this.render(this.el.parentNode);
23514         }else{
23515             s.parentNode.removeChild(s); // remove it
23516         }
23517
23518     }
23519     if (this.store) {
23520         this.store = Roo.factory(this.store, Roo.data);
23521     }
23522     
23523     this.selectedIndex = -1;
23524     if(this.mode == 'local'){
23525         if(config.queryDelay === undefined){
23526             this.queryDelay = 10;
23527         }
23528         if(config.minChars === undefined){
23529             this.minChars = 0;
23530         }
23531     }
23532 };
23533
23534 Roo.extend(Roo.form.ComboBox, Roo.form.TriggerField, {
23535     /**
23536      * @cfg {String/HTMLElement/Element} transform The id, DOM node or element of an existing select to convert to a ComboBox
23537      */
23538     /**
23539      * @cfg {Boolean} lazyRender True to prevent the ComboBox from rendering until requested (should always be used when
23540      * rendering into an Roo.Editor, defaults to false)
23541      */
23542     /**
23543      * @cfg {Boolean/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to:
23544      * {tag: "input", type: "text", size: "24", autocomplete: "off"})
23545      */
23546     /**
23547      * @cfg {Roo.data.Store} store The data store to which this combo is bound (defaults to undefined)
23548      */
23549     /**
23550      * @cfg {String} title If supplied, a header element is created containing this text and added into the top of
23551      * the dropdown list (defaults to undefined, with no header element)
23552      */
23553
23554      /**
23555      * @cfg {String/Roo.Template} tpl The template to use to render the output
23556      */
23557      
23558     // private
23559     defaultAutoCreate : {tag: "input", type: "text", size: "24", autocomplete: "off"},
23560     /**
23561      * @cfg {Number} listWidth The width in pixels of the dropdown list (defaults to the width of the ComboBox field)
23562      */
23563     listWidth: undefined,
23564     /**
23565      * @cfg {String} displayField The underlying data field name to bind to this CombBox (defaults to undefined if
23566      * mode = 'remote' or 'text' if mode = 'local')
23567      */
23568     displayField: undefined,
23569     /**
23570      * @cfg {String} valueField The underlying data value name to bind to this CombBox (defaults to undefined if
23571      * mode = 'remote' or 'value' if mode = 'local'). 
23572      * Note: use of a valueField requires the user make a selection
23573      * in order for a value to be mapped.
23574      */
23575     valueField: undefined,
23576     
23577     
23578     /**
23579      * @cfg {String} hiddenName If specified, a hidden form field with this name is dynamically generated to store the
23580      * field's data value (defaults to the underlying DOM element's name)
23581      */
23582     hiddenName: undefined,
23583     /**
23584      * @cfg {String} listClass CSS class to apply to the dropdown list element (defaults to '')
23585      */
23586     listClass: '',
23587     /**
23588      * @cfg {String} selectedClass CSS class to apply to the selected item in the dropdown list (defaults to 'x-combo-selected')
23589      */
23590     selectedClass: 'x-combo-selected',
23591     /**
23592      * @cfg {String} triggerClass An additional CSS class used to style the trigger button.  The trigger will always get the
23593      * class 'x-form-trigger' and triggerClass will be <b>appended</b> if specified (defaults to 'x-form-arrow-trigger'
23594      * which displays a downward arrow icon).
23595      */
23596     triggerClass : 'x-form-arrow-trigger',
23597     /**
23598      * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
23599      */
23600     shadow:'sides',
23601     /**
23602      * @cfg {String} listAlign A valid anchor position value. See {@link Roo.Element#alignTo} for details on supported
23603      * anchor positions (defaults to 'tl-bl')
23604      */
23605     listAlign: 'tl-bl?',
23606     /**
23607      * @cfg {Number} maxHeight The maximum height in pixels of the dropdown list before scrollbars are shown (defaults to 300)
23608      */
23609     maxHeight: 300,
23610     /**
23611      * @cfg {String} triggerAction The action to execute when the trigger field is activated.  Use 'all' to run the
23612      * query specified by the allQuery config option (defaults to 'query')
23613      */
23614     triggerAction: 'query',
23615     /**
23616      * @cfg {Number} minChars The minimum number of characters the user must type before autocomplete and typeahead activate
23617      * (defaults to 4, does not apply if editable = false)
23618      */
23619     minChars : 4,
23620     /**
23621      * @cfg {Boolean} typeAhead True to populate and autoselect the remainder of the text being typed after a configurable
23622      * delay (typeAheadDelay) if it matches a known value (defaults to false)
23623      */
23624     typeAhead: false,
23625     /**
23626      * @cfg {Number} queryDelay The length of time in milliseconds to delay between the start of typing and sending the
23627      * query to filter the dropdown list (defaults to 500 if mode = 'remote' or 10 if mode = 'local')
23628      */
23629     queryDelay: 500,
23630     /**
23631      * @cfg {Number} pageSize If greater than 0, a paging toolbar is displayed in the footer of the dropdown list and the
23632      * filter queries will execute with page start and limit parameters.  Only applies when mode = 'remote' (defaults to 0)
23633      */
23634     pageSize: 0,
23635     /**
23636      * @cfg {Boolean} selectOnFocus True to select any existing text in the field immediately on focus.  Only applies
23637      * when editable = true (defaults to false)
23638      */
23639     selectOnFocus:false,
23640     /**
23641      * @cfg {String} queryParam Name of the query as it will be passed on the querystring (defaults to 'query')
23642      */
23643     queryParam: 'query',
23644     /**
23645      * @cfg {String} loadingText The text to display in the dropdown list while data is loading.  Only applies
23646      * when mode = 'remote' (defaults to 'Loading...')
23647      */
23648     loadingText: 'Loading...',
23649     /**
23650      * @cfg {Boolean} resizable True to add a resize handle to the bottom of the dropdown list (defaults to false)
23651      */
23652     resizable: false,
23653     /**
23654      * @cfg {Number} handleHeight The height in pixels of the dropdown list resize handle if resizable = true (defaults to 8)
23655      */
23656     handleHeight : 8,
23657     /**
23658      * @cfg {Boolean} editable False to prevent the user from typing text directly into the field, just like a
23659      * traditional select (defaults to true)
23660      */
23661     editable: true,
23662     /**
23663      * @cfg {String} allQuery The text query to send to the server to return all records for the list with no filtering (defaults to '')
23664      */
23665     allQuery: '',
23666     /**
23667      * @cfg {String} mode Set to 'local' if the ComboBox loads local data (defaults to 'remote' which loads from the server)
23668      */
23669     mode: 'remote',
23670     /**
23671      * @cfg {Number} minListWidth The minimum width of the dropdown list in pixels (defaults to 70, will be ignored if
23672      * listWidth has a higher value)
23673      */
23674     minListWidth : 70,
23675     /**
23676      * @cfg {Boolean} forceSelection True to restrict the selected value to one of the values in the list, false to
23677      * allow the user to set arbitrary text into the field (defaults to false)
23678      */
23679     forceSelection:false,
23680     /**
23681      * @cfg {Number} typeAheadDelay The length of time in milliseconds to wait until the typeahead text is displayed
23682      * if typeAhead = true (defaults to 250)
23683      */
23684     typeAheadDelay : 250,
23685     /**
23686      * @cfg {String} valueNotFoundText When using a name/value combo, if the value passed to setValue is not found in
23687      * the store, valueNotFoundText will be displayed as the field text if defined (defaults to undefined)
23688      */
23689     valueNotFoundText : undefined,
23690     /**
23691      * @cfg {Boolean} blockFocus Prevents all focus calls, so it can work with things like HTML edtor bar
23692      */
23693     blockFocus : false,
23694     
23695     /**
23696      * @cfg {Boolean} disableClear Disable showing of clear button.
23697      */
23698     disableClear : false,
23699     /**
23700      * @cfg {Boolean} alwaysQuery  Disable caching of results, and always send query
23701      */
23702     alwaysQuery : false,
23703     
23704     //private
23705     addicon : false,
23706     editicon: false,
23707     
23708     // element that contains real text value.. (when hidden is used..)
23709      
23710     // private
23711     onRender : function(ct, position){
23712         Roo.form.ComboBox.superclass.onRender.call(this, ct, position);
23713         if(this.hiddenName){
23714             this.hiddenField = this.el.insertSibling({tag:'input', type:'hidden', name: this.hiddenName, id:  (this.hiddenId||this.hiddenName)},
23715                     'before', true);
23716             this.hiddenField.value =
23717                 this.hiddenValue !== undefined ? this.hiddenValue :
23718                 this.value !== undefined ? this.value : '';
23719
23720             // prevent input submission
23721             this.el.dom.removeAttribute('name');
23722              
23723              
23724         }
23725         if(Roo.isGecko){
23726             this.el.dom.setAttribute('autocomplete', 'off');
23727         }
23728
23729         var cls = 'x-combo-list';
23730
23731         this.list = new Roo.Layer({
23732             shadow: this.shadow, cls: [cls, this.listClass].join(' '), constrain:false
23733         });
23734
23735         var lw = this.listWidth || Math.max(this.wrap.getWidth(), this.minListWidth);
23736         this.list.setWidth(lw);
23737         this.list.swallowEvent('mousewheel');
23738         this.assetHeight = 0;
23739
23740         if(this.title){
23741             this.header = this.list.createChild({cls:cls+'-hd', html: this.title});
23742             this.assetHeight += this.header.getHeight();
23743         }
23744
23745         this.innerList = this.list.createChild({cls:cls+'-inner'});
23746         this.innerList.on('mouseover', this.onViewOver, this);
23747         this.innerList.on('mousemove', this.onViewMove, this);
23748         this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
23749         
23750         if(this.allowBlank && !this.pageSize && !this.disableClear){
23751             this.footer = this.list.createChild({cls:cls+'-ft'});
23752             this.pageTb = new Roo.Toolbar(this.footer);
23753            
23754         }
23755         if(this.pageSize){
23756             this.footer = this.list.createChild({cls:cls+'-ft'});
23757             this.pageTb = new Roo.PagingToolbar(this.footer, this.store,
23758                     {pageSize: this.pageSize});
23759             
23760         }
23761         
23762         if (this.pageTb && this.allowBlank && !this.disableClear) {
23763             var _this = this;
23764             this.pageTb.add(new Roo.Toolbar.Fill(), {
23765                 cls: 'x-btn-icon x-btn-clear',
23766                 text: '&#160;',
23767                 handler: function()
23768                 {
23769                     _this.collapse();
23770                     _this.clearValue();
23771                     _this.onSelect(false, -1);
23772                 }
23773             });
23774         }
23775         if (this.footer) {
23776             this.assetHeight += this.footer.getHeight();
23777         }
23778         
23779
23780         if(!this.tpl){
23781             this.tpl = '<div class="'+cls+'-item">{' + this.displayField + '}</div>';
23782         }
23783
23784         this.view = new Roo.View(this.innerList, this.tpl, {
23785             singleSelect:true, store: this.store, selectedClass: this.selectedClass
23786         });
23787
23788         this.view.on('click', this.onViewClick, this);
23789
23790         this.store.on('beforeload', this.onBeforeLoad, this);
23791         this.store.on('load', this.onLoad, this);
23792         this.store.on('loadexception', this.onLoadException, this);
23793
23794         if(this.resizable){
23795             this.resizer = new Roo.Resizable(this.list,  {
23796                pinned:true, handles:'se'
23797             });
23798             this.resizer.on('resize', function(r, w, h){
23799                 this.maxHeight = h-this.handleHeight-this.list.getFrameWidth('tb')-this.assetHeight;
23800                 this.listWidth = w;
23801                 this.innerList.setWidth(w - this.list.getFrameWidth('lr'));
23802                 this.restrictHeight();
23803             }, this);
23804             this[this.pageSize?'footer':'innerList'].setStyle('margin-bottom', this.handleHeight+'px');
23805         }
23806         if(!this.editable){
23807             this.editable = true;
23808             this.setEditable(false);
23809         }  
23810         
23811         
23812         if (typeof(this.events.add.listeners) != 'undefined') {
23813             
23814             this.addicon = this.wrap.createChild(
23815                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-add' });  
23816        
23817             this.addicon.on('click', function(e) {
23818                 this.fireEvent('add', this);
23819             }, this);
23820         }
23821         if (typeof(this.events.edit.listeners) != 'undefined') {
23822             
23823             this.editicon = this.wrap.createChild(
23824                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-edit' });  
23825             if (this.addicon) {
23826                 this.editicon.setStyle('margin-left', '40px');
23827             }
23828             this.editicon.on('click', function(e) {
23829                 
23830                 // we fire even  if inothing is selected..
23831                 this.fireEvent('edit', this, this.lastData );
23832                 
23833             }, this);
23834         }
23835         
23836         
23837         
23838     },
23839
23840     // private
23841     initEvents : function(){
23842         Roo.form.ComboBox.superclass.initEvents.call(this);
23843
23844         this.keyNav = new Roo.KeyNav(this.el, {
23845             "up" : function(e){
23846                 this.inKeyMode = true;
23847                 this.selectPrev();
23848             },
23849
23850             "down" : function(e){
23851                 if(!this.isExpanded()){
23852                     this.onTriggerClick();
23853                 }else{
23854                     this.inKeyMode = true;
23855                     this.selectNext();
23856                 }
23857             },
23858
23859             "enter" : function(e){
23860                 this.onViewClick();
23861                 //return true;
23862             },
23863
23864             "esc" : function(e){
23865                 this.collapse();
23866             },
23867
23868             "tab" : function(e){
23869                 this.onViewClick(false);
23870                 this.fireEvent("specialkey", this, e);
23871                 return true;
23872             },
23873
23874             scope : this,
23875
23876             doRelay : function(foo, bar, hname){
23877                 if(hname == 'down' || this.scope.isExpanded()){
23878                    return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
23879                 }
23880                 return true;
23881             },
23882
23883             forceKeyDown: true
23884         });
23885         this.queryDelay = Math.max(this.queryDelay || 10,
23886                 this.mode == 'local' ? 10 : 250);
23887         this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
23888         if(this.typeAhead){
23889             this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
23890         }
23891         if(this.editable !== false){
23892             this.el.on("keyup", this.onKeyUp, this);
23893         }
23894         if(this.forceSelection){
23895             this.on('blur', this.doForce, this);
23896         }
23897     },
23898
23899     onDestroy : function(){
23900         if(this.view){
23901             this.view.setStore(null);
23902             this.view.el.removeAllListeners();
23903             this.view.el.remove();
23904             this.view.purgeListeners();
23905         }
23906         if(this.list){
23907             this.list.destroy();
23908         }
23909         if(this.store){
23910             this.store.un('beforeload', this.onBeforeLoad, this);
23911             this.store.un('load', this.onLoad, this);
23912             this.store.un('loadexception', this.onLoadException, this);
23913         }
23914         Roo.form.ComboBox.superclass.onDestroy.call(this);
23915     },
23916
23917     // private
23918     fireKey : function(e){
23919         if(e.isNavKeyPress() && !this.list.isVisible()){
23920             this.fireEvent("specialkey", this, e);
23921         }
23922     },
23923
23924     // private
23925     onResize: function(w, h){
23926         Roo.form.ComboBox.superclass.onResize.apply(this, arguments);
23927         
23928         if(typeof w != 'number'){
23929             // we do not handle it!?!?
23930             return;
23931         }
23932         var tw = this.trigger.getWidth();
23933         tw += this.addicon ? this.addicon.getWidth() : 0;
23934         tw += this.editicon ? this.editicon.getWidth() : 0;
23935         var x = w - tw;
23936         this.el.setWidth( this.adjustWidth('input', x));
23937             
23938         this.trigger.setStyle('left', x+'px');
23939         
23940         if(this.list && this.listWidth === undefined){
23941             var lw = Math.max(x + this.trigger.getWidth(), this.minListWidth);
23942             this.list.setWidth(lw);
23943             this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
23944         }
23945         
23946     
23947         
23948     },
23949
23950     /**
23951      * Allow or prevent the user from directly editing the field text.  If false is passed,
23952      * the user will only be able to select from the items defined in the dropdown list.  This method
23953      * is the runtime equivalent of setting the 'editable' config option at config time.
23954      * @param {Boolean} value True to allow the user to directly edit the field text
23955      */
23956     setEditable : function(value){
23957         if(value == this.editable){
23958             return;
23959         }
23960         this.editable = value;
23961         if(!value){
23962             this.el.dom.setAttribute('readOnly', true);
23963             this.el.on('mousedown', this.onTriggerClick,  this);
23964             this.el.addClass('x-combo-noedit');
23965         }else{
23966             this.el.dom.setAttribute('readOnly', false);
23967             this.el.un('mousedown', this.onTriggerClick,  this);
23968             this.el.removeClass('x-combo-noedit');
23969         }
23970     },
23971
23972     // private
23973     onBeforeLoad : function(){
23974         if(!this.hasFocus){
23975             return;
23976         }
23977         this.innerList.update(this.loadingText ?
23978                '<div class="loading-indicator">'+this.loadingText+'</div>' : '');
23979         this.restrictHeight();
23980         this.selectedIndex = -1;
23981     },
23982
23983     // private
23984     onLoad : function(){
23985         if(!this.hasFocus){
23986             return;
23987         }
23988         if(this.store.getCount() > 0){
23989             this.expand();
23990             this.restrictHeight();
23991             if(this.lastQuery == this.allQuery){
23992                 if(this.editable){
23993                     this.el.dom.select();
23994                 }
23995                 if(!this.selectByValue(this.value, true)){
23996                     this.select(0, true);
23997                 }
23998             }else{
23999                 this.selectNext();
24000                 if(this.typeAhead && this.lastKey != Roo.EventObject.BACKSPACE && this.lastKey != Roo.EventObject.DELETE){
24001                     this.taTask.delay(this.typeAheadDelay);
24002                 }
24003             }
24004         }else{
24005             this.onEmptyResults();
24006         }
24007         //this.el.focus();
24008     },
24009     // private
24010     onLoadException : function()
24011     {
24012         this.collapse();
24013         Roo.log(this.store.reader.jsonData);
24014         if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
24015             Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
24016         }
24017         
24018         
24019     },
24020     // private
24021     onTypeAhead : function(){
24022         if(this.store.getCount() > 0){
24023             var r = this.store.getAt(0);
24024             var newValue = r.data[this.displayField];
24025             var len = newValue.length;
24026             var selStart = this.getRawValue().length;
24027             if(selStart != len){
24028                 this.setRawValue(newValue);
24029                 this.selectText(selStart, newValue.length);
24030             }
24031         }
24032     },
24033
24034     // private
24035     onSelect : function(record, index){
24036         if(this.fireEvent('beforeselect', this, record, index) !== false){
24037             this.setFromData(index > -1 ? record.data : false);
24038             this.collapse();
24039             this.fireEvent('select', this, record, index);
24040         }
24041     },
24042
24043     /**
24044      * Returns the currently selected field value or empty string if no value is set.
24045      * @return {String} value The selected value
24046      */
24047     getValue : function(){
24048         if(this.valueField){
24049             return typeof this.value != 'undefined' ? this.value : '';
24050         }else{
24051             return Roo.form.ComboBox.superclass.getValue.call(this);
24052         }
24053     },
24054
24055     /**
24056      * Clears any text/value currently set in the field
24057      */
24058     clearValue : function(){
24059         if(this.hiddenField){
24060             this.hiddenField.value = '';
24061         }
24062         this.value = '';
24063         this.setRawValue('');
24064         this.lastSelectionText = '';
24065         
24066     },
24067
24068     /**
24069      * Sets the specified value into the field.  If the value finds a match, the corresponding record text
24070      * will be displayed in the field.  If the value does not match the data value of an existing item,
24071      * and the valueNotFoundText config option is defined, it will be displayed as the default field text.
24072      * Otherwise the field will be blank (although the value will still be set).
24073      * @param {String} value The value to match
24074      */
24075     setValue : function(v){
24076         var text = v;
24077         if(this.valueField){
24078             var r = this.findRecord(this.valueField, v);
24079             if(r){
24080                 text = r.data[this.displayField];
24081             }else if(this.valueNotFoundText !== undefined){
24082                 text = this.valueNotFoundText;
24083             }
24084         }
24085         this.lastSelectionText = text;
24086         if(this.hiddenField){
24087             this.hiddenField.value = v;
24088         }
24089         Roo.form.ComboBox.superclass.setValue.call(this, text);
24090         this.value = v;
24091     },
24092     /**
24093      * @property {Object} the last set data for the element
24094      */
24095     
24096     lastData : false,
24097     /**
24098      * Sets the value of the field based on a object which is related to the record format for the store.
24099      * @param {Object} value the value to set as. or false on reset?
24100      */
24101     setFromData : function(o){
24102         var dv = ''; // display value
24103         var vv = ''; // value value..
24104         this.lastData = o;
24105         if (this.displayField) {
24106             dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
24107         } else {
24108             // this is an error condition!!!
24109             Roo.log('no  displayField value set for '+ (this.name ? this.name : this.id));
24110         }
24111         
24112         if(this.valueField){
24113             vv = !o || typeof(o[this.valueField]) == 'undefined' ? dv : o[this.valueField];
24114         }
24115         if(this.hiddenField){
24116             this.hiddenField.value = vv;
24117             
24118             this.lastSelectionText = dv;
24119             Roo.form.ComboBox.superclass.setValue.call(this, dv);
24120             this.value = vv;
24121             return;
24122         }
24123         // no hidden field.. - we store the value in 'value', but still display
24124         // display field!!!!
24125         this.lastSelectionText = dv;
24126         Roo.form.ComboBox.superclass.setValue.call(this, dv);
24127         this.value = vv;
24128         
24129         
24130     },
24131     // private
24132     reset : function(){
24133         // overridden so that last data is reset..
24134         this.setValue(this.originalValue);
24135         this.clearInvalid();
24136         this.lastData = false;
24137         if (this.view) {
24138             this.view.clearSelections();
24139         }
24140     },
24141     // private
24142     findRecord : function(prop, value){
24143         var record;
24144         if(this.store.getCount() > 0){
24145             this.store.each(function(r){
24146                 if(r.data[prop] == value){
24147                     record = r;
24148                     return false;
24149                 }
24150                 return true;
24151             });
24152         }
24153         return record;
24154     },
24155     
24156     getName: function()
24157     {
24158         // returns hidden if it's set..
24159         if (!this.rendered) {return ''};
24160         return !this.hiddenName && this.el.dom.name  ? this.el.dom.name : (this.hiddenName || '');
24161         
24162     },
24163     // private
24164     onViewMove : function(e, t){
24165         this.inKeyMode = false;
24166     },
24167
24168     // private
24169     onViewOver : function(e, t){
24170         if(this.inKeyMode){ // prevent key nav and mouse over conflicts
24171             return;
24172         }
24173         var item = this.view.findItemFromChild(t);
24174         if(item){
24175             var index = this.view.indexOf(item);
24176             this.select(index, false);
24177         }
24178     },
24179
24180     // private
24181     onViewClick : function(doFocus)
24182     {
24183         var index = this.view.getSelectedIndexes()[0];
24184         var r = this.store.getAt(index);
24185         if(r){
24186             this.onSelect(r, index);
24187         }
24188         if(doFocus !== false && !this.blockFocus){
24189             this.el.focus();
24190         }
24191     },
24192
24193     // private
24194     restrictHeight : function(){
24195         this.innerList.dom.style.height = '';
24196         var inner = this.innerList.dom;
24197         var h = Math.max(inner.clientHeight, inner.offsetHeight, inner.scrollHeight);
24198         this.innerList.setHeight(h < this.maxHeight ? 'auto' : this.maxHeight);
24199         this.list.beginUpdate();
24200         this.list.setHeight(this.innerList.getHeight()+this.list.getFrameWidth('tb')+(this.resizable?this.handleHeight:0)+this.assetHeight);
24201         this.list.alignTo(this.el, this.listAlign);
24202         this.list.endUpdate();
24203     },
24204
24205     // private
24206     onEmptyResults : function(){
24207         this.collapse();
24208     },
24209
24210     /**
24211      * Returns true if the dropdown list is expanded, else false.
24212      */
24213     isExpanded : function(){
24214         return this.list.isVisible();
24215     },
24216
24217     /**
24218      * Select an item in the dropdown list by its data value. This function does NOT cause the select event to fire.
24219      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
24220      * @param {String} value The data value of the item to select
24221      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
24222      * selected item if it is not currently in view (defaults to true)
24223      * @return {Boolean} True if the value matched an item in the list, else false
24224      */
24225     selectByValue : function(v, scrollIntoView){
24226         if(v !== undefined && v !== null){
24227             var r = this.findRecord(this.valueField || this.displayField, v);
24228             if(r){
24229                 this.select(this.store.indexOf(r), scrollIntoView);
24230                 return true;
24231             }
24232         }
24233         return false;
24234     },
24235
24236     /**
24237      * Select an item in the dropdown list by its numeric index in the list. This function does NOT cause the select event to fire.
24238      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
24239      * @param {Number} index The zero-based index of the list item to select
24240      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
24241      * selected item if it is not currently in view (defaults to true)
24242      */
24243     select : function(index, scrollIntoView){
24244         this.selectedIndex = index;
24245         this.view.select(index);
24246         if(scrollIntoView !== false){
24247             var el = this.view.getNode(index);
24248             if(el){
24249                 this.innerList.scrollChildIntoView(el, false);
24250             }
24251         }
24252     },
24253
24254     // private
24255     selectNext : function(){
24256         var ct = this.store.getCount();
24257         if(ct > 0){
24258             if(this.selectedIndex == -1){
24259                 this.select(0);
24260             }else if(this.selectedIndex < ct-1){
24261                 this.select(this.selectedIndex+1);
24262             }
24263         }
24264     },
24265
24266     // private
24267     selectPrev : function(){
24268         var ct = this.store.getCount();
24269         if(ct > 0){
24270             if(this.selectedIndex == -1){
24271                 this.select(0);
24272             }else if(this.selectedIndex != 0){
24273                 this.select(this.selectedIndex-1);
24274             }
24275         }
24276     },
24277
24278     // private
24279     onKeyUp : function(e){
24280         if(this.editable !== false && !e.isSpecialKey()){
24281             this.lastKey = e.getKey();
24282             this.dqTask.delay(this.queryDelay);
24283         }
24284     },
24285
24286     // private
24287     validateBlur : function(){
24288         return !this.list || !this.list.isVisible();   
24289     },
24290
24291     // private
24292     initQuery : function(){
24293         this.doQuery(this.getRawValue());
24294     },
24295
24296     // private
24297     doForce : function(){
24298         if(this.el.dom.value.length > 0){
24299             this.el.dom.value =
24300                 this.lastSelectionText === undefined ? '' : this.lastSelectionText;
24301              
24302         }
24303     },
24304
24305     /**
24306      * Execute a query to filter the dropdown list.  Fires the beforequery event prior to performing the
24307      * query allowing the query action to be canceled if needed.
24308      * @param {String} query The SQL query to execute
24309      * @param {Boolean} forceAll True to force the query to execute even if there are currently fewer characters
24310      * in the field than the minimum specified by the minChars config option.  It also clears any filter previously
24311      * saved in the current store (defaults to false)
24312      */
24313     doQuery : function(q, forceAll){
24314         if(q === undefined || q === null){
24315             q = '';
24316         }
24317         var qe = {
24318             query: q,
24319             forceAll: forceAll,
24320             combo: this,
24321             cancel:false
24322         };
24323         if(this.fireEvent('beforequery', qe)===false || qe.cancel){
24324             return false;
24325         }
24326         q = qe.query;
24327         forceAll = qe.forceAll;
24328         if(forceAll === true || (q.length >= this.minChars)){
24329             if(this.lastQuery != q || this.alwaysQuery){
24330                 this.lastQuery = q;
24331                 if(this.mode == 'local'){
24332                     this.selectedIndex = -1;
24333                     if(forceAll){
24334                         this.store.clearFilter();
24335                     }else{
24336                         this.store.filter(this.displayField, q);
24337                     }
24338                     this.onLoad();
24339                 }else{
24340                     this.store.baseParams[this.queryParam] = q;
24341                     this.store.load({
24342                         params: this.getParams(q)
24343                     });
24344                     this.expand();
24345                 }
24346             }else{
24347                 this.selectedIndex = -1;
24348                 this.onLoad();   
24349             }
24350         }
24351     },
24352
24353     // private
24354     getParams : function(q){
24355         var p = {};
24356         //p[this.queryParam] = q;
24357         if(this.pageSize){
24358             p.start = 0;
24359             p.limit = this.pageSize;
24360         }
24361         return p;
24362     },
24363
24364     /**
24365      * Hides the dropdown list if it is currently expanded. Fires the 'collapse' event on completion.
24366      */
24367     collapse : function(){
24368         if(!this.isExpanded()){
24369             return;
24370         }
24371         this.list.hide();
24372         Roo.get(document).un('mousedown', this.collapseIf, this);
24373         Roo.get(document).un('mousewheel', this.collapseIf, this);
24374         if (!this.editable) {
24375             Roo.get(document).un('keydown', this.listKeyPress, this);
24376         }
24377         this.fireEvent('collapse', this);
24378     },
24379
24380     // private
24381     collapseIf : function(e){
24382         if(!e.within(this.wrap) && !e.within(this.list)){
24383             this.collapse();
24384         }
24385     },
24386
24387     /**
24388      * Expands the dropdown list if it is currently hidden. Fires the 'expand' event on completion.
24389      */
24390     expand : function(){
24391         if(this.isExpanded() || !this.hasFocus){
24392             return;
24393         }
24394         this.list.alignTo(this.el, this.listAlign);
24395         this.list.show();
24396         Roo.get(document).on('mousedown', this.collapseIf, this);
24397         Roo.get(document).on('mousewheel', this.collapseIf, this);
24398         if (!this.editable) {
24399             Roo.get(document).on('keydown', this.listKeyPress, this);
24400         }
24401         
24402         this.fireEvent('expand', this);
24403     },
24404
24405     // private
24406     // Implements the default empty TriggerField.onTriggerClick function
24407     onTriggerClick : function(){
24408         if(this.disabled){
24409             return;
24410         }
24411         if(this.isExpanded()){
24412             this.collapse();
24413             if (!this.blockFocus) {
24414                 this.el.focus();
24415             }
24416             
24417         }else {
24418             this.hasFocus = true;
24419             if(this.triggerAction == 'all') {
24420                 this.doQuery(this.allQuery, true);
24421             } else {
24422                 this.doQuery(this.getRawValue());
24423             }
24424             if (!this.blockFocus) {
24425                 this.el.focus();
24426             }
24427         }
24428     },
24429     listKeyPress : function(e)
24430     {
24431         //Roo.log('listkeypress');
24432         // scroll to first matching element based on key pres..
24433         if (e.isSpecialKey()) {
24434             return false;
24435         }
24436         var k = String.fromCharCode(e.getKey()).toUpperCase();
24437         //Roo.log(k);
24438         var match  = false;
24439         var csel = this.view.getSelectedNodes();
24440         var cselitem = false;
24441         if (csel.length) {
24442             var ix = this.view.indexOf(csel[0]);
24443             cselitem  = this.store.getAt(ix);
24444             if (!cselitem.get(this.displayField) || cselitem.get(this.displayField).substring(0,1).toUpperCase() != k) {
24445                 cselitem = false;
24446             }
24447             
24448         }
24449         
24450         this.store.each(function(v) { 
24451             if (cselitem) {
24452                 // start at existing selection.
24453                 if (cselitem.id == v.id) {
24454                     cselitem = false;
24455                 }
24456                 return;
24457             }
24458                 
24459             if (v.get(this.displayField) && v.get(this.displayField).substring(0,1).toUpperCase() == k) {
24460                 match = this.store.indexOf(v);
24461                 return false;
24462             }
24463         }, this);
24464         
24465         if (match === false) {
24466             return true; // no more action?
24467         }
24468         // scroll to?
24469         this.view.select(match);
24470         var sn = Roo.get(this.view.getSelectedNodes()[0])
24471         sn.scrollIntoView(sn.dom.parentNode, false);
24472     }
24473
24474     /** 
24475     * @cfg {Boolean} grow 
24476     * @hide 
24477     */
24478     /** 
24479     * @cfg {Number} growMin 
24480     * @hide 
24481     */
24482     /** 
24483     * @cfg {Number} growMax 
24484     * @hide 
24485     */
24486     /**
24487      * @hide
24488      * @method autoSize
24489      */
24490 });/*
24491  * Copyright(c) 2010-2012, Roo J Solutions Limited
24492  *
24493  * Licence LGPL
24494  *
24495  */
24496
24497 /**
24498  * @class Roo.form.ComboBoxArray
24499  * @extends Roo.form.TextField
24500  * A facebook style adder... for lists of email / people / countries  etc...
24501  * pick multiple items from a combo box, and shows each one.
24502  *
24503  *  Fred [x]  Brian [x]  [Pick another |v]
24504  *
24505  *
24506  *  For this to work: it needs various extra information
24507  *    - normal combo problay has
24508  *      name, hiddenName
24509  *    + displayField, valueField
24510  *
24511  *    For our purpose...
24512  *
24513  *
24514  *   If we change from 'extends' to wrapping...
24515  *   
24516  *  
24517  *
24518  
24519  
24520  * @constructor
24521  * Create a new ComboBoxArray.
24522  * @param {Object} config Configuration options
24523  */
24524  
24525
24526 Roo.form.ComboBoxArray = function(config)
24527 {
24528     
24529     Roo.form.ComboBoxArray.superclass.constructor.call(this, config);
24530     
24531     this.items = new Roo.util.MixedCollection(false);
24532     
24533     // construct the child combo...
24534     
24535     
24536     
24537     
24538    
24539     
24540 }
24541
24542  
24543 Roo.extend(Roo.form.ComboBoxArray, Roo.form.TextField,
24544
24545     /**
24546      * @cfg {Roo.form.Combo} combo The combo box that is wrapped
24547      */
24548     
24549     lastData : false,
24550     
24551     // behavies liek a hiddne field
24552     inputType:      'hidden',
24553     /**
24554      * @cfg {Number} width The width of the box that displays the selected element
24555      */ 
24556     width:          300,
24557
24558     
24559     
24560     /**
24561      * @cfg {String} name    The name of the visable items on this form (eg. titles not ids)
24562      */
24563     name : false,
24564     /**
24565      * @cfg {String} hiddenName    The hidden name of the field, often contains an comma seperated list of names
24566      */
24567     hiddenName : false,
24568     
24569     
24570     // private the array of items that are displayed..
24571     items  : false,
24572     // private - the hidden field el.
24573     hiddenEl : false,
24574     // private - the filed el..
24575     el : false,
24576     
24577     //validateValue : function() { return true; }, // all values are ok!
24578     //onAddClick: function() { },
24579     
24580     onRender : function(ct, position) 
24581     {
24582         
24583         // create the standard hidden element
24584         //Roo.form.ComboBoxArray.superclass.onRender.call(this, ct, position);
24585         
24586         
24587         // give fake names to child combo;
24588         this.combo.hiddenName = this.hiddenName ? (this.hiddenName+'-subcombo') : this.hiddenName;
24589         this.combo.name = this.name? (this.name+'-subcombo') : this.name;
24590         
24591         this.combo = Roo.factory(this.combo, Roo.form);
24592         this.combo.onRender(ct, position);
24593         if (typeof(this.combo.width) != 'undefined') {
24594             this.combo.onResize(this.combo.width,0);
24595         }
24596         
24597         this.combo.initEvents();
24598         
24599         // assigned so form know we need to do this..
24600         this.store          = this.combo.store;
24601         this.valueField     = this.combo.valueField;
24602         this.displayField   = this.combo.displayField ;
24603         
24604         
24605         this.combo.wrap.addClass('x-cbarray-grp');
24606         
24607         var cbwrap = this.combo.wrap.createChild(
24608             {tag: 'div', cls: 'x-cbarray-cb'},
24609             this.combo.el.dom
24610         );
24611         
24612              
24613         this.hiddenEl = this.combo.wrap.createChild({
24614             tag: 'input',  type:'hidden' , name: this.hiddenName, value : ''
24615         });
24616         this.el = this.combo.wrap.createChild({
24617             tag: 'input',  type:'hidden' , name: this.name, value : ''
24618         });
24619          //   this.el.dom.removeAttribute("name");
24620         
24621         
24622         this.outerWrap = this.combo.wrap;
24623         this.wrap = cbwrap;
24624         
24625         this.outerWrap.setWidth(this.width);
24626         this.outerWrap.dom.removeChild(this.el.dom);
24627         
24628         this.wrap.dom.appendChild(this.el.dom);
24629         this.outerWrap.dom.removeChild(this.combo.trigger.dom);
24630         this.combo.wrap.dom.appendChild(this.combo.trigger.dom);
24631         
24632         this.combo.trigger.setStyle('position','relative');
24633         this.combo.trigger.setStyle('left', '0px');
24634         this.combo.trigger.setStyle('top', '2px');
24635         
24636         this.combo.el.setStyle('vertical-align', 'text-bottom');
24637         
24638         //this.trigger.setStyle('vertical-align', 'top');
24639         
24640         // this should use the code from combo really... on('add' ....)
24641         if (this.adder) {
24642             
24643         
24644             this.adder = this.outerWrap.createChild(
24645                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-adder', style: 'margin-left:2px'});  
24646             var _t = this;
24647             this.adder.on('click', function(e) {
24648                 _t.fireEvent('adderclick', this, e);
24649             }, _t);
24650         }
24651         //var _t = this;
24652         //this.adder.on('click', this.onAddClick, _t);
24653         
24654         
24655         this.combo.on('select', function(cb, rec, ix) {
24656             this.addItem(rec.data);
24657             
24658             cb.setValue('');
24659             cb.el.dom.value = '';
24660             //cb.lastData = rec.data;
24661             // add to list
24662             
24663         }, this);
24664         
24665         
24666     },
24667     
24668     
24669     getName: function()
24670     {
24671         // returns hidden if it's set..
24672         if (!this.rendered) {return ''};
24673         return  this.hiddenName ? this.hiddenName : this.name;
24674         
24675     },
24676     
24677     
24678     onResize: function(w, h){
24679         
24680         return;
24681         // not sure if this is needed..
24682         //this.combo.onResize(w,h);
24683         
24684         if(typeof w != 'number'){
24685             // we do not handle it!?!?
24686             return;
24687         }
24688         var tw = this.combo.trigger.getWidth();
24689         tw += this.addicon ? this.addicon.getWidth() : 0;
24690         tw += this.editicon ? this.editicon.getWidth() : 0;
24691         var x = w - tw;
24692         this.combo.el.setWidth( this.combo.adjustWidth('input', x));
24693             
24694         this.combo.trigger.setStyle('left', '0px');
24695         
24696         if(this.list && this.listWidth === undefined){
24697             var lw = Math.max(x + this.combo.trigger.getWidth(), this.combo.minListWidth);
24698             this.list.setWidth(lw);
24699             this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
24700         }
24701         
24702     
24703         
24704     },
24705     
24706     addItem: function(rec)
24707     {
24708         var valueField = this.combo.valueField;
24709         var displayField = this.combo.displayField;
24710         if (this.items.indexOfKey(rec[valueField]) > -1) {
24711             //console.log("GOT " + rec.data.id);
24712             return;
24713         }
24714         
24715         var x = new Roo.form.ComboBoxArray.Item({
24716             //id : rec[this.idField],
24717             data : rec,
24718             displayField : displayField ,
24719             tipField : displayField ,
24720             cb : this
24721         });
24722         // use the 
24723         this.items.add(rec[valueField],x);
24724         // add it before the element..
24725         this.updateHiddenEl();
24726         x.render(this.outerWrap, this.wrap.dom);
24727         // add the image handler..
24728     },
24729     
24730     updateHiddenEl : function()
24731     {
24732         this.validate();
24733         if (!this.hiddenEl) {
24734             return;
24735         }
24736         var ar = [];
24737         var idField = this.combo.valueField;
24738         
24739         this.items.each(function(f) {
24740             ar.push(f.data[idField]);
24741            
24742         });
24743         this.hiddenEl.dom.value = ar.join(',');
24744         this.validate();
24745     },
24746     
24747     reset : function()
24748     {
24749         //Roo.form.ComboBoxArray.superclass.reset.call(this); 
24750         this.items.each(function(f) {
24751            f.remove(); 
24752         });
24753         this.el.dom.value = '';
24754         if (this.hiddenEl) {
24755             this.hiddenEl.dom.value = '';
24756         }
24757         
24758     },
24759     getValue: function()
24760     {
24761         return this.hiddenEl ? this.hiddenEl.dom.value : '';
24762     },
24763     setValue: function(v) // not a valid action - must use addItems..
24764     {
24765          
24766         this.reset();
24767         
24768         
24769         
24770         if (this.store.isLocal && (typeof(v) == 'string')) {
24771             // then we can use the store to find the values..
24772             // comma seperated at present.. this needs to allow JSON based encoding..
24773             this.hiddenEl.value  = v;
24774             var v_ar = [];
24775             Roo.each(v.split(','), function(k) {
24776                 Roo.log("CHECK " + this.valueField + ',' + k);
24777                 var li = this.store.query(this.valueField, k);
24778                 if (!li.length) {
24779                     return;
24780                 }
24781                 var add = {};
24782                 add[this.valueField] = k;
24783                 add[this.displayField] = li.item(0).data[this.displayField];
24784                 
24785                 this.addItem(add);
24786             }, this) 
24787              
24788         }
24789         if (typeof(v) == 'object') {
24790             // then let's assume it's an array of objects..
24791             Roo.each(v, function(l) {
24792                 this.addItem(l);
24793             }, this);
24794              
24795         }
24796         
24797         
24798     },
24799     setFromData: function(v)
24800     {
24801         // this recieves an object, if setValues is called.
24802         this.reset();
24803         this.el.dom.value = v[this.displayField];
24804         this.hiddenEl.dom.value = v[this.valueField];
24805         if (typeof(v[this.valueField]) != 'string' || !v[this.valueField].length) {
24806             return;
24807         }
24808         var kv = v[this.valueField];
24809         var dv = v[this.displayField];
24810         kv = typeof(kv) != 'string' ? '' : kv;
24811         dv = typeof(dv) != 'string' ? '' : dv;
24812         
24813         
24814         var keys = kv.split(',');
24815         var display = dv.split(',');
24816         for (var i = 0 ; i < keys.length; i++) {
24817             
24818             add = {};
24819             add[this.valueField] = keys[i];
24820             add[this.displayField] = display[i];
24821             this.addItem(add);
24822         }
24823       
24824         
24825     },
24826     
24827     
24828     validateValue : function(value){
24829         return Roo.form.ComboBoxArray.superclass.validateValue.call(this, this.getValue());
24830         
24831     }
24832     
24833 });
24834
24835
24836
24837 /**
24838  * @class Roo.form.ComboBoxArray.Item
24839  * @extends Roo.BoxComponent
24840  * A selected item in the list
24841  *  Fred [x]  Brian [x]  [Pick another |v]
24842  * 
24843  * @constructor
24844  * Create a new item.
24845  * @param {Object} config Configuration options
24846  */
24847  
24848 Roo.form.ComboBoxArray.Item = function(config) {
24849     config.id = Roo.id();
24850     Roo.form.ComboBoxArray.Item.superclass.constructor.call(this, config);
24851 }
24852
24853 Roo.extend(Roo.form.ComboBoxArray.Item, Roo.BoxComponent, {
24854     data : {},
24855     cb: false,
24856     displayField : false,
24857     tipField : false,
24858     
24859     
24860     defaultAutoCreate : {
24861         tag: 'div',
24862         cls: 'x-cbarray-item',
24863         cn : [ 
24864             { tag: 'div' },
24865             {
24866                 tag: 'img',
24867                 width:16,
24868                 height : 16,
24869                 src : Roo.BLANK_IMAGE_URL ,
24870                 align: 'center'
24871             }
24872         ]
24873         
24874     },
24875     
24876  
24877     onRender : function(ct, position)
24878     {
24879         Roo.form.Field.superclass.onRender.call(this, ct, position);
24880         
24881         if(!this.el){
24882             var cfg = this.getAutoCreate();
24883             this.el = ct.createChild(cfg, position);
24884         }
24885         
24886         this.el.child('img').dom.setAttribute('src', Roo.BLANK_IMAGE_URL);
24887         
24888         this.el.child('div').dom.innerHTML = this.cb.renderer ? 
24889             this.cb.renderer(this.data) :
24890             String.format('{0}',this.data[this.displayField]);
24891         
24892             
24893         this.el.child('div').dom.setAttribute('qtip',
24894                         String.format('{0}',this.data[this.tipField])
24895         );
24896         
24897         this.el.child('img').on('click', this.remove, this);
24898         
24899     },
24900    
24901     remove : function()
24902     {
24903         
24904         this.cb.items.remove(this);
24905         this.el.child('img').un('click', this.remove, this);
24906         this.el.remove();
24907         this.cb.updateHiddenEl();
24908     }
24909     
24910     
24911 });/*
24912  * Based on:
24913  * Ext JS Library 1.1.1
24914  * Copyright(c) 2006-2007, Ext JS, LLC.
24915  *
24916  * Originally Released Under LGPL - original licence link has changed is not relivant.
24917  *
24918  * Fork - LGPL
24919  * <script type="text/javascript">
24920  */
24921 /**
24922  * @class Roo.form.Checkbox
24923  * @extends Roo.form.Field
24924  * Single checkbox field.  Can be used as a direct replacement for traditional checkbox fields.
24925  * @constructor
24926  * Creates a new Checkbox
24927  * @param {Object} config Configuration options
24928  */
24929 Roo.form.Checkbox = function(config){
24930     Roo.form.Checkbox.superclass.constructor.call(this, config);
24931     this.addEvents({
24932         /**
24933          * @event check
24934          * Fires when the checkbox is checked or unchecked.
24935              * @param {Roo.form.Checkbox} this This checkbox
24936              * @param {Boolean} checked The new checked value
24937              */
24938         check : true
24939     });
24940 };
24941
24942 Roo.extend(Roo.form.Checkbox, Roo.form.Field,  {
24943     /**
24944      * @cfg {String} focusClass The CSS class to use when the checkbox receives focus (defaults to undefined)
24945      */
24946     focusClass : undefined,
24947     /**
24948      * @cfg {String} fieldClass The default CSS class for the checkbox (defaults to "x-form-field")
24949      */
24950     fieldClass: "x-form-field",
24951     /**
24952      * @cfg {Boolean} checked True if the the checkbox should render already checked (defaults to false)
24953      */
24954     checked: false,
24955     /**
24956      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
24957      * {tag: "input", type: "checkbox", autocomplete: "off"})
24958      */
24959     defaultAutoCreate : { tag: "input", type: 'hidden', autocomplete: "off"},
24960     /**
24961      * @cfg {String} boxLabel The text that appears beside the checkbox
24962      */
24963     boxLabel : "",
24964     /**
24965      * @cfg {String} inputValue The value that should go into the generated input element's value attribute
24966      */  
24967     inputValue : '1',
24968     /**
24969      * @cfg {String} valueOff The value that should go into the generated input element's value when unchecked.
24970      */
24971      valueOff: '0', // value when not checked..
24972
24973     actionMode : 'viewEl', 
24974     //
24975     // private
24976     itemCls : 'x-menu-check-item x-form-item',
24977     groupClass : 'x-menu-group-item',
24978     inputType : 'hidden',
24979     
24980     
24981     inSetChecked: false, // check that we are not calling self...
24982     
24983     inputElement: false, // real input element?
24984     basedOn: false, // ????
24985     
24986     isFormField: true, // not sure where this is needed!!!!
24987
24988     onResize : function(){
24989         Roo.form.Checkbox.superclass.onResize.apply(this, arguments);
24990         if(!this.boxLabel){
24991             this.el.alignTo(this.wrap, 'c-c');
24992         }
24993     },
24994
24995     initEvents : function(){
24996         Roo.form.Checkbox.superclass.initEvents.call(this);
24997         this.el.on("click", this.onClick,  this);
24998         this.el.on("change", this.onClick,  this);
24999     },
25000
25001
25002     getResizeEl : function(){
25003         return this.wrap;
25004     },
25005
25006     getPositionEl : function(){
25007         return this.wrap;
25008     },
25009
25010     // private
25011     onRender : function(ct, position){
25012         Roo.form.Checkbox.superclass.onRender.call(this, ct, position);
25013         /*
25014         if(this.inputValue !== undefined){
25015             this.el.dom.value = this.inputValue;
25016         }
25017         */
25018         //this.wrap = this.el.wrap({cls: "x-form-check-wrap"});
25019         this.wrap = this.el.wrap({cls: 'x-menu-check-item '});
25020         var viewEl = this.wrap.createChild({ 
25021             tag: 'img', cls: 'x-menu-item-icon', style: 'margin: 0px;' ,src : Roo.BLANK_IMAGE_URL });
25022         this.viewEl = viewEl;   
25023         this.wrap.on('click', this.onClick,  this); 
25024         
25025         this.el.on('DOMAttrModified', this.setFromHidden,  this); //ff
25026         this.el.on('propertychange', this.setFromHidden,  this);  //ie
25027         
25028         
25029         
25030         if(this.boxLabel){
25031             this.wrap.createChild({tag: 'label', htmlFor: this.el.id, cls: 'x-form-cb-label', html: this.boxLabel});
25032         //    viewEl.on('click', this.onClick,  this); 
25033         }
25034         //if(this.checked){
25035             this.setChecked(this.checked);
25036         //}else{
25037             //this.checked = this.el.dom;
25038         //}
25039
25040     },
25041
25042     // private
25043     initValue : Roo.emptyFn,
25044
25045     /**
25046      * Returns the checked state of the checkbox.
25047      * @return {Boolean} True if checked, else false
25048      */
25049     getValue : function(){
25050         if(this.el){
25051             return String(this.el.dom.value) == String(this.inputValue ) ? this.inputValue : this.valueOff;
25052         }
25053         return this.valueOff;
25054         
25055     },
25056
25057         // private
25058     onClick : function(){ 
25059         this.setChecked(!this.checked);
25060
25061         //if(this.el.dom.checked != this.checked){
25062         //    this.setValue(this.el.dom.checked);
25063        // }
25064     },
25065
25066     /**
25067      * Sets the checked state of the checkbox.
25068      * On is always based on a string comparison between inputValue and the param.
25069      * @param {Boolean/String} value - the value to set 
25070      * @param {Boolean/String} suppressEvent - whether to suppress the checkchange event.
25071      */
25072     setValue : function(v,suppressEvent){
25073         
25074         
25075         //this.checked = (v === true || v === 'true' || v == '1' || String(v).toLowerCase() == 'on');
25076         //if(this.el && this.el.dom){
25077         //    this.el.dom.checked = this.checked;
25078         //    this.el.dom.defaultChecked = this.checked;
25079         //}
25080         this.setChecked(String(v) === String(this.inputValue), suppressEvent);
25081         //this.fireEvent("check", this, this.checked);
25082     },
25083     // private..
25084     setChecked : function(state,suppressEvent)
25085     {
25086         if (this.inSetChecked) {
25087             this.checked = state;
25088             return;
25089         }
25090         
25091     
25092         if(this.wrap){
25093             this.wrap[state ? 'addClass' : 'removeClass']('x-menu-item-checked');
25094         }
25095         this.checked = state;
25096         if(suppressEvent !== true){
25097             this.fireEvent('check', this, state);
25098         }
25099         this.inSetChecked = true;
25100         this.el.dom.value = state ? this.inputValue : this.valueOff;
25101         this.inSetChecked = false;
25102         
25103     },
25104     // handle setting of hidden value by some other method!!?!?
25105     setFromHidden: function()
25106     {
25107         if(!this.el){
25108             return;
25109         }
25110         //console.log("SET FROM HIDDEN");
25111         //alert('setFrom hidden');
25112         this.setValue(this.el.dom.value);
25113     },
25114     
25115     onDestroy : function()
25116     {
25117         if(this.viewEl){
25118             Roo.get(this.viewEl).remove();
25119         }
25120          
25121         Roo.form.Checkbox.superclass.onDestroy.call(this);
25122     }
25123
25124 });/*
25125  * Based on:
25126  * Ext JS Library 1.1.1
25127  * Copyright(c) 2006-2007, Ext JS, LLC.
25128  *
25129  * Originally Released Under LGPL - original licence link has changed is not relivant.
25130  *
25131  * Fork - LGPL
25132  * <script type="text/javascript">
25133  */
25134  
25135 /**
25136  * @class Roo.form.Radio
25137  * @extends Roo.form.Checkbox
25138  * Single radio field.  Same as Checkbox, but provided as a convenience for automatically setting the input type.
25139  * Radio grouping is handled automatically by the browser if you give each radio in a group the same name.
25140  * @constructor
25141  * Creates a new Radio
25142  * @param {Object} config Configuration options
25143  */
25144 Roo.form.Radio = function(){
25145     Roo.form.Radio.superclass.constructor.apply(this, arguments);
25146 };
25147 Roo.extend(Roo.form.Radio, Roo.form.Checkbox, {
25148     inputType: 'radio',
25149
25150     /**
25151      * If this radio is part of a group, it will return the selected value
25152      * @return {String}
25153      */
25154     getGroupValue : function(){
25155         return this.el.up('form').child('input[name='+this.el.dom.name+']:checked', true).value;
25156     }
25157 });//<script type="text/javascript">
25158
25159 /*
25160  * Ext JS Library 1.1.1
25161  * Copyright(c) 2006-2007, Ext JS, LLC.
25162  * licensing@extjs.com
25163  * 
25164  * http://www.extjs.com/license
25165  */
25166  
25167  /*
25168   * 
25169   * Known bugs:
25170   * Default CSS appears to render it as fixed text by default (should really be Sans-Serif)
25171   * - IE ? - no idea how much works there.
25172   * 
25173   * 
25174   * 
25175   */
25176  
25177
25178 /**
25179  * @class Ext.form.HtmlEditor
25180  * @extends Ext.form.Field
25181  * Provides a lightweight HTML Editor component.
25182  *
25183  * This has been tested on Fireforx / Chrome.. IE may not be so great..
25184  * 
25185  * <br><br><b>Note: The focus/blur and validation marking functionality inherited from Ext.form.Field is NOT
25186  * supported by this editor.</b><br/><br/>
25187  * An Editor is a sensitive component that can't be used in all spots standard fields can be used. Putting an Editor within
25188  * any element that has display set to 'none' can cause problems in Safari and Firefox.<br/><br/>
25189  */
25190 Roo.form.HtmlEditor = Roo.extend(Roo.form.Field, {
25191       /**
25192      * @cfg {Array} toolbars Array of toolbars. - defaults to just the Standard one
25193      */
25194     toolbars : false,
25195     /**
25196      * @cfg {String} createLinkText The default text for the create link prompt
25197      */
25198     createLinkText : 'Please enter the URL for the link:',
25199     /**
25200      * @cfg {String} defaultLinkValue The default value for the create link prompt (defaults to http:/ /)
25201      */
25202     defaultLinkValue : 'http:/'+'/',
25203    
25204      /**
25205      * @cfg {String} resizable  's' or 'se' or 'e' - wrapps the element in a
25206      *                        Roo.resizable.
25207      */
25208     resizable : false,
25209      /**
25210      * @cfg {Number} height (in pixels)
25211      */   
25212     height: 300,
25213    /**
25214      * @cfg {Number} width (in pixels)
25215      */   
25216     width: 500,
25217     
25218     /**
25219      * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
25220      * 
25221      */
25222     stylesheets: false,
25223     
25224     // id of frame..
25225     frameId: false,
25226     
25227     // private properties
25228     validationEvent : false,
25229     deferHeight: true,
25230     initialized : false,
25231     activated : false,
25232     sourceEditMode : false,
25233     onFocus : Roo.emptyFn,
25234     iframePad:3,
25235     hideMode:'offsets',
25236     
25237     defaultAutoCreate : { // modified by initCompnoent..
25238         tag: "textarea",
25239         style:"width:500px;height:300px;",
25240         autocomplete: "off"
25241     },
25242
25243     // private
25244     initComponent : function(){
25245         this.addEvents({
25246             /**
25247              * @event initialize
25248              * Fires when the editor is fully initialized (including the iframe)
25249              * @param {HtmlEditor} this
25250              */
25251             initialize: true,
25252             /**
25253              * @event activate
25254              * Fires when the editor is first receives the focus. Any insertion must wait
25255              * until after this event.
25256              * @param {HtmlEditor} this
25257              */
25258             activate: true,
25259              /**
25260              * @event beforesync
25261              * Fires before the textarea is updated with content from the editor iframe. Return false
25262              * to cancel the sync.
25263              * @param {HtmlEditor} this
25264              * @param {String} html
25265              */
25266             beforesync: true,
25267              /**
25268              * @event beforepush
25269              * Fires before the iframe editor is updated with content from the textarea. Return false
25270              * to cancel the push.
25271              * @param {HtmlEditor} this
25272              * @param {String} html
25273              */
25274             beforepush: true,
25275              /**
25276              * @event sync
25277              * Fires when the textarea is updated with content from the editor iframe.
25278              * @param {HtmlEditor} this
25279              * @param {String} html
25280              */
25281             sync: true,
25282              /**
25283              * @event push
25284              * Fires when the iframe editor is updated with content from the textarea.
25285              * @param {HtmlEditor} this
25286              * @param {String} html
25287              */
25288             push: true,
25289              /**
25290              * @event editmodechange
25291              * Fires when the editor switches edit modes
25292              * @param {HtmlEditor} this
25293              * @param {Boolean} sourceEdit True if source edit, false if standard editing.
25294              */
25295             editmodechange: true,
25296             /**
25297              * @event editorevent
25298              * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
25299              * @param {HtmlEditor} this
25300              */
25301             editorevent: true
25302         });
25303         this.defaultAutoCreate =  {
25304             tag: "textarea",
25305             style:'width: ' + this.width + 'px;height: ' + this.height + 'px;',
25306             autocomplete: "off"
25307         };
25308     },
25309
25310     /**
25311      * Protected method that will not generally be called directly. It
25312      * is called when the editor creates its toolbar. Override this method if you need to
25313      * add custom toolbar buttons.
25314      * @param {HtmlEditor} editor
25315      */
25316     createToolbar : function(editor){
25317         if (!editor.toolbars || !editor.toolbars.length) {
25318             editor.toolbars = [ new Roo.form.HtmlEditor.ToolbarStandard() ]; // can be empty?
25319         }
25320         
25321         for (var i =0 ; i < editor.toolbars.length;i++) {
25322             editor.toolbars[i] = Roo.factory(
25323                     typeof(editor.toolbars[i]) == 'string' ?
25324                         { xtype: editor.toolbars[i]} : editor.toolbars[i],
25325                 Roo.form.HtmlEditor);
25326             editor.toolbars[i].init(editor);
25327         }
25328          
25329         
25330     },
25331
25332     /**
25333      * Protected method that will not generally be called directly. It
25334      * is called when the editor initializes the iframe with HTML contents. Override this method if you
25335      * want to change the initialization markup of the iframe (e.g. to add stylesheets).
25336      */
25337     getDocMarkup : function(){
25338         // body styles..
25339         var st = '';
25340         if (this.stylesheets === false) {
25341             
25342             Roo.get(document.head).select('style').each(function(node) {
25343                 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
25344             });
25345             
25346             Roo.get(document.head).select('link').each(function(node) { 
25347                 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
25348             });
25349             
25350         } else if (!this.stylesheets.length) {
25351                 // simple..
25352                 st = '<style type="text/css">' +
25353                     'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
25354                    '</style>';
25355         } else {
25356             Roo.each(this.stylesheets, function(s) {
25357                 st += '<link rel="stylesheet" type="text/css" href="' + s +'" />'
25358             });
25359             
25360         }
25361         
25362         st +=  '<style type="text/css">' +
25363             'IMG { cursor: pointer } ' +
25364         '</style>';
25365
25366         
25367         return '<html><head>' + st  +
25368             //<style type="text/css">' +
25369             //'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
25370             //'</style>' +
25371             ' </head><body class="roo-htmleditor-body"></body></html>';
25372     },
25373
25374     // private
25375     onRender : function(ct, position)
25376     {
25377         var _t = this;
25378         Roo.form.HtmlEditor.superclass.onRender.call(this, ct, position);
25379         this.el.dom.style.border = '0 none';
25380         this.el.dom.setAttribute('tabIndex', -1);
25381         this.el.addClass('x-hidden');
25382         if(Roo.isIE){ // fix IE 1px bogus margin
25383             this.el.applyStyles('margin-top:-1px;margin-bottom:-1px;')
25384         }
25385         this.wrap = this.el.wrap({
25386             cls:'x-html-editor-wrap', cn:{cls:'x-html-editor-tb'}
25387         });
25388         
25389         if (this.resizable) {
25390             this.resizeEl = new Roo.Resizable(this.wrap, {
25391                 pinned : true,
25392                 wrap: true,
25393                 dynamic : true,
25394                 minHeight : this.height,
25395                 height: this.height,
25396                 handles : this.resizable,
25397                 width: this.width,
25398                 listeners : {
25399                     resize : function(r, w, h) {
25400                         _t.onResize(w,h); // -something
25401                     }
25402                 }
25403             });
25404             
25405         }
25406
25407         this.frameId = Roo.id();
25408         
25409         this.createToolbar(this);
25410         
25411       
25412         
25413         var iframe = this.wrap.createChild({
25414             tag: 'iframe',
25415             id: this.frameId,
25416             name: this.frameId,
25417             frameBorder : 'no',
25418             'src' : Roo.SSL_SECURE_URL ? Roo.SSL_SECURE_URL  :  "javascript:false"
25419         }, this.el
25420         );
25421         
25422        // console.log(iframe);
25423         //this.wrap.dom.appendChild(iframe);
25424
25425         this.iframe = iframe.dom;
25426
25427          this.assignDocWin();
25428         
25429         this.doc.designMode = 'on';
25430        
25431         this.doc.open();
25432         this.doc.write(this.getDocMarkup());
25433         this.doc.close();
25434
25435         
25436         var task = { // must defer to wait for browser to be ready
25437             run : function(){
25438                 //console.log("run task?" + this.doc.readyState);
25439                 this.assignDocWin();
25440                 if(this.doc.body || this.doc.readyState == 'complete'){
25441                     try {
25442                         this.doc.designMode="on";
25443                     } catch (e) {
25444                         return;
25445                     }
25446                     Roo.TaskMgr.stop(task);
25447                     this.initEditor.defer(10, this);
25448                 }
25449             },
25450             interval : 10,
25451             duration:10000,
25452             scope: this
25453         };
25454         Roo.TaskMgr.start(task);
25455
25456         if(!this.width){
25457             this.setSize(this.wrap.getSize());
25458         }
25459         if (this.resizeEl) {
25460             this.resizeEl.resizeTo.defer(100, this.resizeEl,[ this.width,this.height ] );
25461             // should trigger onReize..
25462         }
25463     },
25464
25465     // private
25466     onResize : function(w, h)
25467     {
25468         //Roo.log('resize: ' +w + ',' + h );
25469         Roo.form.HtmlEditor.superclass.onResize.apply(this, arguments);
25470         if(this.el && this.iframe){
25471             if(typeof w == 'number'){
25472                 var aw = w - this.wrap.getFrameWidth('lr');
25473                 this.el.setWidth(this.adjustWidth('textarea', aw));
25474                 this.iframe.style.width = aw + 'px';
25475             }
25476             if(typeof h == 'number'){
25477                 var tbh = 0;
25478                 for (var i =0; i < this.toolbars.length;i++) {
25479                     // fixme - ask toolbars for heights?
25480                     tbh += this.toolbars[i].tb.el.getHeight();
25481                     if (this.toolbars[i].footer) {
25482                         tbh += this.toolbars[i].footer.el.getHeight();
25483                     }
25484                 }
25485                 
25486                 
25487                 
25488                 
25489                 var ah = h - this.wrap.getFrameWidth('tb') - tbh;// this.tb.el.getHeight();
25490                 ah -= 5; // knock a few pixes off for look..
25491                 this.el.setHeight(this.adjustWidth('textarea', ah));
25492                 this.iframe.style.height = ah + 'px';
25493                 if(this.doc){
25494                     (this.doc.body || this.doc.documentElement).style.height = (ah - (this.iframePad*2)) + 'px';
25495                 }
25496             }
25497         }
25498     },
25499
25500     /**
25501      * Toggles the editor between standard and source edit mode.
25502      * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
25503      */
25504     toggleSourceEdit : function(sourceEditMode){
25505         
25506         this.sourceEditMode = sourceEditMode === true;
25507         
25508         if(this.sourceEditMode){
25509 //            Roo.log('in');
25510 //            Roo.log(this.syncValue());
25511             this.syncValue();
25512             this.iframe.className = 'x-hidden';
25513             this.el.removeClass('x-hidden');
25514             this.el.dom.removeAttribute('tabIndex');
25515             this.el.focus();
25516         }else{
25517 //            Roo.log('out')
25518 //            Roo.log(this.pushValue()); 
25519             this.pushValue();
25520             this.iframe.className = '';
25521             this.el.addClass('x-hidden');
25522             this.el.dom.setAttribute('tabIndex', -1);
25523             this.deferFocus();
25524         }
25525         this.setSize(this.wrap.getSize());
25526         this.fireEvent('editmodechange', this, this.sourceEditMode);
25527     },
25528
25529     // private used internally
25530     createLink : function(){
25531         var url = prompt(this.createLinkText, this.defaultLinkValue);
25532         if(url && url != 'http:/'+'/'){
25533             this.relayCmd('createlink', url);
25534         }
25535     },
25536
25537     // private (for BoxComponent)
25538     adjustSize : Roo.BoxComponent.prototype.adjustSize,
25539
25540     // private (for BoxComponent)
25541     getResizeEl : function(){
25542         return this.wrap;
25543     },
25544
25545     // private (for BoxComponent)
25546     getPositionEl : function(){
25547         return this.wrap;
25548     },
25549
25550     // private
25551     initEvents : function(){
25552         this.originalValue = this.getValue();
25553     },
25554
25555     /**
25556      * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
25557      * @method
25558      */
25559     markInvalid : Roo.emptyFn,
25560     /**
25561      * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
25562      * @method
25563      */
25564     clearInvalid : Roo.emptyFn,
25565
25566     setValue : function(v){
25567         Roo.form.HtmlEditor.superclass.setValue.call(this, v);
25568         this.pushValue();
25569     },
25570
25571     /**
25572      * Protected method that will not generally be called directly. If you need/want
25573      * custom HTML cleanup, this is the method you should override.
25574      * @param {String} html The HTML to be cleaned
25575      * return {String} The cleaned HTML
25576      */
25577     cleanHtml : function(html){
25578         html = String(html);
25579         if(html.length > 5){
25580             if(Roo.isSafari){ // strip safari nonsense
25581                 html = html.replace(/\sclass="(?:Apple-style-span|khtml-block-placeholder)"/gi, '');
25582             }
25583         }
25584         if(html == '&nbsp;'){
25585             html = '';
25586         }
25587         return html;
25588     },
25589
25590     /**
25591      * Protected method that will not generally be called directly. Syncs the contents
25592      * of the editor iframe with the textarea.
25593      */
25594     syncValue : function(){
25595         if(this.initialized){
25596             var bd = (this.doc.body || this.doc.documentElement);
25597             //this.cleanUpPaste(); -- this is done else where and causes havoc..
25598             var html = bd.innerHTML;
25599             if(Roo.isSafari){
25600                 var bs = bd.getAttribute('style'); // Safari puts text-align styles on the body element!
25601                 var m = bs.match(/text-align:(.*?);/i);
25602                 if(m && m[1]){
25603                     html = '<div style="'+m[0]+'">' + html + '</div>';
25604                 }
25605             }
25606             html = this.cleanHtml(html);
25607             // fix up the special chars.. normaly like back quotes in word...
25608             // however we do not want to do this with chinese..
25609             html = html.replace(/([\x80-\uffff])/g, function (a, b) {
25610                 var cc = b.charCodeAt();
25611                 if (
25612                     (cc >= 0x4E00 && cc < 0xA000 ) ||
25613                     (cc >= 0x3400 && cc < 0x4E00 ) ||
25614                     (cc >= 0xf900 && cc < 0xfb00 )
25615                 ) {
25616                         return b;
25617                 }
25618                 return "&#"+cc+";" 
25619             });
25620             if(this.fireEvent('beforesync', this, html) !== false){
25621                 this.el.dom.value = html;
25622                 this.fireEvent('sync', this, html);
25623             }
25624         }
25625     },
25626
25627     /**
25628      * Protected method that will not generally be called directly. Pushes the value of the textarea
25629      * into the iframe editor.
25630      */
25631     pushValue : function(){
25632         if(this.initialized){
25633             var v = this.el.dom.value;
25634             
25635             if(v.length < 1){
25636                 v = '&#160;';
25637             }
25638             
25639             if(this.fireEvent('beforepush', this, v) !== false){
25640                 var d = (this.doc.body || this.doc.documentElement);
25641                 d.innerHTML = v;
25642                 this.cleanUpPaste();
25643                 this.el.dom.value = d.innerHTML;
25644                 this.fireEvent('push', this, v);
25645             }
25646         }
25647     },
25648
25649     // private
25650     deferFocus : function(){
25651         this.focus.defer(10, this);
25652     },
25653
25654     // doc'ed in Field
25655     focus : function(){
25656         if(this.win && !this.sourceEditMode){
25657             this.win.focus();
25658         }else{
25659             this.el.focus();
25660         }
25661     },
25662     
25663     assignDocWin: function()
25664     {
25665         var iframe = this.iframe;
25666         
25667          if(Roo.isIE){
25668             this.doc = iframe.contentWindow.document;
25669             this.win = iframe.contentWindow;
25670         } else {
25671             if (!Roo.get(this.frameId)) {
25672                 return;
25673             }
25674             this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
25675             this.win = Roo.get(this.frameId).dom.contentWindow;
25676         }
25677     },
25678     
25679     // private
25680     initEditor : function(){
25681         //console.log("INIT EDITOR");
25682         this.assignDocWin();
25683         
25684         
25685         
25686         this.doc.designMode="on";
25687         this.doc.open();
25688         this.doc.write(this.getDocMarkup());
25689         this.doc.close();
25690         
25691         var dbody = (this.doc.body || this.doc.documentElement);
25692         //var ss = this.el.getStyles('font-size', 'font-family', 'background-image', 'background-repeat');
25693         // this copies styles from the containing element into thsi one..
25694         // not sure why we need all of this..
25695         var ss = this.el.getStyles('font-size', 'background-image', 'background-repeat');
25696         ss['background-attachment'] = 'fixed'; // w3c
25697         dbody.bgProperties = 'fixed'; // ie
25698         Roo.DomHelper.applyStyles(dbody, ss);
25699         Roo.EventManager.on(this.doc, {
25700             //'mousedown': this.onEditorEvent,
25701             'mouseup': this.onEditorEvent,
25702             'dblclick': this.onEditorEvent,
25703             'click': this.onEditorEvent,
25704             'keyup': this.onEditorEvent,
25705             buffer:100,
25706             scope: this
25707         });
25708         if(Roo.isGecko){
25709             Roo.EventManager.on(this.doc, 'keypress', this.mozKeyPress, this);
25710         }
25711         if(Roo.isIE || Roo.isSafari || Roo.isOpera){
25712             Roo.EventManager.on(this.doc, 'keydown', this.fixKeys, this);
25713         }
25714         this.initialized = true;
25715
25716         this.fireEvent('initialize', this);
25717         this.pushValue();
25718     },
25719
25720     // private
25721     onDestroy : function(){
25722         
25723         
25724         
25725         if(this.rendered){
25726             
25727             for (var i =0; i < this.toolbars.length;i++) {
25728                 // fixme - ask toolbars for heights?
25729                 this.toolbars[i].onDestroy();
25730             }
25731             
25732             this.wrap.dom.innerHTML = '';
25733             this.wrap.remove();
25734         }
25735     },
25736
25737     // private
25738     onFirstFocus : function(){
25739         
25740         this.assignDocWin();
25741         
25742         
25743         this.activated = true;
25744         for (var i =0; i < this.toolbars.length;i++) {
25745             this.toolbars[i].onFirstFocus();
25746         }
25747        
25748         if(Roo.isGecko){ // prevent silly gecko errors
25749             this.win.focus();
25750             var s = this.win.getSelection();
25751             if(!s.focusNode || s.focusNode.nodeType != 3){
25752                 var r = s.getRangeAt(0);
25753                 r.selectNodeContents((this.doc.body || this.doc.documentElement));
25754                 r.collapse(true);
25755                 this.deferFocus();
25756             }
25757             try{
25758                 this.execCmd('useCSS', true);
25759                 this.execCmd('styleWithCSS', false);
25760             }catch(e){}
25761         }
25762         this.fireEvent('activate', this);
25763     },
25764
25765     // private
25766     adjustFont: function(btn){
25767         var adjust = btn.cmd == 'increasefontsize' ? 1 : -1;
25768         //if(Roo.isSafari){ // safari
25769         //    adjust *= 2;
25770        // }
25771         var v = parseInt(this.doc.queryCommandValue('FontSize')|| 3, 10);
25772         if(Roo.isSafari){ // safari
25773             var sm = { 10 : 1, 13: 2, 16:3, 18:4, 24: 5, 32:6, 48: 7 };
25774             v =  (v < 10) ? 10 : v;
25775             v =  (v > 48) ? 48 : v;
25776             v = typeof(sm[v]) == 'undefined' ? 1 : sm[v];
25777             
25778         }
25779         
25780         
25781         v = Math.max(1, v+adjust);
25782         
25783         this.execCmd('FontSize', v  );
25784     },
25785
25786     onEditorEvent : function(e){
25787         this.fireEvent('editorevent', this, e);
25788       //  this.updateToolbar();
25789         this.syncValue(); //we can not sync so often.. sync cleans, so this breaks stuff
25790     },
25791
25792     insertTag : function(tg)
25793     {
25794         // could be a bit smarter... -> wrap the current selected tRoo..
25795         if (tg.toLowerCase() == 'span') {
25796             
25797             range = this.createRange(this.getSelection());
25798             var wrappingNode = this.doc.createElement("span");
25799             wrappingNode.appendChild(range.extractContents());
25800             range.insertNode(wrappingNode);
25801
25802             return;
25803             
25804             
25805             
25806         }
25807         this.execCmd("formatblock",   tg);
25808         
25809     },
25810     
25811     insertText : function(txt)
25812     {
25813         
25814         
25815         var range = this.createRange();
25816         range.deleteContents();
25817                //alert(Sender.getAttribute('label'));
25818                
25819         range.insertNode(this.doc.createTextNode(txt));
25820     } ,
25821     
25822     // private
25823     relayBtnCmd : function(btn){
25824         this.relayCmd(btn.cmd);
25825     },
25826
25827     /**
25828      * Executes a Midas editor command on the editor document and performs necessary focus and
25829      * toolbar updates. <b>This should only be called after the editor is initialized.</b>
25830      * @param {String} cmd The Midas command
25831      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
25832      */
25833     relayCmd : function(cmd, value){
25834         this.win.focus();
25835         this.execCmd(cmd, value);
25836         this.fireEvent('editorevent', this);
25837         //this.updateToolbar();
25838         this.deferFocus();
25839     },
25840
25841     /**
25842      * Executes a Midas editor command directly on the editor document.
25843      * For visual commands, you should use {@link #relayCmd} instead.
25844      * <b>This should only be called after the editor is initialized.</b>
25845      * @param {String} cmd The Midas command
25846      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
25847      */
25848     execCmd : function(cmd, value){
25849         this.doc.execCommand(cmd, false, value === undefined ? null : value);
25850         this.syncValue();
25851     },
25852  
25853  
25854    
25855     /**
25856      * Inserts the passed text at the current cursor position. Note: the editor must be initialized and activated
25857      * to insert tRoo.
25858      * @param {String} text | dom node.. 
25859      */
25860     insertAtCursor : function(text)
25861     {
25862         
25863         
25864         
25865         if(!this.activated){
25866             return;
25867         }
25868         /*
25869         if(Roo.isIE){
25870             this.win.focus();
25871             var r = this.doc.selection.createRange();
25872             if(r){
25873                 r.collapse(true);
25874                 r.pasteHTML(text);
25875                 this.syncValue();
25876                 this.deferFocus();
25877             
25878             }
25879             return;
25880         }
25881         */
25882         if(Roo.isGecko || Roo.isOpera || Roo.isSafari){
25883             this.win.focus();
25884             
25885             
25886             // from jquery ui (MIT licenced)
25887             var range, node;
25888             var win = this.win;
25889             
25890             if (win.getSelection && win.getSelection().getRangeAt) {
25891                 range = win.getSelection().getRangeAt(0);
25892                 node = typeof(text) == 'string' ? range.createContextualFragment(text) : text;
25893                 range.insertNode(node);
25894             } else if (win.document.selection && win.document.selection.createRange) {
25895                 // no firefox support
25896                 var txt = typeof(text) == 'string' ? text : text.outerHTML;
25897                 win.document.selection.createRange().pasteHTML(txt);
25898             } else {
25899                 // no firefox support
25900                 var txt = typeof(text) == 'string' ? text : text.outerHTML;
25901                 this.execCmd('InsertHTML', txt);
25902             } 
25903             
25904             this.syncValue();
25905             
25906             this.deferFocus();
25907         }
25908     },
25909  // private
25910     mozKeyPress : function(e){
25911         if(e.ctrlKey){
25912             var c = e.getCharCode(), cmd;
25913           
25914             if(c > 0){
25915                 c = String.fromCharCode(c).toLowerCase();
25916                 switch(c){
25917                     case 'b':
25918                         cmd = 'bold';
25919                         break;
25920                     case 'i':
25921                         cmd = 'italic';
25922                         break;
25923                     
25924                     case 'u':
25925                         cmd = 'underline';
25926                         break;
25927                     
25928                     case 'v':
25929                         this.cleanUpPaste.defer(100, this);
25930                         return;
25931                         
25932                 }
25933                 if(cmd){
25934                     this.win.focus();
25935                     this.execCmd(cmd);
25936                     this.deferFocus();
25937                     e.preventDefault();
25938                 }
25939                 
25940             }
25941         }
25942     },
25943
25944     // private
25945     fixKeys : function(){ // load time branching for fastest keydown performance
25946         if(Roo.isIE){
25947             return function(e){
25948                 var k = e.getKey(), r;
25949                 if(k == e.TAB){
25950                     e.stopEvent();
25951                     r = this.doc.selection.createRange();
25952                     if(r){
25953                         r.collapse(true);
25954                         r.pasteHTML('&#160;&#160;&#160;&#160;');
25955                         this.deferFocus();
25956                     }
25957                     return;
25958                 }
25959                 
25960                 if(k == e.ENTER){
25961                     r = this.doc.selection.createRange();
25962                     if(r){
25963                         var target = r.parentElement();
25964                         if(!target || target.tagName.toLowerCase() != 'li'){
25965                             e.stopEvent();
25966                             r.pasteHTML('<br />');
25967                             r.collapse(false);
25968                             r.select();
25969                         }
25970                     }
25971                 }
25972                 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
25973                     this.cleanUpPaste.defer(100, this);
25974                     return;
25975                 }
25976                 
25977                 
25978             };
25979         }else if(Roo.isOpera){
25980             return function(e){
25981                 var k = e.getKey();
25982                 if(k == e.TAB){
25983                     e.stopEvent();
25984                     this.win.focus();
25985                     this.execCmd('InsertHTML','&#160;&#160;&#160;&#160;');
25986                     this.deferFocus();
25987                 }
25988                 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
25989                     this.cleanUpPaste.defer(100, this);
25990                     return;
25991                 }
25992                 
25993             };
25994         }else if(Roo.isSafari){
25995             return function(e){
25996                 var k = e.getKey();
25997                 
25998                 if(k == e.TAB){
25999                     e.stopEvent();
26000                     this.execCmd('InsertText','\t');
26001                     this.deferFocus();
26002                     return;
26003                 }
26004                if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
26005                     this.cleanUpPaste.defer(100, this);
26006                     return;
26007                 }
26008                 
26009              };
26010         }
26011     }(),
26012     
26013     getAllAncestors: function()
26014     {
26015         var p = this.getSelectedNode();
26016         var a = [];
26017         if (!p) {
26018             a.push(p); // push blank onto stack..
26019             p = this.getParentElement();
26020         }
26021         
26022         
26023         while (p && (p.nodeType == 1) && (p.tagName.toLowerCase() != 'body')) {
26024             a.push(p);
26025             p = p.parentNode;
26026         }
26027         a.push(this.doc.body);
26028         return a;
26029     },
26030     lastSel : false,
26031     lastSelNode : false,
26032     
26033     
26034     getSelection : function() 
26035     {
26036         this.assignDocWin();
26037         return Roo.isIE ? this.doc.selection : this.win.getSelection();
26038     },
26039     
26040     getSelectedNode: function() 
26041     {
26042         // this may only work on Gecko!!!
26043         
26044         // should we cache this!!!!
26045         
26046         
26047         
26048          
26049         var range = this.createRange(this.getSelection()).cloneRange();
26050         
26051         if (Roo.isIE) {
26052             var parent = range.parentElement();
26053             while (true) {
26054                 var testRange = range.duplicate();
26055                 testRange.moveToElementText(parent);
26056                 if (testRange.inRange(range)) {
26057                     break;
26058                 }
26059                 if ((parent.nodeType != 1) || (parent.tagName.toLowerCase() == 'body')) {
26060                     break;
26061                 }
26062                 parent = parent.parentElement;
26063             }
26064             return parent;
26065         }
26066         
26067         // is ancestor a text element.
26068         var ac =  range.commonAncestorContainer;
26069         if (ac.nodeType == 3) {
26070             ac = ac.parentNode;
26071         }
26072         
26073         var ar = ac.childNodes;
26074          
26075         var nodes = [];
26076         var other_nodes = [];
26077         var has_other_nodes = false;
26078         for (var i=0;i<ar.length;i++) {
26079             if ((ar[i].nodeType == 3) && (!ar[i].data.length)) { // empty text ? 
26080                 continue;
26081             }
26082             // fullly contained node.
26083             
26084             if (this.rangeIntersectsNode(range,ar[i]) && this.rangeCompareNode(range,ar[i]) == 3) {
26085                 nodes.push(ar[i]);
26086                 continue;
26087             }
26088             
26089             // probably selected..
26090             if ((ar[i].nodeType == 1) && this.rangeIntersectsNode(range,ar[i]) && (this.rangeCompareNode(range,ar[i]) > 0)) {
26091                 other_nodes.push(ar[i]);
26092                 continue;
26093             }
26094             // outer..
26095             if (!this.rangeIntersectsNode(range,ar[i])|| (this.rangeCompareNode(range,ar[i]) == 0))  {
26096                 continue;
26097             }
26098             
26099             
26100             has_other_nodes = true;
26101         }
26102         if (!nodes.length && other_nodes.length) {
26103             nodes= other_nodes;
26104         }
26105         if (has_other_nodes || !nodes.length || (nodes.length > 1)) {
26106             return false;
26107         }
26108         
26109         return nodes[0];
26110     },
26111     createRange: function(sel)
26112     {
26113         // this has strange effects when using with 
26114         // top toolbar - not sure if it's a great idea.
26115         //this.editor.contentWindow.focus();
26116         if (typeof sel != "undefined") {
26117             try {
26118                 return sel.getRangeAt ? sel.getRangeAt(0) : sel.createRange();
26119             } catch(e) {
26120                 return this.doc.createRange();
26121             }
26122         } else {
26123             return this.doc.createRange();
26124         }
26125     },
26126     getParentElement: function()
26127     {
26128         
26129         this.assignDocWin();
26130         var sel = Roo.isIE ? this.doc.selection : this.win.getSelection();
26131         
26132         var range = this.createRange(sel);
26133          
26134         try {
26135             var p = range.commonAncestorContainer;
26136             while (p.nodeType == 3) { // text node
26137                 p = p.parentNode;
26138             }
26139             return p;
26140         } catch (e) {
26141             return null;
26142         }
26143     
26144     },
26145     /***
26146      *
26147      * Range intersection.. the hard stuff...
26148      *  '-1' = before
26149      *  '0' = hits..
26150      *  '1' = after.
26151      *         [ -- selected range --- ]
26152      *   [fail]                        [fail]
26153      *
26154      *    basically..
26155      *      if end is before start or  hits it. fail.
26156      *      if start is after end or hits it fail.
26157      *
26158      *   if either hits (but other is outside. - then it's not 
26159      *   
26160      *    
26161      **/
26162     
26163     
26164     // @see http://www.thismuchiknow.co.uk/?p=64.
26165     rangeIntersectsNode : function(range, node)
26166     {
26167         var nodeRange = node.ownerDocument.createRange();
26168         try {
26169             nodeRange.selectNode(node);
26170         } catch (e) {
26171             nodeRange.selectNodeContents(node);
26172         }
26173     
26174         var rangeStartRange = range.cloneRange();
26175         rangeStartRange.collapse(true);
26176     
26177         var rangeEndRange = range.cloneRange();
26178         rangeEndRange.collapse(false);
26179     
26180         var nodeStartRange = nodeRange.cloneRange();
26181         nodeStartRange.collapse(true);
26182     
26183         var nodeEndRange = nodeRange.cloneRange();
26184         nodeEndRange.collapse(false);
26185     
26186         return rangeStartRange.compareBoundaryPoints(
26187                  Range.START_TO_START, nodeEndRange) == -1 &&
26188                rangeEndRange.compareBoundaryPoints(
26189                  Range.START_TO_START, nodeStartRange) == 1;
26190         
26191          
26192     },
26193     rangeCompareNode : function(range, node)
26194     {
26195         var nodeRange = node.ownerDocument.createRange();
26196         try {
26197             nodeRange.selectNode(node);
26198         } catch (e) {
26199             nodeRange.selectNodeContents(node);
26200         }
26201         
26202         
26203         range.collapse(true);
26204     
26205         nodeRange.collapse(true);
26206      
26207         var ss = range.compareBoundaryPoints( Range.START_TO_START, nodeRange);
26208         var ee = range.compareBoundaryPoints(  Range.END_TO_END, nodeRange);
26209          
26210         //Roo.log(node.tagName + ': ss='+ss +', ee='+ee)
26211         
26212         var nodeIsBefore   =  ss == 1;
26213         var nodeIsAfter    = ee == -1;
26214         
26215         if (nodeIsBefore && nodeIsAfter)
26216             return 0; // outer
26217         if (!nodeIsBefore && nodeIsAfter)
26218             return 1; //right trailed.
26219         
26220         if (nodeIsBefore && !nodeIsAfter)
26221             return 2;  // left trailed.
26222         // fully contined.
26223         return 3;
26224     },
26225
26226     // private? - in a new class?
26227     cleanUpPaste :  function()
26228     {
26229         // cleans up the whole document..
26230          Roo.log('cleanuppaste');
26231         this.cleanUpChildren(this.doc.body);
26232         var clean = this.cleanWordChars(this.doc.body.innerHTML);
26233         if (clean != this.doc.body.innerHTML) {
26234             this.doc.body.innerHTML = clean;
26235         }
26236         
26237     },
26238     
26239     cleanWordChars : function(input) {// change the chars to hex code
26240         var he = Roo.form.HtmlEditor;
26241         
26242         var output = input;
26243         Roo.each(he.swapCodes, function(sw) { 
26244             var swapper = new RegExp("\\u" + sw[0].toString(16), "g"); // hex codes
26245             
26246             output = output.replace(swapper, sw[1]);
26247         });
26248         
26249         return output;
26250     },
26251     
26252     
26253     cleanUpChildren : function (n)
26254     {
26255         if (!n.childNodes.length) {
26256             return;
26257         }
26258         for (var i = n.childNodes.length-1; i > -1 ; i--) {
26259            this.cleanUpChild(n.childNodes[i]);
26260         }
26261     },
26262     
26263     
26264         
26265     
26266     cleanUpChild : function (node)
26267     {
26268         var ed = this;
26269         //console.log(node);
26270         if (node.nodeName == "#text") {
26271             // clean up silly Windows -- stuff?
26272             return; 
26273         }
26274         if (node.nodeName == "#comment") {
26275             node.parentNode.removeChild(node);
26276             // clean up silly Windows -- stuff?
26277             return; 
26278         }
26279         
26280         if (Roo.form.HtmlEditor.black.indexOf(node.tagName.toLowerCase()) > -1) {
26281             // remove node.
26282             node.parentNode.removeChild(node);
26283             return;
26284             
26285         }
26286         
26287         var remove_keep_children= Roo.form.HtmlEditor.remove.indexOf(node.tagName.toLowerCase()) > -1;
26288         
26289         // remove <a name=....> as rendering on yahoo mailer is borked with this.
26290         // this will have to be flaged elsewhere - perhaps ablack=name... on the mailer..
26291         
26292         //if (node.tagName.toLowerCase() == 'a' && !node.hasAttribute('href')) {
26293         //    remove_keep_children = true;
26294         //}
26295         
26296         if (remove_keep_children) {
26297             this.cleanUpChildren(node);
26298             // inserts everything just before this node...
26299             while (node.childNodes.length) {
26300                 var cn = node.childNodes[0];
26301                 node.removeChild(cn);
26302                 node.parentNode.insertBefore(cn, node);
26303             }
26304             node.parentNode.removeChild(node);
26305             return;
26306         }
26307         
26308         if (!node.attributes || !node.attributes.length) {
26309             this.cleanUpChildren(node);
26310             return;
26311         }
26312         
26313         function cleanAttr(n,v)
26314         {
26315             
26316             if (v.match(/^\./) || v.match(/^\//)) {
26317                 return;
26318             }
26319             if (v.match(/^(http|https):\/\//) || v.match(/^mailto:/)) {
26320                 return;
26321             }
26322             if (v.match(/^#/)) {
26323                 return;
26324             }
26325 //            Roo.log("(REMOVE TAG)"+ node.tagName +'.' + n + '=' + v);
26326             node.removeAttribute(n);
26327             
26328         }
26329         
26330         function cleanStyle(n,v)
26331         {
26332             if (v.match(/expression/)) { //XSS?? should we even bother..
26333                 node.removeAttribute(n);
26334                 return;
26335             }
26336             var cwhite = typeof(ed.cwhite) == 'undefined' ? Roo.form.HtmlEditor.cwhite : ed.cwhite;
26337             var cblack = typeof(ed.cblack) == 'undefined' ? Roo.form.HtmlEditor.cblack : ed.cblack;
26338             
26339             
26340             var parts = v.split(/;/);
26341             var clean = [];
26342             
26343             Roo.each(parts, function(p) {
26344                 p = p.replace(/^\s+/g,'').replace(/\s+$/g,'');
26345                 if (!p.length) {
26346                     return true;
26347                 }
26348                 var l = p.split(':').shift().replace(/\s+/g,'');
26349                 l = l.replace(/^\s+/g,'').replace(/\s+$/g,'');
26350                 
26351                 
26352                 if ( cblack.indexOf(l) > -1) {
26353 //                    Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
26354                     //node.removeAttribute(n);
26355                     return true;
26356                 }
26357                 //Roo.log()
26358                 // only allow 'c whitelisted system attributes'
26359                 if ( cwhite.length &&  cwhite.indexOf(l) < 0) {
26360 //                    Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
26361                     //node.removeAttribute(n);
26362                     return true;
26363                 }
26364                 
26365                 
26366                  
26367                 
26368                 clean.push(p);
26369                 return true;
26370             });
26371             if (clean.length) { 
26372                 node.setAttribute(n, clean.join(';'));
26373             } else {
26374                 node.removeAttribute(n);
26375             }
26376             
26377         }
26378         
26379         
26380         for (var i = node.attributes.length-1; i > -1 ; i--) {
26381             var a = node.attributes[i];
26382             //console.log(a);
26383             
26384             if (a.name.toLowerCase().substr(0,2)=='on')  {
26385                 node.removeAttribute(a.name);
26386                 continue;
26387             }
26388             if (Roo.form.HtmlEditor.ablack.indexOf(a.name.toLowerCase()) > -1) {
26389                 node.removeAttribute(a.name);
26390                 continue;
26391             }
26392             if (Roo.form.HtmlEditor.aclean.indexOf(a.name.toLowerCase()) > -1) {
26393                 cleanAttr(a.name,a.value); // fixme..
26394                 continue;
26395             }
26396             if (a.name == 'style') {
26397                 cleanStyle(a.name,a.value);
26398                 continue;
26399             }
26400             /// clean up MS crap..
26401             // tecnically this should be a list of valid class'es..
26402             
26403             
26404             if (a.name == 'class') {
26405                 if (a.value.match(/^Mso/)) {
26406                     node.className = '';
26407                 }
26408                 
26409                 if (a.value.match(/body/)) {
26410                     node.className = '';
26411                 }
26412                 continue;
26413             }
26414             
26415             // style cleanup!?
26416             // class cleanup?
26417             
26418         }
26419         
26420         
26421         this.cleanUpChildren(node);
26422         
26423         
26424     }
26425     
26426     
26427     // hide stuff that is not compatible
26428     /**
26429      * @event blur
26430      * @hide
26431      */
26432     /**
26433      * @event change
26434      * @hide
26435      */
26436     /**
26437      * @event focus
26438      * @hide
26439      */
26440     /**
26441      * @event specialkey
26442      * @hide
26443      */
26444     /**
26445      * @cfg {String} fieldClass @hide
26446      */
26447     /**
26448      * @cfg {String} focusClass @hide
26449      */
26450     /**
26451      * @cfg {String} autoCreate @hide
26452      */
26453     /**
26454      * @cfg {String} inputType @hide
26455      */
26456     /**
26457      * @cfg {String} invalidClass @hide
26458      */
26459     /**
26460      * @cfg {String} invalidText @hide
26461      */
26462     /**
26463      * @cfg {String} msgFx @hide
26464      */
26465     /**
26466      * @cfg {String} validateOnBlur @hide
26467      */
26468 });
26469
26470 Roo.form.HtmlEditor.white = [
26471         'area', 'br', 'img', 'input', 'hr', 'wbr',
26472         
26473        'address', 'blockquote', 'center', 'dd',      'dir',       'div', 
26474        'dl',      'dt',         'h1',     'h2',      'h3',        'h4', 
26475        'h5',      'h6',         'hr',     'isindex', 'listing',   'marquee', 
26476        'menu',    'multicol',   'ol',     'p',       'plaintext', 'pre', 
26477        'table',   'ul',         'xmp', 
26478        
26479        'caption', 'col', 'colgroup', 'tbody', 'td', 'tfoot', 'th', 
26480       'thead',   'tr', 
26481      
26482       'dir', 'menu', 'ol', 'ul', 'dl',
26483        
26484       'embed',  'object'
26485 ];
26486
26487
26488 Roo.form.HtmlEditor.black = [
26489     //    'embed',  'object', // enable - backend responsiblity to clean thiese
26490         'applet', // 
26491         'base',   'basefont', 'bgsound', 'blink',  'body', 
26492         'frame',  'frameset', 'head',    'html',   'ilayer', 
26493         'iframe', 'layer',  'link',     'meta',    'object',   
26494         'script', 'style' ,'title',  'xml' // clean later..
26495 ];
26496 Roo.form.HtmlEditor.clean = [
26497     'script', 'style', 'title', 'xml'
26498 ];
26499 Roo.form.HtmlEditor.remove = [
26500     'font'
26501 ];
26502 // attributes..
26503
26504 Roo.form.HtmlEditor.ablack = [
26505     'on'
26506 ];
26507     
26508 Roo.form.HtmlEditor.aclean = [ 
26509     'action', 'background', 'codebase', 'dynsrc', 'href', 'lowsrc' 
26510 ];
26511
26512 // protocols..
26513 Roo.form.HtmlEditor.pwhite= [
26514         'http',  'https',  'mailto'
26515 ];
26516
26517 // white listed style attributes.
26518 Roo.form.HtmlEditor.cwhite= [
26519       //  'text-align', /// default is to allow most things..
26520       
26521          
26522 //        'font-size'//??
26523 ];
26524
26525 // black listed style attributes.
26526 Roo.form.HtmlEditor.cblack= [
26527       //  'font-size' -- this can be set by the project 
26528 ];
26529
26530
26531 Roo.form.HtmlEditor.swapCodes   =[ 
26532     [    8211, "--" ], 
26533     [    8212, "--" ], 
26534     [    8216,  "'" ],  
26535     [    8217, "'" ],  
26536     [    8220, '"' ],  
26537     [    8221, '"' ],  
26538     [    8226, "*" ],  
26539     [    8230, "..." ]
26540 ]; 
26541
26542     // <script type="text/javascript">
26543 /*
26544  * Based on
26545  * Ext JS Library 1.1.1
26546  * Copyright(c) 2006-2007, Ext JS, LLC.
26547  *  
26548  
26549  */
26550
26551 /**
26552  * @class Roo.form.HtmlEditorToolbar1
26553  * Basic Toolbar
26554  * 
26555  * Usage:
26556  *
26557  new Roo.form.HtmlEditor({
26558     ....
26559     toolbars : [
26560         new Roo.form.HtmlEditorToolbar1({
26561             disable : { fonts: 1 , format: 1, ..., ... , ...],
26562             btns : [ .... ]
26563         })
26564     }
26565      
26566  * 
26567  * @cfg {Object} disable List of elements to disable..
26568  * @cfg {Array} btns List of additional buttons.
26569  * 
26570  * 
26571  * NEEDS Extra CSS? 
26572  * .x-html-editor-tb .x-edit-none .x-btn-text { background: none; }
26573  */
26574  
26575 Roo.form.HtmlEditor.ToolbarStandard = function(config)
26576 {
26577     
26578     Roo.apply(this, config);
26579     
26580     // default disabled, based on 'good practice'..
26581     this.disable = this.disable || {};
26582     Roo.applyIf(this.disable, {
26583         fontSize : true,
26584         colors : true,
26585         specialElements : true
26586     });
26587     
26588     
26589     //Roo.form.HtmlEditorToolbar1.superclass.constructor.call(this, editor.wrap.dom.firstChild, [], config);
26590     // dont call parent... till later.
26591 }
26592
26593 Roo.apply(Roo.form.HtmlEditor.ToolbarStandard.prototype,  {
26594     
26595     tb: false,
26596     
26597     rendered: false,
26598     
26599     editor : false,
26600     /**
26601      * @cfg {Object} disable  List of toolbar elements to disable
26602          
26603      */
26604     disable : false,
26605       /**
26606      * @cfg {Array} fontFamilies An array of available font families
26607      */
26608     fontFamilies : [
26609         'Arial',
26610         'Courier New',
26611         'Tahoma',
26612         'Times New Roman',
26613         'Verdana'
26614     ],
26615     
26616     specialChars : [
26617            "&#169;",
26618           "&#174;",     
26619           "&#8482;",    
26620           "&#163;" ,    
26621          // "&#8212;",    
26622           "&#8230;",    
26623           "&#247;" ,    
26624         //  "&#225;" ,     ?? a acute?
26625            "&#8364;"    , //Euro
26626        //   "&#8220;"    ,
26627         //  "&#8221;"    ,
26628         //  "&#8226;"    ,
26629           "&#176;"  //   , // degrees
26630
26631          // "&#233;"     , // e ecute
26632          // "&#250;"     , // u ecute?
26633     ],
26634     
26635     specialElements : [
26636         {
26637             text: "Insert Table",
26638             xtype: 'MenuItem',
26639             xns : Roo.Menu,
26640             ihtml :  '<table><tr><td>Cell</td></tr></table>' 
26641                 
26642         },
26643         {    
26644             text: "Insert Image",
26645             xtype: 'MenuItem',
26646             xns : Roo.Menu,
26647             ihtml : '<img src="about:blank"/>'
26648             
26649         }
26650         
26651          
26652     ],
26653     
26654     
26655     inputElements : [ 
26656             "form", "input:text", "input:hidden", "input:checkbox", "input:radio", "input:password", 
26657             "input:submit", "input:button", "select", "textarea", "label" ],
26658     formats : [
26659         ["p"] ,  
26660         ["h1"],["h2"],["h3"],["h4"],["h5"],["h6"], 
26661         ["pre"],[ "code"], 
26662         ["abbr"],[ "acronym"],[ "address"],[ "cite"],[ "samp"],[ "var"],
26663         ['div'],['span']
26664     ],
26665      /**
26666      * @cfg {String} defaultFont default font to use.
26667      */
26668     defaultFont: 'tahoma',
26669    
26670     fontSelect : false,
26671     
26672     
26673     formatCombo : false,
26674     
26675     init : function(editor)
26676     {
26677         this.editor = editor;
26678         
26679         
26680         var fid = editor.frameId;
26681         var etb = this;
26682         function btn(id, toggle, handler){
26683             var xid = fid + '-'+ id ;
26684             return {
26685                 id : xid,
26686                 cmd : id,
26687                 cls : 'x-btn-icon x-edit-'+id,
26688                 enableToggle:toggle !== false,
26689                 scope: editor, // was editor...
26690                 handler:handler||editor.relayBtnCmd,
26691                 clickEvent:'mousedown',
26692                 tooltip: etb.buttonTips[id] || undefined, ///tips ???
26693                 tabIndex:-1
26694             };
26695         }
26696         
26697         
26698         
26699         var tb = new Roo.Toolbar(editor.wrap.dom.firstChild);
26700         this.tb = tb;
26701          // stop form submits
26702         tb.el.on('click', function(e){
26703             e.preventDefault(); // what does this do?
26704         });
26705
26706         if(!this.disable.font) { // && !Roo.isSafari){
26707             /* why no safari for fonts 
26708             editor.fontSelect = tb.el.createChild({
26709                 tag:'select',
26710                 tabIndex: -1,
26711                 cls:'x-font-select',
26712                 html: this.createFontOptions()
26713             });
26714             
26715             editor.fontSelect.on('change', function(){
26716                 var font = editor.fontSelect.dom.value;
26717                 editor.relayCmd('fontname', font);
26718                 editor.deferFocus();
26719             }, editor);
26720             
26721             tb.add(
26722                 editor.fontSelect.dom,
26723                 '-'
26724             );
26725             */
26726             
26727         };
26728         if(!this.disable.formats){
26729             this.formatCombo = new Roo.form.ComboBox({
26730                 store: new Roo.data.SimpleStore({
26731                     id : 'tag',
26732                     fields: ['tag'],
26733                     data : this.formats // from states.js
26734                 }),
26735                 blockFocus : true,
26736                 name : '',
26737                 //autoCreate : {tag: "div",  size: "20"},
26738                 displayField:'tag',
26739                 typeAhead: false,
26740                 mode: 'local',
26741                 editable : false,
26742                 triggerAction: 'all',
26743                 emptyText:'Add tag',
26744                 selectOnFocus:true,
26745                 width:135,
26746                 listeners : {
26747                     'select': function(c, r, i) {
26748                         editor.insertTag(r.get('tag'));
26749                         editor.focus();
26750                     }
26751                 }
26752
26753             });
26754             tb.addField(this.formatCombo);
26755             
26756         }
26757         
26758         if(!this.disable.format){
26759             tb.add(
26760                 btn('bold'),
26761                 btn('italic'),
26762                 btn('underline')
26763             );
26764         };
26765         if(!this.disable.fontSize){
26766             tb.add(
26767                 '-',
26768                 
26769                 
26770                 btn('increasefontsize', false, editor.adjustFont),
26771                 btn('decreasefontsize', false, editor.adjustFont)
26772             );
26773         };
26774         
26775         
26776         if(!this.disable.colors){
26777             tb.add(
26778                 '-', {
26779                     id:editor.frameId +'-forecolor',
26780                     cls:'x-btn-icon x-edit-forecolor',
26781                     clickEvent:'mousedown',
26782                     tooltip: this.buttonTips['forecolor'] || undefined,
26783                     tabIndex:-1,
26784                     menu : new Roo.menu.ColorMenu({
26785                         allowReselect: true,
26786                         focus: Roo.emptyFn,
26787                         value:'000000',
26788                         plain:true,
26789                         selectHandler: function(cp, color){
26790                             editor.execCmd('forecolor', Roo.isSafari || Roo.isIE ? '#'+color : color);
26791                             editor.deferFocus();
26792                         },
26793                         scope: editor,
26794                         clickEvent:'mousedown'
26795                     })
26796                 }, {
26797                     id:editor.frameId +'backcolor',
26798                     cls:'x-btn-icon x-edit-backcolor',
26799                     clickEvent:'mousedown',
26800                     tooltip: this.buttonTips['backcolor'] || undefined,
26801                     tabIndex:-1,
26802                     menu : new Roo.menu.ColorMenu({
26803                         focus: Roo.emptyFn,
26804                         value:'FFFFFF',
26805                         plain:true,
26806                         allowReselect: true,
26807                         selectHandler: function(cp, color){
26808                             if(Roo.isGecko){
26809                                 editor.execCmd('useCSS', false);
26810                                 editor.execCmd('hilitecolor', color);
26811                                 editor.execCmd('useCSS', true);
26812                                 editor.deferFocus();
26813                             }else{
26814                                 editor.execCmd(Roo.isOpera ? 'hilitecolor' : 'backcolor', 
26815                                     Roo.isSafari || Roo.isIE ? '#'+color : color);
26816                                 editor.deferFocus();
26817                             }
26818                         },
26819                         scope:editor,
26820                         clickEvent:'mousedown'
26821                     })
26822                 }
26823             );
26824         };
26825         // now add all the items...
26826         
26827
26828         if(!this.disable.alignments){
26829             tb.add(
26830                 '-',
26831                 btn('justifyleft'),
26832                 btn('justifycenter'),
26833                 btn('justifyright')
26834             );
26835         };
26836
26837         //if(!Roo.isSafari){
26838             if(!this.disable.links){
26839                 tb.add(
26840                     '-',
26841                     btn('createlink', false, editor.createLink)    /// MOVE TO HERE?!!?!?!?!
26842                 );
26843             };
26844
26845             if(!this.disable.lists){
26846                 tb.add(
26847                     '-',
26848                     btn('insertorderedlist'),
26849                     btn('insertunorderedlist')
26850                 );
26851             }
26852             if(!this.disable.sourceEdit){
26853                 tb.add(
26854                     '-',
26855                     btn('sourceedit', true, function(btn){
26856                         this.toggleSourceEdit(btn.pressed);
26857                     })
26858                 );
26859             }
26860         //}
26861         
26862         var smenu = { };
26863         // special menu.. - needs to be tidied up..
26864         if (!this.disable.special) {
26865             smenu = {
26866                 text: "&#169;",
26867                 cls: 'x-edit-none',
26868                 
26869                 menu : {
26870                     items : []
26871                 }
26872             };
26873             for (var i =0; i < this.specialChars.length; i++) {
26874                 smenu.menu.items.push({
26875                     
26876                     html: this.specialChars[i],
26877                     handler: function(a,b) {
26878                         editor.insertAtCursor(String.fromCharCode(a.html.replace('&#','').replace(';', '')));
26879                         //editor.insertAtCursor(a.html);
26880                         
26881                     },
26882                     tabIndex:-1
26883                 });
26884             }
26885             
26886             
26887             tb.add(smenu);
26888             
26889             
26890         }
26891          
26892         if (!this.disable.specialElements) {
26893             var semenu = {
26894                 text: "Other;",
26895                 cls: 'x-edit-none',
26896                 menu : {
26897                     items : []
26898                 }
26899             };
26900             for (var i =0; i < this.specialElements.length; i++) {
26901                 semenu.menu.items.push(
26902                     Roo.apply({ 
26903                         handler: function(a,b) {
26904                             editor.insertAtCursor(this.ihtml);
26905                         }
26906                     }, this.specialElements[i])
26907                 );
26908                     
26909             }
26910             
26911             tb.add(semenu);
26912             
26913             
26914         }
26915          
26916         
26917         if (this.btns) {
26918             for(var i =0; i< this.btns.length;i++) {
26919                 var b = Roo.factory(this.btns[i],Roo.form);
26920                 b.cls =  'x-edit-none';
26921                 b.scope = editor;
26922                 tb.add(b);
26923             }
26924         
26925         }
26926         
26927         
26928         
26929         // disable everything...
26930         
26931         this.tb.items.each(function(item){
26932            if(item.id != editor.frameId+ '-sourceedit'){
26933                 item.disable();
26934             }
26935         });
26936         this.rendered = true;
26937         
26938         // the all the btns;
26939         editor.on('editorevent', this.updateToolbar, this);
26940         // other toolbars need to implement this..
26941         //editor.on('editmodechange', this.updateToolbar, this);
26942     },
26943     
26944     
26945     
26946     /**
26947      * Protected method that will not generally be called directly. It triggers
26948      * a toolbar update by reading the markup state of the current selection in the editor.
26949      */
26950     updateToolbar: function(){
26951
26952         if(!this.editor.activated){
26953             this.editor.onFirstFocus();
26954             return;
26955         }
26956
26957         var btns = this.tb.items.map, 
26958             doc = this.editor.doc,
26959             frameId = this.editor.frameId;
26960
26961         if(!this.disable.font && !Roo.isSafari){
26962             /*
26963             var name = (doc.queryCommandValue('FontName')||this.editor.defaultFont).toLowerCase();
26964             if(name != this.fontSelect.dom.value){
26965                 this.fontSelect.dom.value = name;
26966             }
26967             */
26968         }
26969         if(!this.disable.format){
26970             btns[frameId + '-bold'].toggle(doc.queryCommandState('bold'));
26971             btns[frameId + '-italic'].toggle(doc.queryCommandState('italic'));
26972             btns[frameId + '-underline'].toggle(doc.queryCommandState('underline'));
26973         }
26974         if(!this.disable.alignments){
26975             btns[frameId + '-justifyleft'].toggle(doc.queryCommandState('justifyleft'));
26976             btns[frameId + '-justifycenter'].toggle(doc.queryCommandState('justifycenter'));
26977             btns[frameId + '-justifyright'].toggle(doc.queryCommandState('justifyright'));
26978         }
26979         if(!Roo.isSafari && !this.disable.lists){
26980             btns[frameId + '-insertorderedlist'].toggle(doc.queryCommandState('insertorderedlist'));
26981             btns[frameId + '-insertunorderedlist'].toggle(doc.queryCommandState('insertunorderedlist'));
26982         }
26983         
26984         var ans = this.editor.getAllAncestors();
26985         if (this.formatCombo) {
26986             
26987             
26988             var store = this.formatCombo.store;
26989             this.formatCombo.setValue("");
26990             for (var i =0; i < ans.length;i++) {
26991                 if (ans[i] && store.query('tag',ans[i].tagName.toLowerCase(), false).length) {
26992                     // select it..
26993                     this.formatCombo.setValue(ans[i].tagName.toLowerCase());
26994                     break;
26995                 }
26996             }
26997         }
26998         
26999         
27000         
27001         // hides menus... - so this cant be on a menu...
27002         Roo.menu.MenuMgr.hideAll();
27003
27004         //this.editorsyncValue();
27005     },
27006    
27007     
27008     createFontOptions : function(){
27009         var buf = [], fs = this.fontFamilies, ff, lc;
27010         
27011         
27012         
27013         for(var i = 0, len = fs.length; i< len; i++){
27014             ff = fs[i];
27015             lc = ff.toLowerCase();
27016             buf.push(
27017                 '<option value="',lc,'" style="font-family:',ff,';"',
27018                     (this.defaultFont == lc ? ' selected="true">' : '>'),
27019                     ff,
27020                 '</option>'
27021             );
27022         }
27023         return buf.join('');
27024     },
27025     
27026     toggleSourceEdit : function(sourceEditMode){
27027         if(sourceEditMode === undefined){
27028             sourceEditMode = !this.sourceEditMode;
27029         }
27030         this.sourceEditMode = sourceEditMode === true;
27031         var btn = this.tb.items.get(this.editor.frameId +'-sourceedit');
27032         // just toggle the button?
27033         if(btn.pressed !== this.editor.sourceEditMode){
27034             btn.toggle(this.editor.sourceEditMode);
27035             return;
27036         }
27037         
27038         if(this.sourceEditMode){
27039             this.tb.items.each(function(item){
27040                 if(item.cmd != 'sourceedit'){
27041                     item.disable();
27042                 }
27043             });
27044           
27045         }else{
27046             if(this.initialized){
27047                 this.tb.items.each(function(item){
27048                     item.enable();
27049                 });
27050             }
27051             
27052         }
27053         // tell the editor that it's been pressed..
27054         this.editor.toggleSourceEdit(sourceEditMode);
27055        
27056     },
27057      /**
27058      * Object collection of toolbar tooltips for the buttons in the editor. The key
27059      * is the command id associated with that button and the value is a valid QuickTips object.
27060      * For example:
27061 <pre><code>
27062 {
27063     bold : {
27064         title: 'Bold (Ctrl+B)',
27065         text: 'Make the selected text bold.',
27066         cls: 'x-html-editor-tip'
27067     },
27068     italic : {
27069         title: 'Italic (Ctrl+I)',
27070         text: 'Make the selected text italic.',
27071         cls: 'x-html-editor-tip'
27072     },
27073     ...
27074 </code></pre>
27075     * @type Object
27076      */
27077     buttonTips : {
27078         bold : {
27079             title: 'Bold (Ctrl+B)',
27080             text: 'Make the selected text bold.',
27081             cls: 'x-html-editor-tip'
27082         },
27083         italic : {
27084             title: 'Italic (Ctrl+I)',
27085             text: 'Make the selected text italic.',
27086             cls: 'x-html-editor-tip'
27087         },
27088         underline : {
27089             title: 'Underline (Ctrl+U)',
27090             text: 'Underline the selected text.',
27091             cls: 'x-html-editor-tip'
27092         },
27093         increasefontsize : {
27094             title: 'Grow Text',
27095             text: 'Increase the font size.',
27096             cls: 'x-html-editor-tip'
27097         },
27098         decreasefontsize : {
27099             title: 'Shrink Text',
27100             text: 'Decrease the font size.',
27101             cls: 'x-html-editor-tip'
27102         },
27103         backcolor : {
27104             title: 'Text Highlight Color',
27105             text: 'Change the background color of the selected text.',
27106             cls: 'x-html-editor-tip'
27107         },
27108         forecolor : {
27109             title: 'Font Color',
27110             text: 'Change the color of the selected text.',
27111             cls: 'x-html-editor-tip'
27112         },
27113         justifyleft : {
27114             title: 'Align Text Left',
27115             text: 'Align text to the left.',
27116             cls: 'x-html-editor-tip'
27117         },
27118         justifycenter : {
27119             title: 'Center Text',
27120             text: 'Center text in the editor.',
27121             cls: 'x-html-editor-tip'
27122         },
27123         justifyright : {
27124             title: 'Align Text Right',
27125             text: 'Align text to the right.',
27126             cls: 'x-html-editor-tip'
27127         },
27128         insertunorderedlist : {
27129             title: 'Bullet List',
27130             text: 'Start a bulleted list.',
27131             cls: 'x-html-editor-tip'
27132         },
27133         insertorderedlist : {
27134             title: 'Numbered List',
27135             text: 'Start a numbered list.',
27136             cls: 'x-html-editor-tip'
27137         },
27138         createlink : {
27139             title: 'Hyperlink',
27140             text: 'Make the selected text a hyperlink.',
27141             cls: 'x-html-editor-tip'
27142         },
27143         sourceedit : {
27144             title: 'Source Edit',
27145             text: 'Switch to source editing mode.',
27146             cls: 'x-html-editor-tip'
27147         }
27148     },
27149     // private
27150     onDestroy : function(){
27151         if(this.rendered){
27152             
27153             this.tb.items.each(function(item){
27154                 if(item.menu){
27155                     item.menu.removeAll();
27156                     if(item.menu.el){
27157                         item.menu.el.destroy();
27158                     }
27159                 }
27160                 item.destroy();
27161             });
27162              
27163         }
27164     },
27165     onFirstFocus: function() {
27166         this.tb.items.each(function(item){
27167            item.enable();
27168         });
27169     }
27170 });
27171
27172
27173
27174
27175 // <script type="text/javascript">
27176 /*
27177  * Based on
27178  * Ext JS Library 1.1.1
27179  * Copyright(c) 2006-2007, Ext JS, LLC.
27180  *  
27181  
27182  */
27183
27184  
27185 /**
27186  * @class Roo.form.HtmlEditor.ToolbarContext
27187  * Context Toolbar
27188  * 
27189  * Usage:
27190  *
27191  new Roo.form.HtmlEditor({
27192     ....
27193     toolbars : [
27194         { xtype: 'ToolbarStandard', styles : {} }
27195         { xtype: 'ToolbarContext', disable : {} }
27196     ]
27197 })
27198
27199      
27200  * 
27201  * @config : {Object} disable List of elements to disable.. (not done yet.)
27202  * @config : {Object} styles  Map of styles available.
27203  * 
27204  */
27205
27206 Roo.form.HtmlEditor.ToolbarContext = function(config)
27207 {
27208     
27209     Roo.apply(this, config);
27210     //Roo.form.HtmlEditorToolbar1.superclass.constructor.call(this, editor.wrap.dom.firstChild, [], config);
27211     // dont call parent... till later.
27212     this.styles = this.styles || {};
27213 }
27214
27215  
27216
27217 Roo.form.HtmlEditor.ToolbarContext.types = {
27218     'IMG' : {
27219         width : {
27220             title: "Width",
27221             width: 40
27222         },
27223         height:  {
27224             title: "Height",
27225             width: 40
27226         },
27227         align: {
27228             title: "Align",
27229             opts : [ [""],[ "left"],[ "right"],[ "center"],[ "top"]],
27230             width : 80
27231             
27232         },
27233         border: {
27234             title: "Border",
27235             width: 40
27236         },
27237         alt: {
27238             title: "Alt",
27239             width: 120
27240         },
27241         src : {
27242             title: "Src",
27243             width: 220
27244         }
27245         
27246     },
27247     'A' : {
27248         name : {
27249             title: "Name",
27250             width: 50
27251         },
27252         href:  {
27253             title: "Href",
27254             width: 220
27255         } // border?
27256         
27257     },
27258     'TABLE' : {
27259         rows : {
27260             title: "Rows",
27261             width: 20
27262         },
27263         cols : {
27264             title: "Cols",
27265             width: 20
27266         },
27267         width : {
27268             title: "Width",
27269             width: 40
27270         },
27271         height : {
27272             title: "Height",
27273             width: 40
27274         },
27275         border : {
27276             title: "Border",
27277             width: 20
27278         }
27279     },
27280     'TD' : {
27281         width : {
27282             title: "Width",
27283             width: 40
27284         },
27285         height : {
27286             title: "Height",
27287             width: 40
27288         },   
27289         align: {
27290             title: "Align",
27291             opts : [[""],[ "left"],[ "center"],[ "right"],[ "justify"],[ "char"]],
27292             width: 80
27293         },
27294         valign: {
27295             title: "Valign",
27296             opts : [[""],[ "top"],[ "middle"],[ "bottom"],[ "baseline"]],
27297             width: 80
27298         },
27299         colspan: {
27300             title: "Colspan",
27301             width: 20
27302             
27303         },
27304          'font-family'  : {
27305             title : "Font",
27306             style : 'fontFamily',
27307             displayField: 'display',
27308             optname : 'font-family',
27309             width: 140
27310         }
27311     },
27312     'INPUT' : {
27313         name : {
27314             title: "name",
27315             width: 120
27316         },
27317         value : {
27318             title: "Value",
27319             width: 120
27320         },
27321         width : {
27322             title: "Width",
27323             width: 40
27324         }
27325     },
27326     'LABEL' : {
27327         'for' : {
27328             title: "For",
27329             width: 120
27330         }
27331     },
27332     'TEXTAREA' : {
27333           name : {
27334             title: "name",
27335             width: 120
27336         },
27337         rows : {
27338             title: "Rows",
27339             width: 20
27340         },
27341         cols : {
27342             title: "Cols",
27343             width: 20
27344         }
27345     },
27346     'SELECT' : {
27347         name : {
27348             title: "name",
27349             width: 120
27350         },
27351         selectoptions : {
27352             title: "Options",
27353             width: 200
27354         }
27355     },
27356     
27357     // should we really allow this??
27358     // should this just be 
27359     'BODY' : {
27360         title : {
27361             title: "Title",
27362             width: 200,
27363             disabled : true
27364         }
27365     },
27366     'SPAN' : {
27367         'font-family'  : {
27368             title : "Font",
27369             style : 'fontFamily',
27370             displayField: 'display',
27371             optname : 'font-family',
27372             width: 140
27373         }
27374     },
27375     'DIV' : {
27376         'font-family'  : {
27377             title : "Font",
27378             style : 'fontFamily',
27379             displayField: 'display',
27380             optname : 'font-family',
27381             width: 140
27382         }
27383     },
27384      'P' : {
27385         'font-family'  : {
27386             title : "Font",
27387             style : 'fontFamily',
27388             displayField: 'display',
27389             optname : 'font-family',
27390             width: 140
27391         }
27392     },
27393     
27394     '*' : {
27395         // empty..
27396     }
27397
27398 };
27399
27400 // this should be configurable.. - you can either set it up using stores, or modify options somehwere..
27401 Roo.form.HtmlEditor.ToolbarContext.stores = false;
27402
27403 Roo.form.HtmlEditor.ToolbarContext.options = {
27404         'font-family'  : [ 
27405                 [ 'Helvetica,Arial,sans-serif', 'Helvetica'],
27406                 [ 'Courier New', 'Courier New'],
27407                 [ 'Tahoma', 'Tahoma'],
27408                 [ 'Times New Roman,serif', 'Times'],
27409                 [ 'Verdana','Verdana' ]
27410         ]
27411 };
27412
27413 // fixme - these need to be configurable..
27414  
27415
27416 Roo.form.HtmlEditor.ToolbarContext.types
27417
27418
27419 Roo.apply(Roo.form.HtmlEditor.ToolbarContext.prototype,  {
27420     
27421     tb: false,
27422     
27423     rendered: false,
27424     
27425     editor : false,
27426     /**
27427      * @cfg {Object} disable  List of toolbar elements to disable
27428          
27429      */
27430     disable : false,
27431     /**
27432      * @cfg {Object} styles List of styles 
27433      *    eg. { '*' : [ 'headline' ] , 'TD' : [ 'underline', 'double-underline' ] } 
27434      *
27435      * These must be defined in the page, so they get rendered correctly..
27436      * .headline { }
27437      * TD.underline { }
27438      * 
27439      */
27440     styles : false,
27441     
27442     options: false,
27443     
27444     toolbars : false,
27445     
27446     init : function(editor)
27447     {
27448         this.editor = editor;
27449         
27450         
27451         var fid = editor.frameId;
27452         var etb = this;
27453         function btn(id, toggle, handler){
27454             var xid = fid + '-'+ id ;
27455             return {
27456                 id : xid,
27457                 cmd : id,
27458                 cls : 'x-btn-icon x-edit-'+id,
27459                 enableToggle:toggle !== false,
27460                 scope: editor, // was editor...
27461                 handler:handler||editor.relayBtnCmd,
27462                 clickEvent:'mousedown',
27463                 tooltip: etb.buttonTips[id] || undefined, ///tips ???
27464                 tabIndex:-1
27465             };
27466         }
27467         // create a new element.
27468         var wdiv = editor.wrap.createChild({
27469                 tag: 'div'
27470             }, editor.wrap.dom.firstChild.nextSibling, true);
27471         
27472         // can we do this more than once??
27473         
27474          // stop form submits
27475       
27476  
27477         // disable everything...
27478         var ty= Roo.form.HtmlEditor.ToolbarContext.types;
27479         this.toolbars = {};
27480            
27481         for (var i in  ty) {
27482           
27483             this.toolbars[i] = this.buildToolbar(ty[i],i);
27484         }
27485         this.tb = this.toolbars.BODY;
27486         this.tb.el.show();
27487         this.buildFooter();
27488         this.footer.show();
27489         editor.on('hide', function( ) { this.footer.hide() }, this);
27490         editor.on('show', function( ) { this.footer.show() }, this);
27491         
27492          
27493         this.rendered = true;
27494         
27495         // the all the btns;
27496         editor.on('editorevent', this.updateToolbar, this);
27497         // other toolbars need to implement this..
27498         //editor.on('editmodechange', this.updateToolbar, this);
27499     },
27500     
27501     
27502     
27503     /**
27504      * Protected method that will not generally be called directly. It triggers
27505      * a toolbar update by reading the markup state of the current selection in the editor.
27506      */
27507     updateToolbar: function(editor,ev,sel){
27508
27509         //Roo.log(ev);
27510         // capture mouse up - this is handy for selecting images..
27511         // perhaps should go somewhere else...
27512         if(!this.editor.activated){
27513              this.editor.onFirstFocus();
27514             return;
27515         }
27516         
27517         // http://developer.yahoo.com/yui/docs/simple-editor.js.html
27518         // selectNode - might want to handle IE?
27519         if (ev &&
27520             (ev.type == 'mouseup' || ev.type == 'click' ) &&
27521             ev.target && ev.target.tagName == 'IMG') {
27522             // they have click on an image...
27523             // let's see if we can change the selection...
27524             sel = ev.target;
27525          
27526               var nodeRange = sel.ownerDocument.createRange();
27527             try {
27528                 nodeRange.selectNode(sel);
27529             } catch (e) {
27530                 nodeRange.selectNodeContents(sel);
27531             }
27532             //nodeRange.collapse(true);
27533             var s = editor.win.getSelection();
27534             s.removeAllRanges();
27535             s.addRange(nodeRange);
27536         }  
27537         
27538       
27539         var updateFooter = sel ? false : true;
27540         
27541         
27542         var ans = this.editor.getAllAncestors();
27543         
27544         // pick
27545         var ty= Roo.form.HtmlEditor.ToolbarContext.types;
27546         
27547         if (!sel) { 
27548             sel = ans.length ? (ans[0] ?  ans[0]  : ans[1]) : this.editor.doc.body;
27549             sel = sel ? sel : this.editor.doc.body;
27550             sel = sel.tagName.length ? sel : this.editor.doc.body;
27551             
27552         }
27553         // pick a menu that exists..
27554         var tn = sel.tagName.toUpperCase();
27555         //sel = typeof(ty[tn]) != 'undefined' ? sel : this.editor.doc.body;
27556         
27557         tn = sel.tagName.toUpperCase();
27558         
27559         var lastSel = this.tb.selectedNode
27560         
27561         this.tb.selectedNode = sel;
27562         
27563         // if current menu does not match..
27564         if ((this.tb.name != tn) || (lastSel != this.tb.selectedNode)) {
27565                 
27566             this.tb.el.hide();
27567             ///console.log("show: " + tn);
27568             this.tb =  typeof(ty[tn]) != 'undefined' ? this.toolbars[tn] : this.toolbars['*'];
27569             this.tb.el.show();
27570             // update name
27571             this.tb.items.first().el.innerHTML = tn + ':&nbsp;';
27572             
27573             
27574             // update attributes
27575             if (this.tb.fields) {
27576                 this.tb.fields.each(function(e) {
27577                     if (e.stylename) {
27578                         e.setValue(sel.style[e.stylename]);
27579                         return;
27580                     } 
27581                    e.setValue(sel.getAttribute(e.attrname));
27582                 });
27583             }
27584             
27585             var hasStyles = false;
27586             for(var i in this.styles) {
27587                 hasStyles = true;
27588                 break;
27589             }
27590             
27591             // update styles
27592             if (hasStyles) { 
27593                 var st = this.tb.fields.item(0);
27594                 
27595                 st.store.removeAll();
27596                
27597                 
27598                 var cn = sel.className.split(/\s+/);
27599                 
27600                 var avs = [];
27601                 if (this.styles['*']) {
27602                     
27603                     Roo.each(this.styles['*'], function(v) {
27604                         avs.push( [ v , cn.indexOf(v) > -1 ? 1 : 0 ] );         
27605                     });
27606                 }
27607                 if (this.styles[tn]) { 
27608                     Roo.each(this.styles[tn], function(v) {
27609                         avs.push( [ v , cn.indexOf(v) > -1 ? 1 : 0 ] );         
27610                     });
27611                 }
27612                 
27613                 st.store.loadData(avs);
27614                 st.collapse();
27615                 st.setValue(cn);
27616             }
27617             // flag our selected Node.
27618             this.tb.selectedNode = sel;
27619            
27620            
27621             Roo.menu.MenuMgr.hideAll();
27622
27623         }
27624         
27625         if (!updateFooter) {
27626             //this.footDisp.dom.innerHTML = ''; 
27627             return;
27628         }
27629         // update the footer
27630         //
27631         var html = '';
27632         
27633         this.footerEls = ans.reverse();
27634         Roo.each(this.footerEls, function(a,i) {
27635             if (!a) { return; }
27636             html += html.length ? ' &gt; '  :  '';
27637             
27638             html += '<span class="x-ed-loc-' + i + '">' + a.tagName + '</span>';
27639             
27640         });
27641        
27642         // 
27643         var sz = this.footDisp.up('td').getSize();
27644         this.footDisp.dom.style.width = (sz.width -10) + 'px';
27645         this.footDisp.dom.style.marginLeft = '5px';
27646         
27647         this.footDisp.dom.style.overflow = 'hidden';
27648         
27649         this.footDisp.dom.innerHTML = html;
27650             
27651         //this.editorsyncValue();
27652     },
27653      
27654     
27655    
27656        
27657     // private
27658     onDestroy : function(){
27659         if(this.rendered){
27660             
27661             this.tb.items.each(function(item){
27662                 if(item.menu){
27663                     item.menu.removeAll();
27664                     if(item.menu.el){
27665                         item.menu.el.destroy();
27666                     }
27667                 }
27668                 item.destroy();
27669             });
27670              
27671         }
27672     },
27673     onFirstFocus: function() {
27674         // need to do this for all the toolbars..
27675         this.tb.items.each(function(item){
27676            item.enable();
27677         });
27678     },
27679     buildToolbar: function(tlist, nm)
27680     {
27681         var editor = this.editor;
27682          // create a new element.
27683         var wdiv = editor.wrap.createChild({
27684                 tag: 'div'
27685             }, editor.wrap.dom.firstChild.nextSibling, true);
27686         
27687        
27688         var tb = new Roo.Toolbar(wdiv);
27689         // add the name..
27690         
27691         tb.add(nm+ ":&nbsp;");
27692         
27693         var styles = [];
27694         for(var i in this.styles) {
27695             styles.push(i);
27696         }
27697         
27698         // styles...
27699         if (styles && styles.length) {
27700             
27701             // this needs a multi-select checkbox...
27702             tb.addField( new Roo.form.ComboBox({
27703                 store: new Roo.data.SimpleStore({
27704                     id : 'val',
27705                     fields: ['val', 'selected'],
27706                     data : [] 
27707                 }),
27708                 name : '-roo-edit-className',
27709                 attrname : 'className',
27710                 displayField: 'val',
27711                 typeAhead: false,
27712                 mode: 'local',
27713                 editable : false,
27714                 triggerAction: 'all',
27715                 emptyText:'Select Style',
27716                 selectOnFocus:true,
27717                 width: 130,
27718                 listeners : {
27719                     'select': function(c, r, i) {
27720                         // initial support only for on class per el..
27721                         tb.selectedNode.className =  r ? r.get('val') : '';
27722                         editor.syncValue();
27723                     }
27724                 }
27725     
27726             }));
27727         }
27728         
27729         var tbc = Roo.form.HtmlEditor.ToolbarContext;
27730         var tbops = tbc.options;
27731         
27732         for (var i in tlist) {
27733             
27734             var item = tlist[i];
27735             tb.add(item.title + ":&nbsp;");
27736             
27737             
27738             //optname == used so you can configure the options available..
27739             var opts = item.opts ? item.opts : false;
27740             if (item.optname) {
27741                 opts = tbops[item.optname];
27742            
27743             }
27744             
27745             if (opts) {
27746                 // opts == pulldown..
27747                 tb.addField( new Roo.form.ComboBox({
27748                     store:   typeof(tbc.stores[i]) != 'undefined' ?  Roo.factory(tbc.stores[i],Roo.data) : new Roo.data.SimpleStore({
27749                         id : 'val',
27750                         fields: ['val', 'display'],
27751                         data : opts  
27752                     }),
27753                     name : '-roo-edit-' + i,
27754                     attrname : i,
27755                     stylename : item.style ? item.style : false,
27756                     displayField: item.displayField ? item.displayField : 'val',
27757                     valueField :  'val',
27758                     typeAhead: false,
27759                     mode: typeof(tbc.stores[i]) != 'undefined'  ? 'remote' : 'local',
27760                     editable : false,
27761                     triggerAction: 'all',
27762                     emptyText:'Select',
27763                     selectOnFocus:true,
27764                     width: item.width ? item.width  : 130,
27765                     listeners : {
27766                         'select': function(c, r, i) {
27767                             if (c.stylename) {
27768                                 tb.selectedNode.style[c.stylename] =  r.get('val');
27769                                 return;
27770                             }
27771                             tb.selectedNode.setAttribute(c.attrname, r.get('val'));
27772                         }
27773                     }
27774
27775                 }));
27776                 continue;
27777                     
27778                  
27779                 
27780                 tb.addField( new Roo.form.TextField({
27781                     name: i,
27782                     width: 100,
27783                     //allowBlank:false,
27784                     value: ''
27785                 }));
27786                 continue;
27787             }
27788             tb.addField( new Roo.form.TextField({
27789                 name: '-roo-edit-' + i,
27790                 attrname : i,
27791                 
27792                 width: item.width,
27793                 //allowBlank:true,
27794                 value: '',
27795                 listeners: {
27796                     'change' : function(f, nv, ov) {
27797                         tb.selectedNode.setAttribute(f.attrname, nv);
27798                     }
27799                 }
27800             }));
27801              
27802         }
27803         tb.addFill();
27804         var _this = this;
27805         tb.addButton( {
27806             text: 'Remove Tag',
27807     
27808             listeners : {
27809                 click : function ()
27810                 {
27811                     // remove
27812                     // undo does not work.
27813                      
27814                     var sn = tb.selectedNode;
27815                     
27816                     var pn = sn.parentNode;
27817                     
27818                     var stn =  sn.childNodes[0];
27819                     var en = sn.childNodes[sn.childNodes.length - 1 ];
27820                     while (sn.childNodes.length) {
27821                         var node = sn.childNodes[0];
27822                         sn.removeChild(node);
27823                         //Roo.log(node);
27824                         pn.insertBefore(node, sn);
27825                         
27826                     }
27827                     pn.removeChild(sn);
27828                     var range = editor.createRange();
27829         
27830                     range.setStart(stn,0);
27831                     range.setEnd(en,0); //????
27832                     //range.selectNode(sel);
27833                     
27834                     
27835                     var selection = editor.getSelection();
27836                     selection.removeAllRanges();
27837                     selection.addRange(range);
27838                     
27839                     
27840                     
27841                     //_this.updateToolbar(null, null, pn);
27842                     _this.updateToolbar(null, null, null);
27843                     _this.footDisp.dom.innerHTML = ''; 
27844                 }
27845             }
27846             
27847                     
27848                 
27849             
27850         });
27851         
27852         
27853         tb.el.on('click', function(e){
27854             e.preventDefault(); // what does this do?
27855         });
27856         tb.el.setVisibilityMode( Roo.Element.DISPLAY);
27857         tb.el.hide();
27858         tb.name = nm;
27859         // dont need to disable them... as they will get hidden
27860         return tb;
27861          
27862         
27863     },
27864     buildFooter : function()
27865     {
27866         
27867         var fel = this.editor.wrap.createChild();
27868         this.footer = new Roo.Toolbar(fel);
27869         // toolbar has scrolly on left / right?
27870         var footDisp= new Roo.Toolbar.Fill();
27871         var _t = this;
27872         this.footer.add(
27873             {
27874                 text : '&lt;',
27875                 xtype: 'Button',
27876                 handler : function() {
27877                     _t.footDisp.scrollTo('left',0,true)
27878                 }
27879             }
27880         );
27881         this.footer.add( footDisp );
27882         this.footer.add( 
27883             {
27884                 text : '&gt;',
27885                 xtype: 'Button',
27886                 handler : function() {
27887                     // no animation..
27888                     _t.footDisp.select('span').last().scrollIntoView(_t.footDisp,true);
27889                 }
27890             }
27891         );
27892         var fel = Roo.get(footDisp.el);
27893         fel.addClass('x-editor-context');
27894         this.footDispWrap = fel; 
27895         this.footDispWrap.overflow  = 'hidden';
27896         
27897         this.footDisp = fel.createChild();
27898         this.footDispWrap.on('click', this.onContextClick, this)
27899         
27900         
27901     },
27902     onContextClick : function (ev,dom)
27903     {
27904         ev.preventDefault();
27905         var  cn = dom.className;
27906         //Roo.log(cn);
27907         if (!cn.match(/x-ed-loc-/)) {
27908             return;
27909         }
27910         var n = cn.split('-').pop();
27911         var ans = this.footerEls;
27912         var sel = ans[n];
27913         
27914          // pick
27915         var range = this.editor.createRange();
27916         
27917         range.selectNodeContents(sel);
27918         //range.selectNode(sel);
27919         
27920         
27921         var selection = this.editor.getSelection();
27922         selection.removeAllRanges();
27923         selection.addRange(range);
27924         
27925         
27926         
27927         this.updateToolbar(null, null, sel);
27928         
27929         
27930     }
27931     
27932     
27933     
27934     
27935     
27936 });
27937
27938
27939
27940
27941
27942 /*
27943  * Based on:
27944  * Ext JS Library 1.1.1
27945  * Copyright(c) 2006-2007, Ext JS, LLC.
27946  *
27947  * Originally Released Under LGPL - original licence link has changed is not relivant.
27948  *
27949  * Fork - LGPL
27950  * <script type="text/javascript">
27951  */
27952  
27953 /**
27954  * @class Roo.form.BasicForm
27955  * @extends Roo.util.Observable
27956  * Supplies the functionality to do "actions" on forms and initialize Roo.form.Field types on existing markup.
27957  * @constructor
27958  * @param {String/HTMLElement/Roo.Element} el The form element or its id
27959  * @param {Object} config Configuration options
27960  */
27961 Roo.form.BasicForm = function(el, config){
27962     this.allItems = [];
27963     this.childForms = [];
27964     Roo.apply(this, config);
27965     /*
27966      * The Roo.form.Field items in this form.
27967      * @type MixedCollection
27968      */
27969      
27970      
27971     this.items = new Roo.util.MixedCollection(false, function(o){
27972         return o.id || (o.id = Roo.id());
27973     });
27974     this.addEvents({
27975         /**
27976          * @event beforeaction
27977          * Fires before any action is performed. Return false to cancel the action.
27978          * @param {Form} this
27979          * @param {Action} action The action to be performed
27980          */
27981         beforeaction: true,
27982         /**
27983          * @event actionfailed
27984          * Fires when an action fails.
27985          * @param {Form} this
27986          * @param {Action} action The action that failed
27987          */
27988         actionfailed : true,
27989         /**
27990          * @event actioncomplete
27991          * Fires when an action is completed.
27992          * @param {Form} this
27993          * @param {Action} action The action that completed
27994          */
27995         actioncomplete : true
27996     });
27997     if(el){
27998         this.initEl(el);
27999     }
28000     Roo.form.BasicForm.superclass.constructor.call(this);
28001 };
28002
28003 Roo.extend(Roo.form.BasicForm, Roo.util.Observable, {
28004     /**
28005      * @cfg {String} method
28006      * The request method to use (GET or POST) for form actions if one isn't supplied in the action options.
28007      */
28008     /**
28009      * @cfg {DataReader} reader
28010      * An Roo.data.DataReader (e.g. {@link Roo.data.XmlReader}) to be used to read data when executing "load" actions.
28011      * This is optional as there is built-in support for processing JSON.
28012      */
28013     /**
28014      * @cfg {DataReader} errorReader
28015      * An Roo.data.DataReader (e.g. {@link Roo.data.XmlReader}) to be used to read data when reading validation errors on "submit" actions.
28016      * This is completely optional as there is built-in support for processing JSON.
28017      */
28018     /**
28019      * @cfg {String} url
28020      * The URL to use for form actions if one isn't supplied in the action options.
28021      */
28022     /**
28023      * @cfg {Boolean} fileUpload
28024      * Set to true if this form is a file upload.
28025      */
28026      
28027     /**
28028      * @cfg {Object} baseParams
28029      * Parameters to pass with all requests. e.g. baseParams: {id: '123', foo: 'bar'}.
28030      */
28031      /**
28032      
28033     /**
28034      * @cfg {Number} timeout Timeout for form actions in seconds (default is 30 seconds).
28035      */
28036     timeout: 30,
28037
28038     // private
28039     activeAction : null,
28040
28041     /**
28042      * @cfg {Boolean} trackResetOnLoad If set to true, form.reset() resets to the last loaded
28043      * or setValues() data instead of when the form was first created.
28044      */
28045     trackResetOnLoad : false,
28046     
28047     
28048     /**
28049      * childForms - used for multi-tab forms
28050      * @type {Array}
28051      */
28052     childForms : false,
28053     
28054     /**
28055      * allItems - full list of fields.
28056      * @type {Array}
28057      */
28058     allItems : false,
28059     
28060     /**
28061      * By default wait messages are displayed with Roo.MessageBox.wait. You can target a specific
28062      * element by passing it or its id or mask the form itself by passing in true.
28063      * @type Mixed
28064      */
28065     waitMsgTarget : false,
28066
28067     // private
28068     initEl : function(el){
28069         this.el = Roo.get(el);
28070         this.id = this.el.id || Roo.id();
28071         this.el.on('submit', this.onSubmit, this);
28072         this.el.addClass('x-form');
28073     },
28074
28075     // private
28076     onSubmit : function(e){
28077         e.stopEvent();
28078     },
28079
28080     /**
28081      * Returns true if client-side validation on the form is successful.
28082      * @return Boolean
28083      */
28084     isValid : function(){
28085         var valid = true;
28086         this.items.each(function(f){
28087            if(!f.validate()){
28088                valid = false;
28089            }
28090         });
28091         return valid;
28092     },
28093
28094     /**
28095      * Returns true if any fields in this form have changed since their original load.
28096      * @return Boolean
28097      */
28098     isDirty : function(){
28099         var dirty = false;
28100         this.items.each(function(f){
28101            if(f.isDirty()){
28102                dirty = true;
28103                return false;
28104            }
28105         });
28106         return dirty;
28107     },
28108
28109     /**
28110      * Performs a predefined action (submit or load) or custom actions you define on this form.
28111      * @param {String} actionName The name of the action type
28112      * @param {Object} options (optional) The options to pass to the action.  All of the config options listed
28113      * below are supported by both the submit and load actions unless otherwise noted (custom actions could also
28114      * accept other config options):
28115      * <pre>
28116 Property          Type             Description
28117 ----------------  ---------------  ----------------------------------------------------------------------------------
28118 url               String           The url for the action (defaults to the form's url)
28119 method            String           The form method to use (defaults to the form's method, or POST if not defined)
28120 params            String/Object    The params to pass (defaults to the form's baseParams, or none if not defined)
28121 clientValidation  Boolean          Applies to submit only.  Pass true to call form.isValid() prior to posting to
28122                                    validate the form on the client (defaults to false)
28123      * </pre>
28124      * @return {BasicForm} this
28125      */
28126     doAction : function(action, options){
28127         if(typeof action == 'string'){
28128             action = new Roo.form.Action.ACTION_TYPES[action](this, options);
28129         }
28130         if(this.fireEvent('beforeaction', this, action) !== false){
28131             this.beforeAction(action);
28132             action.run.defer(100, action);
28133         }
28134         return this;
28135     },
28136
28137     /**
28138      * Shortcut to do a submit action.
28139      * @param {Object} options The options to pass to the action (see {@link #doAction} for details)
28140      * @return {BasicForm} this
28141      */
28142     submit : function(options){
28143         this.doAction('submit', options);
28144         return this;
28145     },
28146
28147     /**
28148      * Shortcut to do a load action.
28149      * @param {Object} options The options to pass to the action (see {@link #doAction} for details)
28150      * @return {BasicForm} this
28151      */
28152     load : function(options){
28153         this.doAction('load', options);
28154         return this;
28155     },
28156
28157     /**
28158      * Persists the values in this form into the passed Roo.data.Record object in a beginEdit/endEdit block.
28159      * @param {Record} record The record to edit
28160      * @return {BasicForm} this
28161      */
28162     updateRecord : function(record){
28163         record.beginEdit();
28164         var fs = record.fields;
28165         fs.each(function(f){
28166             var field = this.findField(f.name);
28167             if(field){
28168                 record.set(f.name, field.getValue());
28169             }
28170         }, this);
28171         record.endEdit();
28172         return this;
28173     },
28174
28175     /**
28176      * Loads an Roo.data.Record into this form.
28177      * @param {Record} record The record to load
28178      * @return {BasicForm} this
28179      */
28180     loadRecord : function(record){
28181         this.setValues(record.data);
28182         return this;
28183     },
28184
28185     // private
28186     beforeAction : function(action){
28187         var o = action.options;
28188         
28189        
28190         if(this.waitMsgTarget === true){
28191             this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
28192         }else if(this.waitMsgTarget){
28193             this.waitMsgTarget = Roo.get(this.waitMsgTarget);
28194             this.waitMsgTarget.mask(o.waitMsg || "Sending", 'x-mask-loading');
28195         }else {
28196             Roo.MessageBox.wait(o.waitMsg || "Sending", o.waitTitle || this.waitTitle || 'Please Wait...');
28197         }
28198          
28199     },
28200
28201     // private
28202     afterAction : function(action, success){
28203         this.activeAction = null;
28204         var o = action.options;
28205         
28206         if(this.waitMsgTarget === true){
28207             this.el.unmask();
28208         }else if(this.waitMsgTarget){
28209             this.waitMsgTarget.unmask();
28210         }else{
28211             Roo.MessageBox.updateProgress(1);
28212             Roo.MessageBox.hide();
28213         }
28214          
28215         if(success){
28216             if(o.reset){
28217                 this.reset();
28218             }
28219             Roo.callback(o.success, o.scope, [this, action]);
28220             this.fireEvent('actioncomplete', this, action);
28221             
28222         }else{
28223             
28224             // failure condition..
28225             // we have a scenario where updates need confirming.
28226             // eg. if a locking scenario exists..
28227             // we look for { errors : { needs_confirm : true }} in the response.
28228             if (
28229                 (typeof(action.result) != 'undefined')  &&
28230                 (typeof(action.result.errors) != 'undefined')  &&
28231                 (typeof(action.result.errors.needs_confirm) != 'undefined')
28232            ){
28233                 var _t = this;
28234                 Roo.MessageBox.confirm(
28235                     "Change requires confirmation",
28236                     action.result.errorMsg,
28237                     function(r) {
28238                         if (r != 'yes') {
28239                             return;
28240                         }
28241                         _t.doAction('submit', { params :  { _submit_confirmed : 1 } }  );
28242                     }
28243                     
28244                 );
28245                 
28246                 
28247                 
28248                 return;
28249             }
28250             
28251             Roo.callback(o.failure, o.scope, [this, action]);
28252             // show an error message if no failed handler is set..
28253             if (!this.hasListener('actionfailed')) {
28254                 Roo.MessageBox.alert("Error",
28255                     (typeof(action.result) != 'undefined' && typeof(action.result.errorMsg) != 'undefined') ?
28256                         action.result.errorMsg :
28257                         "Saving Failed, please check your entries or try again"
28258                 );
28259             }
28260             
28261             this.fireEvent('actionfailed', this, action);
28262         }
28263         
28264     },
28265
28266     /**
28267      * Find a Roo.form.Field in this form by id, dataIndex, name or hiddenName
28268      * @param {String} id The value to search for
28269      * @return Field
28270      */
28271     findField : function(id){
28272         var field = this.items.get(id);
28273         if(!field){
28274             this.items.each(function(f){
28275                 if(f.isFormField && (f.dataIndex == id || f.id == id || f.getName() == id)){
28276                     field = f;
28277                     return false;
28278                 }
28279             });
28280         }
28281         return field || null;
28282     },
28283
28284     /**
28285      * Add a secondary form to this one, 
28286      * Used to provide tabbed forms. One form is primary, with hidden values 
28287      * which mirror the elements from the other forms.
28288      * 
28289      * @param {Roo.form.Form} form to add.
28290      * 
28291      */
28292     addForm : function(form)
28293     {
28294        
28295         if (this.childForms.indexOf(form) > -1) {
28296             // already added..
28297             return;
28298         }
28299         this.childForms.push(form);
28300         var n = '';
28301         Roo.each(form.allItems, function (fe) {
28302             
28303             n = typeof(fe.getName) == 'undefined' ? fe.name : fe.getName();
28304             if (this.findField(n)) { // already added..
28305                 return;
28306             }
28307             var add = new Roo.form.Hidden({
28308                 name : n
28309             });
28310             add.render(this.el);
28311             
28312             this.add( add );
28313         }, this);
28314         
28315     },
28316     /**
28317      * Mark fields in this form invalid in bulk.
28318      * @param {Array/Object} errors Either an array in the form [{id:'fieldId', msg:'The message'},...] or an object hash of {id: msg, id2: msg2}
28319      * @return {BasicForm} this
28320      */
28321     markInvalid : function(errors){
28322         if(errors instanceof Array){
28323             for(var i = 0, len = errors.length; i < len; i++){
28324                 var fieldError = errors[i];
28325                 var f = this.findField(fieldError.id);
28326                 if(f){
28327                     f.markInvalid(fieldError.msg);
28328                 }
28329             }
28330         }else{
28331             var field, id;
28332             for(id in errors){
28333                 if(typeof errors[id] != 'function' && (field = this.findField(id))){
28334                     field.markInvalid(errors[id]);
28335                 }
28336             }
28337         }
28338         Roo.each(this.childForms || [], function (f) {
28339             f.markInvalid(errors);
28340         });
28341         
28342         return this;
28343     },
28344
28345     /**
28346      * Set values for fields in this form in bulk.
28347      * @param {Array/Object} values Either an array in the form [{id:'fieldId', value:'foo'},...] or an object hash of {id: value, id2: value2}
28348      * @return {BasicForm} this
28349      */
28350     setValues : function(values){
28351         if(values instanceof Array){ // array of objects
28352             for(var i = 0, len = values.length; i < len; i++){
28353                 var v = values[i];
28354                 var f = this.findField(v.id);
28355                 if(f){
28356                     f.setValue(v.value);
28357                     if(this.trackResetOnLoad){
28358                         f.originalValue = f.getValue();
28359                     }
28360                 }
28361             }
28362         }else{ // object hash
28363             var field, id;
28364             for(id in values){
28365                 if(typeof values[id] != 'function' && (field = this.findField(id))){
28366                     
28367                     if (field.setFromData && 
28368                         field.valueField && 
28369                         field.displayField &&
28370                         // combos' with local stores can 
28371                         // be queried via setValue()
28372                         // to set their value..
28373                         (field.store && !field.store.isLocal)
28374                         ) {
28375                         // it's a combo
28376                         var sd = { };
28377                         sd[field.valueField] = typeof(values[field.hiddenName]) == 'undefined' ? '' : values[field.hiddenName];
28378                         sd[field.displayField] = typeof(values[field.name]) == 'undefined' ? '' : values[field.name];
28379                         field.setFromData(sd);
28380                         
28381                     } else {
28382                         field.setValue(values[id]);
28383                     }
28384                     
28385                     
28386                     if(this.trackResetOnLoad){
28387                         field.originalValue = field.getValue();
28388                     }
28389                 }
28390             }
28391         }
28392          
28393         Roo.each(this.childForms || [], function (f) {
28394             f.setValues(values);
28395         });
28396                 
28397         return this;
28398     },
28399
28400     /**
28401      * Returns the fields in this form as an object with key/value pairs. If multiple fields exist with the same name
28402      * they are returned as an array.
28403      * @param {Boolean} asString
28404      * @return {Object}
28405      */
28406     getValues : function(asString){
28407         if (this.childForms) {
28408             // copy values from the child forms
28409             Roo.each(this.childForms, function (f) {
28410                 this.setValues(f.getValues());
28411             }, this);
28412         }
28413         
28414         
28415         
28416         var fs = Roo.lib.Ajax.serializeForm(this.el.dom);
28417         if(asString === true){
28418             return fs;
28419         }
28420         return Roo.urlDecode(fs);
28421     },
28422     
28423     /**
28424      * Returns the fields in this form as an object with key/value pairs. 
28425      * This differs from getValues as it calls getValue on each child item, rather than using dom data.
28426      * @return {Object}
28427      */
28428     getFieldValues : function(with_hidden)
28429     {
28430         if (this.childForms) {
28431             // copy values from the child forms
28432             // should this call getFieldValues - probably not as we do not currently copy
28433             // hidden fields when we generate..
28434             Roo.each(this.childForms, function (f) {
28435                 this.setValues(f.getValues());
28436             }, this);
28437         }
28438         
28439         var ret = {};
28440         this.items.each(function(f){
28441             if (!f.getName()) {
28442                 return;
28443             }
28444             var v = f.getValue();
28445             // not sure if this supported any more..
28446             if ((typeof(v) == 'object') && f.getRawValue) {
28447                 v = f.getRawValue() ; // dates..
28448             }
28449             // combo boxes where name != hiddenName...
28450             if (f.name != f.getName()) {
28451                 ret[f.name] = f.getRawValue();
28452             }
28453             ret[f.getName()] = v;
28454         });
28455         
28456         return ret;
28457     },
28458
28459     /**
28460      * Clears all invalid messages in this form.
28461      * @return {BasicForm} this
28462      */
28463     clearInvalid : function(){
28464         this.items.each(function(f){
28465            f.clearInvalid();
28466         });
28467         
28468         Roo.each(this.childForms || [], function (f) {
28469             f.clearInvalid();
28470         });
28471         
28472         
28473         return this;
28474     },
28475
28476     /**
28477      * Resets this form.
28478      * @return {BasicForm} this
28479      */
28480     reset : function(){
28481         this.items.each(function(f){
28482             f.reset();
28483         });
28484         
28485         Roo.each(this.childForms || [], function (f) {
28486             f.reset();
28487         });
28488        
28489         
28490         return this;
28491     },
28492
28493     /**
28494      * Add Roo.form components to this form.
28495      * @param {Field} field1
28496      * @param {Field} field2 (optional)
28497      * @param {Field} etc (optional)
28498      * @return {BasicForm} this
28499      */
28500     add : function(){
28501         this.items.addAll(Array.prototype.slice.call(arguments, 0));
28502         return this;
28503     },
28504
28505
28506     /**
28507      * Removes a field from the items collection (does NOT remove its markup).
28508      * @param {Field} field
28509      * @return {BasicForm} this
28510      */
28511     remove : function(field){
28512         this.items.remove(field);
28513         return this;
28514     },
28515
28516     /**
28517      * Looks at the fields in this form, checks them for an id attribute,
28518      * and calls applyTo on the existing dom element with that id.
28519      * @return {BasicForm} this
28520      */
28521     render : function(){
28522         this.items.each(function(f){
28523             if(f.isFormField && !f.rendered && document.getElementById(f.id)){ // if the element exists
28524                 f.applyTo(f.id);
28525             }
28526         });
28527         return this;
28528     },
28529
28530     /**
28531      * Calls {@link Ext#apply} for all fields in this form with the passed object.
28532      * @param {Object} values
28533      * @return {BasicForm} this
28534      */
28535     applyToFields : function(o){
28536         this.items.each(function(f){
28537            Roo.apply(f, o);
28538         });
28539         return this;
28540     },
28541
28542     /**
28543      * Calls {@link Ext#applyIf} for all field in this form with the passed object.
28544      * @param {Object} values
28545      * @return {BasicForm} this
28546      */
28547     applyIfToFields : function(o){
28548         this.items.each(function(f){
28549            Roo.applyIf(f, o);
28550         });
28551         return this;
28552     }
28553 });
28554
28555 // back compat
28556 Roo.BasicForm = Roo.form.BasicForm;/*
28557  * Based on:
28558  * Ext JS Library 1.1.1
28559  * Copyright(c) 2006-2007, Ext JS, LLC.
28560  *
28561  * Originally Released Under LGPL - original licence link has changed is not relivant.
28562  *
28563  * Fork - LGPL
28564  * <script type="text/javascript">
28565  */
28566
28567 /**
28568  * @class Roo.form.Form
28569  * @extends Roo.form.BasicForm
28570  * Adds the ability to dynamically render forms with JavaScript to {@link Roo.form.BasicForm}.
28571  * @constructor
28572  * @param {Object} config Configuration options
28573  */
28574 Roo.form.Form = function(config){
28575     var xitems =  [];
28576     if (config.items) {
28577         xitems = config.items;
28578         delete config.items;
28579     }
28580    
28581     
28582     Roo.form.Form.superclass.constructor.call(this, null, config);
28583     this.url = this.url || this.action;
28584     if(!this.root){
28585         this.root = new Roo.form.Layout(Roo.applyIf({
28586             id: Roo.id()
28587         }, config));
28588     }
28589     this.active = this.root;
28590     /**
28591      * Array of all the buttons that have been added to this form via {@link addButton}
28592      * @type Array
28593      */
28594     this.buttons = [];
28595     this.allItems = [];
28596     this.addEvents({
28597         /**
28598          * @event clientvalidation
28599          * If the monitorValid config option is true, this event fires repetitively to notify of valid state
28600          * @param {Form} this
28601          * @param {Boolean} valid true if the form has passed client-side validation
28602          */
28603         clientvalidation: true,
28604         /**
28605          * @event rendered
28606          * Fires when the form is rendered
28607          * @param {Roo.form.Form} form
28608          */
28609         rendered : true
28610     });
28611     
28612     if (this.progressUrl) {
28613             // push a hidden field onto the list of fields..
28614             this.addxtype( {
28615                     xns: Roo.form, 
28616                     xtype : 'Hidden', 
28617                     name : 'UPLOAD_IDENTIFIER' 
28618             });
28619         }
28620         
28621     
28622     Roo.each(xitems, this.addxtype, this);
28623     
28624     
28625     
28626 };
28627
28628 Roo.extend(Roo.form.Form, Roo.form.BasicForm, {
28629     /**
28630      * @cfg {Number} labelWidth The width of labels. This property cascades to child containers.
28631      */
28632     /**
28633      * @cfg {String} itemCls A css class to apply to the x-form-item of fields. This property cascades to child containers.
28634      */
28635     /**
28636      * @cfg {String} buttonAlign Valid values are "left," "center" and "right" (defaults to "center")
28637      */
28638     buttonAlign:'center',
28639
28640     /**
28641      * @cfg {Number} minButtonWidth Minimum width of all buttons in pixels (defaults to 75)
28642      */
28643     minButtonWidth:75,
28644
28645     /**
28646      * @cfg {String} labelAlign Valid values are "left," "top" and "right" (defaults to "left").
28647      * This property cascades to child containers if not set.
28648      */
28649     labelAlign:'left',
28650
28651     /**
28652      * @cfg {Boolean} monitorValid If true the form monitors its valid state <b>client-side</b> and
28653      * fires a looping event with that state. This is required to bind buttons to the valid
28654      * state using the config value formBind:true on the button.
28655      */
28656     monitorValid : false,
28657
28658     /**
28659      * @cfg {Number} monitorPoll The milliseconds to poll valid state, ignored if monitorValid is not true (defaults to 200)
28660      */
28661     monitorPoll : 200,
28662     
28663     /**
28664      * @cfg {String} progressUrl - Url to return progress data 
28665      */
28666     
28667     progressUrl : false,
28668   
28669     /**
28670      * Opens a new {@link Roo.form.Column} container in the layout stack. If fields are passed after the config, the
28671      * fields are added and the column is closed. If no fields are passed the column remains open
28672      * until end() is called.
28673      * @param {Object} config The config to pass to the column
28674      * @param {Field} field1 (optional)
28675      * @param {Field} field2 (optional)
28676      * @param {Field} etc (optional)
28677      * @return Column The column container object
28678      */
28679     column : function(c){
28680         var col = new Roo.form.Column(c);
28681         this.start(col);
28682         if(arguments.length > 1){ // duplicate code required because of Opera
28683             this.add.apply(this, Array.prototype.slice.call(arguments, 1));
28684             this.end();
28685         }
28686         return col;
28687     },
28688
28689     /**
28690      * Opens a new {@link Roo.form.FieldSet} container in the layout stack. If fields are passed after the config, the
28691      * fields are added and the fieldset is closed. If no fields are passed the fieldset remains open
28692      * until end() is called.
28693      * @param {Object} config The config to pass to the fieldset
28694      * @param {Field} field1 (optional)
28695      * @param {Field} field2 (optional)
28696      * @param {Field} etc (optional)
28697      * @return FieldSet The fieldset container object
28698      */
28699     fieldset : function(c){
28700         var fs = new Roo.form.FieldSet(c);
28701         this.start(fs);
28702         if(arguments.length > 1){ // duplicate code required because of Opera
28703             this.add.apply(this, Array.prototype.slice.call(arguments, 1));
28704             this.end();
28705         }
28706         return fs;
28707     },
28708
28709     /**
28710      * Opens a new {@link Roo.form.Layout} container in the layout stack. If fields are passed after the config, the
28711      * fields are added and the container is closed. If no fields are passed the container remains open
28712      * until end() is called.
28713      * @param {Object} config The config to pass to the Layout
28714      * @param {Field} field1 (optional)
28715      * @param {Field} field2 (optional)
28716      * @param {Field} etc (optional)
28717      * @return Layout The container object
28718      */
28719     container : function(c){
28720         var l = new Roo.form.Layout(c);
28721         this.start(l);
28722         if(arguments.length > 1){ // duplicate code required because of Opera
28723             this.add.apply(this, Array.prototype.slice.call(arguments, 1));
28724             this.end();
28725         }
28726         return l;
28727     },
28728
28729     /**
28730      * Opens the passed container in the layout stack. The container can be any {@link Roo.form.Layout} or subclass.
28731      * @param {Object} container A Roo.form.Layout or subclass of Layout
28732      * @return {Form} this
28733      */
28734     start : function(c){
28735         // cascade label info
28736         Roo.applyIf(c, {'labelAlign': this.active.labelAlign, 'labelWidth': this.active.labelWidth, 'itemCls': this.active.itemCls});
28737         this.active.stack.push(c);
28738         c.ownerCt = this.active;
28739         this.active = c;
28740         return this;
28741     },
28742
28743     /**
28744      * Closes the current open container
28745      * @return {Form} this
28746      */
28747     end : function(){
28748         if(this.active == this.root){
28749             return this;
28750         }
28751         this.active = this.active.ownerCt;
28752         return this;
28753     },
28754
28755     /**
28756      * Add Roo.form components to the current open container (e.g. column, fieldset, etc.).  Fields added via this method
28757      * can also be passed with an additional property of fieldLabel, which if supplied, will provide the text to display
28758      * as the label of the field.
28759      * @param {Field} field1
28760      * @param {Field} field2 (optional)
28761      * @param {Field} etc. (optional)
28762      * @return {Form} this
28763      */
28764     add : function(){
28765         this.active.stack.push.apply(this.active.stack, arguments);
28766         this.allItems.push.apply(this.allItems,arguments);
28767         var r = [];
28768         for(var i = 0, a = arguments, len = a.length; i < len; i++) {
28769             if(a[i].isFormField){
28770                 r.push(a[i]);
28771             }
28772         }
28773         if(r.length > 0){
28774             Roo.form.Form.superclass.add.apply(this, r);
28775         }
28776         return this;
28777     },
28778     
28779
28780     
28781     
28782     
28783      /**
28784      * Find any element that has been added to a form, using it's ID or name
28785      * This can include framesets, columns etc. along with regular fields..
28786      * @param {String} id - id or name to find.
28787      
28788      * @return {Element} e - or false if nothing found.
28789      */
28790     findbyId : function(id)
28791     {
28792         var ret = false;
28793         if (!id) {
28794             return ret;
28795         }
28796         Roo.each(this.allItems, function(f){
28797             if (f.id == id || f.name == id ){
28798                 ret = f;
28799                 return false;
28800             }
28801         });
28802         return ret;
28803     },
28804
28805     
28806     
28807     /**
28808      * Render this form into the passed container. This should only be called once!
28809      * @param {String/HTMLElement/Element} container The element this component should be rendered into
28810      * @return {Form} this
28811      */
28812     render : function(ct)
28813     {
28814         
28815         
28816         
28817         ct = Roo.get(ct);
28818         var o = this.autoCreate || {
28819             tag: 'form',
28820             method : this.method || 'POST',
28821             id : this.id || Roo.id()
28822         };
28823         this.initEl(ct.createChild(o));
28824
28825         this.root.render(this.el);
28826         
28827        
28828              
28829         this.items.each(function(f){
28830             f.render('x-form-el-'+f.id);
28831         });
28832
28833         if(this.buttons.length > 0){
28834             // tables are required to maintain order and for correct IE layout
28835             var tb = this.el.createChild({cls:'x-form-btns-ct', cn: {
28836                 cls:"x-form-btns x-form-btns-"+this.buttonAlign,
28837                 html:'<table cellspacing="0"><tbody><tr></tr></tbody></table><div class="x-clear"></div>'
28838             }}, null, true);
28839             var tr = tb.getElementsByTagName('tr')[0];
28840             for(var i = 0, len = this.buttons.length; i < len; i++) {
28841                 var b = this.buttons[i];
28842                 var td = document.createElement('td');
28843                 td.className = 'x-form-btn-td';
28844                 b.render(tr.appendChild(td));
28845             }
28846         }
28847         if(this.monitorValid){ // initialize after render
28848             this.startMonitoring();
28849         }
28850         this.fireEvent('rendered', this);
28851         return this;
28852     },
28853
28854     /**
28855      * Adds a button to the footer of the form - this <b>must</b> be called before the form is rendered.
28856      * @param {String/Object} config A string becomes the button text, an object can either be a Button config
28857      * object or a valid Roo.DomHelper element config
28858      * @param {Function} handler The function called when the button is clicked
28859      * @param {Object} scope (optional) The scope of the handler function
28860      * @return {Roo.Button}
28861      */
28862     addButton : function(config, handler, scope){
28863         var bc = {
28864             handler: handler,
28865             scope: scope,
28866             minWidth: this.minButtonWidth,
28867             hideParent:true
28868         };
28869         if(typeof config == "string"){
28870             bc.text = config;
28871         }else{
28872             Roo.apply(bc, config);
28873         }
28874         var btn = new Roo.Button(null, bc);
28875         this.buttons.push(btn);
28876         return btn;
28877     },
28878
28879      /**
28880      * Adds a series of form elements (using the xtype property as the factory method.
28881      * Valid xtypes are:  TextField, TextArea .... Button, Layout, FieldSet, Column, (and 'end' to close a block)
28882      * @param {Object} config 
28883      */
28884     
28885     addxtype : function()
28886     {
28887         var ar = Array.prototype.slice.call(arguments, 0);
28888         var ret = false;
28889         for(var i = 0; i < ar.length; i++) {
28890             if (!ar[i]) {
28891                 continue; // skip -- if this happends something invalid got sent, we 
28892                 // should ignore it, as basically that interface element will not show up
28893                 // and that should be pretty obvious!!
28894             }
28895             
28896             if (Roo.form[ar[i].xtype]) {
28897                 ar[i].form = this;
28898                 var fe = Roo.factory(ar[i], Roo.form);
28899                 if (!ret) {
28900                     ret = fe;
28901                 }
28902                 fe.form = this;
28903                 if (fe.store) {
28904                     fe.store.form = this;
28905                 }
28906                 if (fe.isLayout) {  
28907                          
28908                     this.start(fe);
28909                     this.allItems.push(fe);
28910                     if (fe.items && fe.addxtype) {
28911                         fe.addxtype.apply(fe, fe.items);
28912                         delete fe.items;
28913                     }
28914                      this.end();
28915                     continue;
28916                 }
28917                 
28918                 
28919                  
28920                 this.add(fe);
28921               //  console.log('adding ' + ar[i].xtype);
28922             }
28923             if (ar[i].xtype == 'Button') {  
28924                 //console.log('adding button');
28925                 //console.log(ar[i]);
28926                 this.addButton(ar[i]);
28927                 this.allItems.push(fe);
28928                 continue;
28929             }
28930             
28931             if (ar[i].xtype == 'end') { // so we can add fieldsets... / layout etc.
28932                 alert('end is not supported on xtype any more, use items');
28933             //    this.end();
28934             //    //console.log('adding end');
28935             }
28936             
28937         }
28938         return ret;
28939     },
28940     
28941     /**
28942      * Starts monitoring of the valid state of this form. Usually this is done by passing the config
28943      * option "monitorValid"
28944      */
28945     startMonitoring : function(){
28946         if(!this.bound){
28947             this.bound = true;
28948             Roo.TaskMgr.start({
28949                 run : this.bindHandler,
28950                 interval : this.monitorPoll || 200,
28951                 scope: this
28952             });
28953         }
28954     },
28955
28956     /**
28957      * Stops monitoring of the valid state of this form
28958      */
28959     stopMonitoring : function(){
28960         this.bound = false;
28961     },
28962
28963     // private
28964     bindHandler : function(){
28965         if(!this.bound){
28966             return false; // stops binding
28967         }
28968         var valid = true;
28969         this.items.each(function(f){
28970             if(!f.isValid(true)){
28971                 valid = false;
28972                 return false;
28973             }
28974         });
28975         for(var i = 0, len = this.buttons.length; i < len; i++){
28976             var btn = this.buttons[i];
28977             if(btn.formBind === true && btn.disabled === valid){
28978                 btn.setDisabled(!valid);
28979             }
28980         }
28981         this.fireEvent('clientvalidation', this, valid);
28982     }
28983     
28984     
28985     
28986     
28987     
28988     
28989     
28990     
28991 });
28992
28993
28994 // back compat
28995 Roo.Form = Roo.form.Form;
28996 /*
28997  * Based on:
28998  * Ext JS Library 1.1.1
28999  * Copyright(c) 2006-2007, Ext JS, LLC.
29000  *
29001  * Originally Released Under LGPL - original licence link has changed is not relivant.
29002  *
29003  * Fork - LGPL
29004  * <script type="text/javascript">
29005  */
29006  
29007  /**
29008  * @class Roo.form.Action
29009  * Internal Class used to handle form actions
29010  * @constructor
29011  * @param {Roo.form.BasicForm} el The form element or its id
29012  * @param {Object} config Configuration options
29013  */
29014  
29015  
29016 // define the action interface
29017 Roo.form.Action = function(form, options){
29018     this.form = form;
29019     this.options = options || {};
29020 };
29021 /**
29022  * Client Validation Failed
29023  * @const 
29024  */
29025 Roo.form.Action.CLIENT_INVALID = 'client';
29026 /**
29027  * Server Validation Failed
29028  * @const 
29029  */
29030  Roo.form.Action.SERVER_INVALID = 'server';
29031  /**
29032  * Connect to Server Failed
29033  * @const 
29034  */
29035 Roo.form.Action.CONNECT_FAILURE = 'connect';
29036 /**
29037  * Reading Data from Server Failed
29038  * @const 
29039  */
29040 Roo.form.Action.LOAD_FAILURE = 'load';
29041
29042 Roo.form.Action.prototype = {
29043     type : 'default',
29044     failureType : undefined,
29045     response : undefined,
29046     result : undefined,
29047
29048     // interface method
29049     run : function(options){
29050
29051     },
29052
29053     // interface method
29054     success : function(response){
29055
29056     },
29057
29058     // interface method
29059     handleResponse : function(response){
29060
29061     },
29062
29063     // default connection failure
29064     failure : function(response){
29065         
29066         this.response = response;
29067         this.failureType = Roo.form.Action.CONNECT_FAILURE;
29068         this.form.afterAction(this, false);
29069     },
29070
29071     processResponse : function(response){
29072         this.response = response;
29073         if(!response.responseText){
29074             return true;
29075         }
29076         this.result = this.handleResponse(response);
29077         return this.result;
29078     },
29079
29080     // utility functions used internally
29081     getUrl : function(appendParams){
29082         var url = this.options.url || this.form.url || this.form.el.dom.action;
29083         if(appendParams){
29084             var p = this.getParams();
29085             if(p){
29086                 url += (url.indexOf('?') != -1 ? '&' : '?') + p;
29087             }
29088         }
29089         return url;
29090     },
29091
29092     getMethod : function(){
29093         return (this.options.method || this.form.method || this.form.el.dom.method || 'POST').toUpperCase();
29094     },
29095
29096     getParams : function(){
29097         var bp = this.form.baseParams;
29098         var p = this.options.params;
29099         if(p){
29100             if(typeof p == "object"){
29101                 p = Roo.urlEncode(Roo.applyIf(p, bp));
29102             }else if(typeof p == 'string' && bp){
29103                 p += '&' + Roo.urlEncode(bp);
29104             }
29105         }else if(bp){
29106             p = Roo.urlEncode(bp);
29107         }
29108         return p;
29109     },
29110
29111     createCallback : function(){
29112         return {
29113             success: this.success,
29114             failure: this.failure,
29115             scope: this,
29116             timeout: (this.form.timeout*1000),
29117             upload: this.form.fileUpload ? this.success : undefined
29118         };
29119     }
29120 };
29121
29122 Roo.form.Action.Submit = function(form, options){
29123     Roo.form.Action.Submit.superclass.constructor.call(this, form, options);
29124 };
29125
29126 Roo.extend(Roo.form.Action.Submit, Roo.form.Action, {
29127     type : 'submit',
29128
29129     haveProgress : false,
29130     uploadComplete : false,
29131     
29132     // uploadProgress indicator.
29133     uploadProgress : function()
29134     {
29135         if (!this.form.progressUrl) {
29136             return;
29137         }
29138         
29139         if (!this.haveProgress) {
29140             Roo.MessageBox.progress("Uploading", "Uploading");
29141         }
29142         if (this.uploadComplete) {
29143            Roo.MessageBox.hide();
29144            return;
29145         }
29146         
29147         this.haveProgress = true;
29148    
29149         var uid = this.form.findField('UPLOAD_IDENTIFIER').getValue();
29150         
29151         var c = new Roo.data.Connection();
29152         c.request({
29153             url : this.form.progressUrl,
29154             params: {
29155                 id : uid
29156             },
29157             method: 'GET',
29158             success : function(req){
29159                //console.log(data);
29160                 var rdata = false;
29161                 var edata;
29162                 try  {
29163                    rdata = Roo.decode(req.responseText)
29164                 } catch (e) {
29165                     Roo.log("Invalid data from server..");
29166                     Roo.log(edata);
29167                     return;
29168                 }
29169                 if (!rdata || !rdata.success) {
29170                     Roo.log(rdata);
29171                     Roo.MessageBox.alert(Roo.encode(rdata));
29172                     return;
29173                 }
29174                 var data = rdata.data;
29175                 
29176                 if (this.uploadComplete) {
29177                    Roo.MessageBox.hide();
29178                    return;
29179                 }
29180                    
29181                 if (data){
29182                     Roo.MessageBox.updateProgress(data.bytes_uploaded/data.bytes_total,
29183                        Math.floor((data.bytes_total - data.bytes_uploaded)/1000) + 'k remaining'
29184                     );
29185                 }
29186                 this.uploadProgress.defer(2000,this);
29187             },
29188        
29189             failure: function(data) {
29190                 Roo.log('progress url failed ');
29191                 Roo.log(data);
29192             },
29193             scope : this
29194         });
29195            
29196     },
29197     
29198     
29199     run : function()
29200     {
29201         // run get Values on the form, so it syncs any secondary forms.
29202         this.form.getValues();
29203         
29204         var o = this.options;
29205         var method = this.getMethod();
29206         var isPost = method == 'POST';
29207         if(o.clientValidation === false || this.form.isValid()){
29208             
29209             if (this.form.progressUrl) {
29210                 this.form.findField('UPLOAD_IDENTIFIER').setValue(
29211                     (new Date() * 1) + '' + Math.random());
29212                     
29213             } 
29214             
29215             
29216             Roo.Ajax.request(Roo.apply(this.createCallback(), {
29217                 form:this.form.el.dom,
29218                 url:this.getUrl(!isPost),
29219                 method: method,
29220                 params:isPost ? this.getParams() : null,
29221                 isUpload: this.form.fileUpload
29222             }));
29223             
29224             this.uploadProgress();
29225
29226         }else if (o.clientValidation !== false){ // client validation failed
29227             this.failureType = Roo.form.Action.CLIENT_INVALID;
29228             this.form.afterAction(this, false);
29229         }
29230     },
29231
29232     success : function(response)
29233     {
29234         this.uploadComplete= true;
29235         if (this.haveProgress) {
29236             Roo.MessageBox.hide();
29237         }
29238         
29239         
29240         var result = this.processResponse(response);
29241         if(result === true || result.success){
29242             this.form.afterAction(this, true);
29243             return;
29244         }
29245         if(result.errors){
29246             this.form.markInvalid(result.errors);
29247             this.failureType = Roo.form.Action.SERVER_INVALID;
29248         }
29249         this.form.afterAction(this, false);
29250     },
29251     failure : function(response)
29252     {
29253         this.uploadComplete= true;
29254         if (this.haveProgress) {
29255             Roo.MessageBox.hide();
29256         }
29257         
29258         this.response = response;
29259         this.failureType = Roo.form.Action.CONNECT_FAILURE;
29260         this.form.afterAction(this, false);
29261     },
29262     
29263     handleResponse : function(response){
29264         if(this.form.errorReader){
29265             var rs = this.form.errorReader.read(response);
29266             var errors = [];
29267             if(rs.records){
29268                 for(var i = 0, len = rs.records.length; i < len; i++) {
29269                     var r = rs.records[i];
29270                     errors[i] = r.data;
29271                 }
29272             }
29273             if(errors.length < 1){
29274                 errors = null;
29275             }
29276             return {
29277                 success : rs.success,
29278                 errors : errors
29279             };
29280         }
29281         var ret = false;
29282         try {
29283             ret = Roo.decode(response.responseText);
29284         } catch (e) {
29285             ret = {
29286                 success: false,
29287                 errorMsg: "Failed to read server message: " + (response ? response.responseText : ' - no message'),
29288                 errors : []
29289             };
29290         }
29291         return ret;
29292         
29293     }
29294 });
29295
29296
29297 Roo.form.Action.Load = function(form, options){
29298     Roo.form.Action.Load.superclass.constructor.call(this, form, options);
29299     this.reader = this.form.reader;
29300 };
29301
29302 Roo.extend(Roo.form.Action.Load, Roo.form.Action, {
29303     type : 'load',
29304
29305     run : function(){
29306         
29307         Roo.Ajax.request(Roo.apply(
29308                 this.createCallback(), {
29309                     method:this.getMethod(),
29310                     url:this.getUrl(false),
29311                     params:this.getParams()
29312         }));
29313     },
29314
29315     success : function(response){
29316         
29317         var result = this.processResponse(response);
29318         if(result === true || !result.success || !result.data){
29319             this.failureType = Roo.form.Action.LOAD_FAILURE;
29320             this.form.afterAction(this, false);
29321             return;
29322         }
29323         this.form.clearInvalid();
29324         this.form.setValues(result.data);
29325         this.form.afterAction(this, true);
29326     },
29327
29328     handleResponse : function(response){
29329         if(this.form.reader){
29330             var rs = this.form.reader.read(response);
29331             var data = rs.records && rs.records[0] ? rs.records[0].data : null;
29332             return {
29333                 success : rs.success,
29334                 data : data
29335             };
29336         }
29337         return Roo.decode(response.responseText);
29338     }
29339 });
29340
29341 Roo.form.Action.ACTION_TYPES = {
29342     'load' : Roo.form.Action.Load,
29343     'submit' : Roo.form.Action.Submit
29344 };/*
29345  * Based on:
29346  * Ext JS Library 1.1.1
29347  * Copyright(c) 2006-2007, Ext JS, LLC.
29348  *
29349  * Originally Released Under LGPL - original licence link has changed is not relivant.
29350  *
29351  * Fork - LGPL
29352  * <script type="text/javascript">
29353  */
29354  
29355 /**
29356  * @class Roo.form.Layout
29357  * @extends Roo.Component
29358  * Creates a container for layout and rendering of fields in an {@link Roo.form.Form}.
29359  * @constructor
29360  * @param {Object} config Configuration options
29361  */
29362 Roo.form.Layout = function(config){
29363     var xitems = [];
29364     if (config.items) {
29365         xitems = config.items;
29366         delete config.items;
29367     }
29368     Roo.form.Layout.superclass.constructor.call(this, config);
29369     this.stack = [];
29370     Roo.each(xitems, this.addxtype, this);
29371      
29372 };
29373
29374 Roo.extend(Roo.form.Layout, Roo.Component, {
29375     /**
29376      * @cfg {String/Object} autoCreate
29377      * A DomHelper element spec used to autocreate the layout (defaults to {tag: 'div', cls: 'x-form-ct'})
29378      */
29379     /**
29380      * @cfg {String/Object/Function} style
29381      * A style specification string, e.g. "width:100px", or object in the form {width:"100px"}, or
29382      * a function which returns such a specification.
29383      */
29384     /**
29385      * @cfg {String} labelAlign
29386      * Valid values are "left," "top" and "right" (defaults to "left")
29387      */
29388     /**
29389      * @cfg {Number} labelWidth
29390      * Fixed width in pixels of all field labels (defaults to undefined)
29391      */
29392     /**
29393      * @cfg {Boolean} clear
29394      * True to add a clearing element at the end of this layout, equivalent to CSS clear: both (defaults to true)
29395      */
29396     clear : true,
29397     /**
29398      * @cfg {String} labelSeparator
29399      * The separator to use after field labels (defaults to ':')
29400      */
29401     labelSeparator : ':',
29402     /**
29403      * @cfg {Boolean} hideLabels
29404      * True to suppress the display of field labels in this layout (defaults to false)
29405      */
29406     hideLabels : false,
29407
29408     // private
29409     defaultAutoCreate : {tag: 'div', cls: 'x-form-ct'},
29410     
29411     isLayout : true,
29412     
29413     // private
29414     onRender : function(ct, position){
29415         if(this.el){ // from markup
29416             this.el = Roo.get(this.el);
29417         }else {  // generate
29418             var cfg = this.getAutoCreate();
29419             this.el = ct.createChild(cfg, position);
29420         }
29421         if(this.style){
29422             this.el.applyStyles(this.style);
29423         }
29424         if(this.labelAlign){
29425             this.el.addClass('x-form-label-'+this.labelAlign);
29426         }
29427         if(this.hideLabels){
29428             this.labelStyle = "display:none";
29429             this.elementStyle = "padding-left:0;";
29430         }else{
29431             if(typeof this.labelWidth == 'number'){
29432                 this.labelStyle = "width:"+this.labelWidth+"px;";
29433                 this.elementStyle = "padding-left:"+((this.labelWidth+(typeof this.labelPad == 'number' ? this.labelPad : 5))+'px')+";";
29434             }
29435             if(this.labelAlign == 'top'){
29436                 this.labelStyle = "width:auto;";
29437                 this.elementStyle = "padding-left:0;";
29438             }
29439         }
29440         var stack = this.stack;
29441         var slen = stack.length;
29442         if(slen > 0){
29443             if(!this.fieldTpl){
29444                 var t = new Roo.Template(
29445                     '<div class="x-form-item {5}">',
29446                         '<label for="{0}" style="{2}">{1}{4}</label>',
29447                         '<div class="x-form-element" id="x-form-el-{0}" style="{3}">',
29448                         '</div>',
29449                     '</div><div class="x-form-clear-left"></div>'
29450                 );
29451                 t.disableFormats = true;
29452                 t.compile();
29453                 Roo.form.Layout.prototype.fieldTpl = t;
29454             }
29455             for(var i = 0; i < slen; i++) {
29456                 if(stack[i].isFormField){
29457                     this.renderField(stack[i]);
29458                 }else{
29459                     this.renderComponent(stack[i]);
29460                 }
29461             }
29462         }
29463         if(this.clear){
29464             this.el.createChild({cls:'x-form-clear'});
29465         }
29466     },
29467
29468     // private
29469     renderField : function(f){
29470         f.fieldEl = Roo.get(this.fieldTpl.append(this.el, [
29471                f.id, //0
29472                f.fieldLabel, //1
29473                f.labelStyle||this.labelStyle||'', //2
29474                this.elementStyle||'', //3
29475                typeof f.labelSeparator == 'undefined' ? this.labelSeparator : f.labelSeparator, //4
29476                f.itemCls||this.itemCls||''  //5
29477        ], true).getPrevSibling());
29478     },
29479
29480     // private
29481     renderComponent : function(c){
29482         c.render(c.isLayout ? this.el : this.el.createChild());    
29483     },
29484     /**
29485      * Adds a object form elements (using the xtype property as the factory method.)
29486      * Valid xtypes are:  TextField, TextArea .... Button, Layout, FieldSet, Column
29487      * @param {Object} config 
29488      */
29489     addxtype : function(o)
29490     {
29491         // create the lement.
29492         o.form = this.form;
29493         var fe = Roo.factory(o, Roo.form);
29494         this.form.allItems.push(fe);
29495         this.stack.push(fe);
29496         
29497         if (fe.isFormField) {
29498             this.form.items.add(fe);
29499         }
29500          
29501         return fe;
29502     }
29503 });
29504
29505 /**
29506  * @class Roo.form.Column
29507  * @extends Roo.form.Layout
29508  * Creates a column container for layout and rendering of fields in an {@link Roo.form.Form}.
29509  * @constructor
29510  * @param {Object} config Configuration options
29511  */
29512 Roo.form.Column = function(config){
29513     Roo.form.Column.superclass.constructor.call(this, config);
29514 };
29515
29516 Roo.extend(Roo.form.Column, Roo.form.Layout, {
29517     /**
29518      * @cfg {Number/String} width
29519      * The fixed width of the column in pixels or CSS value (defaults to "auto")
29520      */
29521     /**
29522      * @cfg {String/Object} autoCreate
29523      * A DomHelper element spec used to autocreate the column (defaults to {tag: 'div', cls: 'x-form-ct x-form-column'})
29524      */
29525
29526     // private
29527     defaultAutoCreate : {tag: 'div', cls: 'x-form-ct x-form-column'},
29528
29529     // private
29530     onRender : function(ct, position){
29531         Roo.form.Column.superclass.onRender.call(this, ct, position);
29532         if(this.width){
29533             this.el.setWidth(this.width);
29534         }
29535     }
29536 });
29537
29538
29539 /**
29540  * @class Roo.form.Row
29541  * @extends Roo.form.Layout
29542  * Creates a row container for layout and rendering of fields in an {@link Roo.form.Form}.
29543  * @constructor
29544  * @param {Object} config Configuration options
29545  */
29546
29547  
29548 Roo.form.Row = function(config){
29549     Roo.form.Row.superclass.constructor.call(this, config);
29550 };
29551  
29552 Roo.extend(Roo.form.Row, Roo.form.Layout, {
29553       /**
29554      * @cfg {Number/String} width
29555      * The fixed width of the column in pixels or CSS value (defaults to "auto")
29556      */
29557     /**
29558      * @cfg {Number/String} height
29559      * The fixed height of the column in pixels or CSS value (defaults to "auto")
29560      */
29561     defaultAutoCreate : {tag: 'div', cls: 'x-form-ct x-form-row'},
29562     
29563     padWidth : 20,
29564     // private
29565     onRender : function(ct, position){
29566         //console.log('row render');
29567         if(!this.rowTpl){
29568             var t = new Roo.Template(
29569                 '<div class="x-form-item {5}" style="float:left;width:{6}px">',
29570                     '<label for="{0}" style="{2}">{1}{4}</label>',
29571                     '<div class="x-form-element" id="x-form-el-{0}" style="{3}">',
29572                     '</div>',
29573                 '</div>'
29574             );
29575             t.disableFormats = true;
29576             t.compile();
29577             Roo.form.Layout.prototype.rowTpl = t;
29578         }
29579         this.fieldTpl = this.rowTpl;
29580         
29581         //console.log('lw' + this.labelWidth +', la:' + this.labelAlign);
29582         var labelWidth = 100;
29583         
29584         if ((this.labelAlign != 'top')) {
29585             if (typeof this.labelWidth == 'number') {
29586                 labelWidth = this.labelWidth
29587             }
29588             this.padWidth =  20 + labelWidth;
29589             
29590         }
29591         
29592         Roo.form.Column.superclass.onRender.call(this, ct, position);
29593         if(this.width){
29594             this.el.setWidth(this.width);
29595         }
29596         if(this.height){
29597             this.el.setHeight(this.height);
29598         }
29599     },
29600     
29601     // private
29602     renderField : function(f){
29603         f.fieldEl = this.fieldTpl.append(this.el, [
29604                f.id, f.fieldLabel,
29605                f.labelStyle||this.labelStyle||'',
29606                this.elementStyle||'',
29607                typeof f.labelSeparator == 'undefined' ? this.labelSeparator : f.labelSeparator,
29608                f.itemCls||this.itemCls||'',
29609                f.width ? f.width + this.padWidth : 160 + this.padWidth
29610        ],true);
29611     }
29612 });
29613  
29614
29615 /**
29616  * @class Roo.form.FieldSet
29617  * @extends Roo.form.Layout
29618  * Creates a fieldset container for layout and rendering of fields in an {@link Roo.form.Form}.
29619  * @constructor
29620  * @param {Object} config Configuration options
29621  */
29622 Roo.form.FieldSet = function(config){
29623     Roo.form.FieldSet.superclass.constructor.call(this, config);
29624 };
29625
29626 Roo.extend(Roo.form.FieldSet, Roo.form.Layout, {
29627     /**
29628      * @cfg {String} legend
29629      * The text to display as the legend for the FieldSet (defaults to '')
29630      */
29631     /**
29632      * @cfg {String/Object} autoCreate
29633      * A DomHelper element spec used to autocreate the fieldset (defaults to {tag: 'fieldset', cn: {tag:'legend'}})
29634      */
29635
29636     // private
29637     defaultAutoCreate : {tag: 'fieldset', cn: {tag:'legend'}},
29638
29639     // private
29640     onRender : function(ct, position){
29641         Roo.form.FieldSet.superclass.onRender.call(this, ct, position);
29642         if(this.legend){
29643             this.setLegend(this.legend);
29644         }
29645     },
29646
29647     // private
29648     setLegend : function(text){
29649         if(this.rendered){
29650             this.el.child('legend').update(text);
29651         }
29652     }
29653 });/*
29654  * Based on:
29655  * Ext JS Library 1.1.1
29656  * Copyright(c) 2006-2007, Ext JS, LLC.
29657  *
29658  * Originally Released Under LGPL - original licence link has changed is not relivant.
29659  *
29660  * Fork - LGPL
29661  * <script type="text/javascript">
29662  */
29663 /**
29664  * @class Roo.form.VTypes
29665  * Overridable validation definitions. The validations provided are basic and intended to be easily customizable and extended.
29666  * @singleton
29667  */
29668 Roo.form.VTypes = function(){
29669     // closure these in so they are only created once.
29670     var alpha = /^[a-zA-Z_]+$/;
29671     var alphanum = /^[a-zA-Z0-9_]+$/;
29672     var email = /^([\w]+)(.[\w]+)*@([\w-]+\.){1,5}([A-Za-z]){2,4}$/;
29673     var url = /(((https?)|(ftp)):\/\/([\-\w]+\.)+\w{2,3}(\/[%\-\w]+(\.\w{2,})?)*(([\w\-\.\?\\\/+@&#;`~=%!]*)(\.\w{2,})?)*\/?)/i;
29674
29675     // All these messages and functions are configurable
29676     return {
29677         /**
29678          * The function used to validate email addresses
29679          * @param {String} value The email address
29680          */
29681         'email' : function(v){
29682             return email.test(v);
29683         },
29684         /**
29685          * The error text to display when the email validation function returns false
29686          * @type String
29687          */
29688         'emailText' : 'This field should be an e-mail address in the format "user@domain.com"',
29689         /**
29690          * The keystroke filter mask to be applied on email input
29691          * @type RegExp
29692          */
29693         'emailMask' : /[a-z0-9_\.\-@]/i,
29694
29695         /**
29696          * The function used to validate URLs
29697          * @param {String} value The URL
29698          */
29699         'url' : function(v){
29700             return url.test(v);
29701         },
29702         /**
29703          * The error text to display when the url validation function returns false
29704          * @type String
29705          */
29706         'urlText' : 'This field should be a URL in the format "http:/'+'/www.domain.com"',
29707         
29708         /**
29709          * The function used to validate alpha values
29710          * @param {String} value The value
29711          */
29712         'alpha' : function(v){
29713             return alpha.test(v);
29714         },
29715         /**
29716          * The error text to display when the alpha validation function returns false
29717          * @type String
29718          */
29719         'alphaText' : 'This field should only contain letters and _',
29720         /**
29721          * The keystroke filter mask to be applied on alpha input
29722          * @type RegExp
29723          */
29724         'alphaMask' : /[a-z_]/i,
29725
29726         /**
29727          * The function used to validate alphanumeric values
29728          * @param {String} value The value
29729          */
29730         'alphanum' : function(v){
29731             return alphanum.test(v);
29732         },
29733         /**
29734          * The error text to display when the alphanumeric validation function returns false
29735          * @type String
29736          */
29737         'alphanumText' : 'This field should only contain letters, numbers and _',
29738         /**
29739          * The keystroke filter mask to be applied on alphanumeric input
29740          * @type RegExp
29741          */
29742         'alphanumMask' : /[a-z0-9_]/i
29743     };
29744 }();//<script type="text/javascript">
29745
29746 /**
29747  * @class Roo.form.FCKeditor
29748  * @extends Roo.form.TextArea
29749  * Wrapper around the FCKEditor http://www.fckeditor.net
29750  * @constructor
29751  * Creates a new FCKeditor
29752  * @param {Object} config Configuration options
29753  */
29754 Roo.form.FCKeditor = function(config){
29755     Roo.form.FCKeditor.superclass.constructor.call(this, config);
29756     this.addEvents({
29757          /**
29758          * @event editorinit
29759          * Fired when the editor is initialized - you can add extra handlers here..
29760          * @param {FCKeditor} this
29761          * @param {Object} the FCK object.
29762          */
29763         editorinit : true
29764     });
29765     
29766     
29767 };
29768 Roo.form.FCKeditor.editors = { };
29769 Roo.extend(Roo.form.FCKeditor, Roo.form.TextArea,
29770 {
29771     //defaultAutoCreate : {
29772     //    tag : "textarea",style   : "width:100px;height:60px;" ,autocomplete    : "off"
29773     //},
29774     // private
29775     /**
29776      * @cfg {Object} fck options - see fck manual for details.
29777      */
29778     fckconfig : false,
29779     
29780     /**
29781      * @cfg {Object} fck toolbar set (Basic or Default)
29782      */
29783     toolbarSet : 'Basic',
29784     /**
29785      * @cfg {Object} fck BasePath
29786      */ 
29787     basePath : '/fckeditor/',
29788     
29789     
29790     frame : false,
29791     
29792     value : '',
29793     
29794    
29795     onRender : function(ct, position)
29796     {
29797         if(!this.el){
29798             this.defaultAutoCreate = {
29799                 tag: "textarea",
29800                 style:"width:300px;height:60px;",
29801                 autocomplete: "off"
29802             };
29803         }
29804         Roo.form.FCKeditor.superclass.onRender.call(this, ct, position);
29805         /*
29806         if(this.grow){
29807             this.textSizeEl = Roo.DomHelper.append(document.body, {tag: "pre", cls: "x-form-grow-sizer"});
29808             if(this.preventScrollbars){
29809                 this.el.setStyle("overflow", "hidden");
29810             }
29811             this.el.setHeight(this.growMin);
29812         }
29813         */
29814         //console.log('onrender' + this.getId() );
29815         Roo.form.FCKeditor.editors[this.getId()] = this;
29816          
29817
29818         this.replaceTextarea() ;
29819         
29820     },
29821     
29822     getEditor : function() {
29823         return this.fckEditor;
29824     },
29825     /**
29826      * Sets a data value into the field and validates it.  To set the value directly without validation see {@link #setRawValue}.
29827      * @param {Mixed} value The value to set
29828      */
29829     
29830     
29831     setValue : function(value)
29832     {
29833         //console.log('setValue: ' + value);
29834         
29835         if(typeof(value) == 'undefined') { // not sure why this is happending...
29836             return;
29837         }
29838         Roo.form.FCKeditor.superclass.setValue.apply(this,[value]);
29839         
29840         //if(!this.el || !this.getEditor()) {
29841         //    this.value = value;
29842             //this.setValue.defer(100,this,[value]);    
29843         //    return;
29844         //} 
29845         
29846         if(!this.getEditor()) {
29847             return;
29848         }
29849         
29850         this.getEditor().SetData(value);
29851         
29852         //
29853
29854     },
29855
29856     /**
29857      * Returns the normalized data value (undefined or emptyText will be returned as '').  To return the raw value see {@link #getRawValue}.
29858      * @return {Mixed} value The field value
29859      */
29860     getValue : function()
29861     {
29862         
29863         if (this.frame && this.frame.dom.style.display == 'none') {
29864             return Roo.form.FCKeditor.superclass.getValue.call(this);
29865         }
29866         
29867         if(!this.el || !this.getEditor()) {
29868            
29869            // this.getValue.defer(100,this); 
29870             return this.value;
29871         }
29872        
29873         
29874         var value=this.getEditor().GetData();
29875         Roo.form.FCKeditor.superclass.setValue.apply(this,[value]);
29876         return Roo.form.FCKeditor.superclass.getValue.call(this);
29877         
29878
29879     },
29880
29881     /**
29882      * Returns the raw data value which may or may not be a valid, defined value.  To return a normalized value see {@link #getValue}.
29883      * @return {Mixed} value The field value
29884      */
29885     getRawValue : function()
29886     {
29887         if (this.frame && this.frame.dom.style.display == 'none') {
29888             return Roo.form.FCKeditor.superclass.getRawValue.call(this);
29889         }
29890         
29891         if(!this.el || !this.getEditor()) {
29892             //this.getRawValue.defer(100,this); 
29893             return this.value;
29894             return;
29895         }
29896         
29897         
29898         
29899         var value=this.getEditor().GetData();
29900         Roo.form.FCKeditor.superclass.setRawValue.apply(this,[value]);
29901         return Roo.form.FCKeditor.superclass.getRawValue.call(this);
29902          
29903     },
29904     
29905     setSize : function(w,h) {
29906         
29907         
29908         
29909         //if (this.frame && this.frame.dom.style.display == 'none') {
29910         //    Roo.form.FCKeditor.superclass.setSize.apply(this, [w, h]);
29911         //    return;
29912         //}
29913         //if(!this.el || !this.getEditor()) {
29914         //    this.setSize.defer(100,this, [w,h]); 
29915         //    return;
29916         //}
29917         
29918         
29919         
29920         Roo.form.FCKeditor.superclass.setSize.apply(this, [w, h]);
29921         
29922         this.frame.dom.setAttribute('width', w);
29923         this.frame.dom.setAttribute('height', h);
29924         this.frame.setSize(w,h);
29925         
29926     },
29927     
29928     toggleSourceEdit : function(value) {
29929         
29930       
29931          
29932         this.el.dom.style.display = value ? '' : 'none';
29933         this.frame.dom.style.display = value ?  'none' : '';
29934         
29935     },
29936     
29937     
29938     focus: function(tag)
29939     {
29940         if (this.frame.dom.style.display == 'none') {
29941             return Roo.form.FCKeditor.superclass.focus.call(this);
29942         }
29943         if(!this.el || !this.getEditor()) {
29944             this.focus.defer(100,this, [tag]); 
29945             return;
29946         }
29947         
29948         
29949         
29950         
29951         var tgs = this.getEditor().EditorDocument.getElementsByTagName(tag);
29952         this.getEditor().Focus();
29953         if (tgs.length) {
29954             if (!this.getEditor().Selection.GetSelection()) {
29955                 this.focus.defer(100,this, [tag]); 
29956                 return;
29957             }
29958             
29959             
29960             var r = this.getEditor().EditorDocument.createRange();
29961             r.setStart(tgs[0],0);
29962             r.setEnd(tgs[0],0);
29963             this.getEditor().Selection.GetSelection().removeAllRanges();
29964             this.getEditor().Selection.GetSelection().addRange(r);
29965             this.getEditor().Focus();
29966         }
29967         
29968     },
29969     
29970     
29971     
29972     replaceTextarea : function()
29973     {
29974         if ( document.getElementById( this.getId() + '___Frame' ) )
29975             return ;
29976         //if ( !this.checkBrowser || this._isCompatibleBrowser() )
29977         //{
29978             // We must check the elements firstly using the Id and then the name.
29979         var oTextarea = document.getElementById( this.getId() );
29980         
29981         var colElementsByName = document.getElementsByName( this.getId() ) ;
29982          
29983         oTextarea.style.display = 'none' ;
29984
29985         if ( oTextarea.tabIndex ) {            
29986             this.TabIndex = oTextarea.tabIndex ;
29987         }
29988         
29989         this._insertHtmlBefore( this._getConfigHtml(), oTextarea ) ;
29990         this._insertHtmlBefore( this._getIFrameHtml(), oTextarea ) ;
29991         this.frame = Roo.get(this.getId() + '___Frame')
29992     },
29993     
29994     _getConfigHtml : function()
29995     {
29996         var sConfig = '' ;
29997
29998         for ( var o in this.fckconfig ) {
29999             sConfig += sConfig.length > 0  ? '&amp;' : '';
30000             sConfig += encodeURIComponent( o ) + '=' + encodeURIComponent( this.fckconfig[o] ) ;
30001         }
30002
30003         return '<input type="hidden" id="' + this.getId() + '___Config" value="' + sConfig + '" style="display:none" />' ;
30004     },
30005     
30006     
30007     _getIFrameHtml : function()
30008     {
30009         var sFile = 'fckeditor.html' ;
30010         /* no idea what this is about..
30011         try
30012         {
30013             if ( (/fcksource=true/i).test( window.top.location.search ) )
30014                 sFile = 'fckeditor.original.html' ;
30015         }
30016         catch (e) { 
30017         */
30018
30019         var sLink = this.basePath + 'editor/' + sFile + '?InstanceName=' + encodeURIComponent( this.getId() ) ;
30020         sLink += this.toolbarSet ? ( '&amp;Toolbar=' + this.toolbarSet)  : '';
30021         
30022         
30023         var html = '<iframe id="' + this.getId() +
30024             '___Frame" src="' + sLink +
30025             '" width="' + this.width +
30026             '" height="' + this.height + '"' +
30027             (this.tabIndex ?  ' tabindex="' + this.tabIndex + '"' :'' ) +
30028             ' frameborder="0" scrolling="no"></iframe>' ;
30029
30030         return html ;
30031     },
30032     
30033     _insertHtmlBefore : function( html, element )
30034     {
30035         if ( element.insertAdjacentHTML )       {
30036             // IE
30037             element.insertAdjacentHTML( 'beforeBegin', html ) ;
30038         } else { // Gecko
30039             var oRange = document.createRange() ;
30040             oRange.setStartBefore( element ) ;
30041             var oFragment = oRange.createContextualFragment( html );
30042             element.parentNode.insertBefore( oFragment, element ) ;
30043         }
30044     }
30045     
30046     
30047   
30048     
30049     
30050     
30051     
30052
30053 });
30054
30055 //Roo.reg('fckeditor', Roo.form.FCKeditor);
30056
30057 function FCKeditor_OnComplete(editorInstance){
30058     var f = Roo.form.FCKeditor.editors[editorInstance.Name];
30059     f.fckEditor = editorInstance;
30060     //console.log("loaded");
30061     f.fireEvent('editorinit', f, editorInstance);
30062
30063   
30064
30065  
30066
30067
30068
30069
30070
30071
30072
30073
30074
30075
30076
30077
30078
30079
30080
30081 //<script type="text/javascript">
30082 /**
30083  * @class Roo.form.GridField
30084  * @extends Roo.form.Field
30085  * Embed a grid (or editable grid into a form)
30086  * STATUS ALPHA
30087  * 
30088  * This embeds a grid in a form, the value of the field should be the json encoded array of rows
30089  * it needs 
30090  * xgrid.store = Roo.data.Store
30091  * xgrid.store.proxy = Roo.data.MemoryProxy (data = [] )
30092  * xgrid.store.reader = Roo.data.JsonReader 
30093  * 
30094  * 
30095  * @constructor
30096  * Creates a new GridField
30097  * @param {Object} config Configuration options
30098  */
30099 Roo.form.GridField = function(config){
30100     Roo.form.GridField.superclass.constructor.call(this, config);
30101      
30102 };
30103
30104 Roo.extend(Roo.form.GridField, Roo.form.Field,  {
30105     /**
30106      * @cfg {Number} width  - used to restrict width of grid..
30107      */
30108     width : 100,
30109     /**
30110      * @cfg {Number} height - used to restrict height of grid..
30111      */
30112     height : 50,
30113      /**
30114      * @cfg {Object} xgrid (xtype'd description of grid) { xtype : 'Grid', dataSource: .... }
30115          * 
30116          *}
30117      */
30118     xgrid : false, 
30119     /**
30120      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
30121      * {tag: "input", type: "checkbox", autocomplete: "off"})
30122      */
30123    // defaultAutoCreate : { tag: 'div' },
30124     defaultAutoCreate : { tag: 'input', type: 'hidden', autocomplete: 'off'},
30125     /**
30126      * @cfg {String} addTitle Text to include for adding a title.
30127      */
30128     addTitle : false,
30129     //
30130     onResize : function(){
30131         Roo.form.Field.superclass.onResize.apply(this, arguments);
30132     },
30133
30134     initEvents : function(){
30135         // Roo.form.Checkbox.superclass.initEvents.call(this);
30136         // has no events...
30137        
30138     },
30139
30140
30141     getResizeEl : function(){
30142         return this.wrap;
30143     },
30144
30145     getPositionEl : function(){
30146         return this.wrap;
30147     },
30148
30149     // private
30150     onRender : function(ct, position){
30151         
30152         this.style = this.style || 'overflow: hidden; border:1px solid #c3daf9;';
30153         var style = this.style;
30154         delete this.style;
30155         
30156         Roo.form.GridField.superclass.onRender.call(this, ct, position);
30157         this.wrap = this.el.wrap({cls: ''}); // not sure why ive done thsi...
30158         this.viewEl = this.wrap.createChild({ tag: 'div' });
30159         if (style) {
30160             this.viewEl.applyStyles(style);
30161         }
30162         if (this.width) {
30163             this.viewEl.setWidth(this.width);
30164         }
30165         if (this.height) {
30166             this.viewEl.setHeight(this.height);
30167         }
30168         //if(this.inputValue !== undefined){
30169         //this.setValue(this.value);
30170         
30171         
30172         this.grid = new Roo.grid[this.xgrid.xtype](this.viewEl, this.xgrid);
30173         
30174         
30175         this.grid.render();
30176         this.grid.getDataSource().on('remove', this.refreshValue, this);
30177         this.grid.getDataSource().on('update', this.refreshValue, this);
30178         this.grid.on('afteredit', this.refreshValue, this);
30179  
30180     },
30181      
30182     
30183     /**
30184      * Sets the value of the item. 
30185      * @param {String} either an object  or a string..
30186      */
30187     setValue : function(v){
30188         //this.value = v;
30189         v = v || []; // empty set..
30190         // this does not seem smart - it really only affects memoryproxy grids..
30191         if (this.grid && this.grid.getDataSource() && typeof(v) != 'undefined') {
30192             var ds = this.grid.getDataSource();
30193             // assumes a json reader..
30194             var data = {}
30195             data[ds.reader.meta.root ] =  typeof(v) == 'string' ? Roo.decode(v) : v;
30196             ds.loadData( data);
30197         }
30198         // clear selection so it does not get stale.
30199         if (this.grid.sm) { 
30200             this.grid.sm.clearSelections();
30201         }
30202         
30203         Roo.form.GridField.superclass.setValue.call(this, v);
30204         this.refreshValue();
30205         // should load data in the grid really....
30206     },
30207     
30208     // private
30209     refreshValue: function() {
30210          var val = [];
30211         this.grid.getDataSource().each(function(r) {
30212             val.push(r.data);
30213         });
30214         this.el.dom.value = Roo.encode(val);
30215     }
30216     
30217      
30218     
30219     
30220 });/*
30221  * Based on:
30222  * Ext JS Library 1.1.1
30223  * Copyright(c) 2006-2007, Ext JS, LLC.
30224  *
30225  * Originally Released Under LGPL - original licence link has changed is not relivant.
30226  *
30227  * Fork - LGPL
30228  * <script type="text/javascript">
30229  */
30230 /**
30231  * @class Roo.form.DisplayField
30232  * @extends Roo.form.Field
30233  * A generic Field to display non-editable data.
30234  * @constructor
30235  * Creates a new Display Field item.
30236  * @param {Object} config Configuration options
30237  */
30238 Roo.form.DisplayField = function(config){
30239     Roo.form.DisplayField.superclass.constructor.call(this, config);
30240     
30241 };
30242
30243 Roo.extend(Roo.form.DisplayField, Roo.form.TextField,  {
30244     inputType:      'hidden',
30245     allowBlank:     true,
30246     readOnly:         true,
30247     
30248  
30249     /**
30250      * @cfg {String} focusClass The CSS class to use when the checkbox receives focus (defaults to undefined)
30251      */
30252     focusClass : undefined,
30253     /**
30254      * @cfg {String} fieldClass The default CSS class for the checkbox (defaults to "x-form-field")
30255      */
30256     fieldClass: 'x-form-field',
30257     
30258      /**
30259      * @cfg {Function} valueRenderer The renderer for the field (so you can reformat output). should return raw HTML
30260      */
30261     valueRenderer: undefined,
30262     
30263     width: 100,
30264     /**
30265      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
30266      * {tag: "input", type: "checkbox", autocomplete: "off"})
30267      */
30268      
30269  //   defaultAutoCreate : { tag: 'input', type: 'hidden', autocomplete: 'off'},
30270
30271     onResize : function(){
30272         Roo.form.DisplayField.superclass.onResize.apply(this, arguments);
30273         
30274     },
30275
30276     initEvents : function(){
30277         // Roo.form.Checkbox.superclass.initEvents.call(this);
30278         // has no events...
30279        
30280     },
30281
30282
30283     getResizeEl : function(){
30284         return this.wrap;
30285     },
30286
30287     getPositionEl : function(){
30288         return this.wrap;
30289     },
30290
30291     // private
30292     onRender : function(ct, position){
30293         
30294         Roo.form.DisplayField.superclass.onRender.call(this, ct, position);
30295         //if(this.inputValue !== undefined){
30296         this.wrap = this.el.wrap();
30297         
30298         this.viewEl = this.wrap.createChild({ tag: 'div', cls: 'x-form-displayfield'});
30299         
30300         if (this.bodyStyle) {
30301             this.viewEl.applyStyles(this.bodyStyle);
30302         }
30303         //this.viewEl.setStyle('padding', '2px');
30304         
30305         this.setValue(this.value);
30306         
30307     },
30308 /*
30309     // private
30310     initValue : Roo.emptyFn,
30311
30312   */
30313
30314         // private
30315     onClick : function(){
30316         
30317     },
30318
30319     /**
30320      * Sets the checked state of the checkbox.
30321      * @param {Boolean/String} checked True, 'true', '1', or 'on' to check the checkbox, any other value will uncheck it.
30322      */
30323     setValue : function(v){
30324         this.value = v;
30325         var html = this.valueRenderer ?  this.valueRenderer(v) : String.format('{0}', v);
30326         // this might be called before we have a dom element..
30327         if (!this.viewEl) {
30328             return;
30329         }
30330         this.viewEl.dom.innerHTML = html;
30331         Roo.form.DisplayField.superclass.setValue.call(this, v);
30332
30333     }
30334 });/*
30335  * 
30336  * Licence- LGPL
30337  * 
30338  */
30339
30340 /**
30341  * @class Roo.form.DayPicker
30342  * @extends Roo.form.Field
30343  * A Day picker show [M] [T] [W] ....
30344  * @constructor
30345  * Creates a new Day Picker
30346  * @param {Object} config Configuration options
30347  */
30348 Roo.form.DayPicker= function(config){
30349     Roo.form.DayPicker.superclass.constructor.call(this, config);
30350      
30351 };
30352
30353 Roo.extend(Roo.form.DayPicker, Roo.form.Field,  {
30354     /**
30355      * @cfg {String} focusClass The CSS class to use when the checkbox receives focus (defaults to undefined)
30356      */
30357     focusClass : undefined,
30358     /**
30359      * @cfg {String} fieldClass The default CSS class for the checkbox (defaults to "x-form-field")
30360      */
30361     fieldClass: "x-form-field",
30362    
30363     /**
30364      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
30365      * {tag: "input", type: "checkbox", autocomplete: "off"})
30366      */
30367     defaultAutoCreate : { tag: "input", type: 'hidden', autocomplete: "off"},
30368     
30369    
30370     actionMode : 'viewEl', 
30371     //
30372     // private
30373  
30374     inputType : 'hidden',
30375     
30376      
30377     inputElement: false, // real input element?
30378     basedOn: false, // ????
30379     
30380     isFormField: true, // not sure where this is needed!!!!
30381
30382     onResize : function(){
30383         Roo.form.Checkbox.superclass.onResize.apply(this, arguments);
30384         if(!this.boxLabel){
30385             this.el.alignTo(this.wrap, 'c-c');
30386         }
30387     },
30388
30389     initEvents : function(){
30390         Roo.form.Checkbox.superclass.initEvents.call(this);
30391         this.el.on("click", this.onClick,  this);
30392         this.el.on("change", this.onClick,  this);
30393     },
30394
30395
30396     getResizeEl : function(){
30397         return this.wrap;
30398     },
30399
30400     getPositionEl : function(){
30401         return this.wrap;
30402     },
30403
30404     
30405     // private
30406     onRender : function(ct, position){
30407         Roo.form.Checkbox.superclass.onRender.call(this, ct, position);
30408        
30409         this.wrap = this.el.wrap({cls: 'x-form-daypick-item '});
30410         
30411         var r1 = '<table><tr>';
30412         var r2 = '<tr class="x-form-daypick-icons">';
30413         for (var i=0; i < 7; i++) {
30414             r1+= '<td><div>' + Date.dayNames[i].substring(0,3) + '</div></td>';
30415             r2+= '<td><img class="x-menu-item-icon" src="' + Roo.BLANK_IMAGE_URL  +'"></td>';
30416         }
30417         
30418         var viewEl = this.wrap.createChild( r1 + '</tr>' + r2 + '</tr></table>');
30419         viewEl.select('img').on('click', this.onClick, this);
30420         this.viewEl = viewEl;   
30421         
30422         
30423         // this will not work on Chrome!!!
30424         this.el.on('DOMAttrModified', this.setFromHidden,  this); //ff
30425         this.el.on('propertychange', this.setFromHidden,  this);  //ie
30426         
30427         
30428           
30429
30430     },
30431
30432     // private
30433     initValue : Roo.emptyFn,
30434
30435     /**
30436      * Returns the checked state of the checkbox.
30437      * @return {Boolean} True if checked, else false
30438      */
30439     getValue : function(){
30440         return this.el.dom.value;
30441         
30442     },
30443
30444         // private
30445     onClick : function(e){ 
30446         //this.setChecked(!this.checked);
30447         Roo.get(e.target).toggleClass('x-menu-item-checked');
30448         this.refreshValue();
30449         //if(this.el.dom.checked != this.checked){
30450         //    this.setValue(this.el.dom.checked);
30451        // }
30452     },
30453     
30454     // private
30455     refreshValue : function()
30456     {
30457         var val = '';
30458         this.viewEl.select('img',true).each(function(e,i,n)  {
30459             val += e.is(".x-menu-item-checked") ? String(n) : '';
30460         });
30461         this.setValue(val, true);
30462     },
30463
30464     /**
30465      * Sets the checked state of the checkbox.
30466      * On is always based on a string comparison between inputValue and the param.
30467      * @param {Boolean/String} value - the value to set 
30468      * @param {Boolean/String} suppressEvent - whether to suppress the checkchange event.
30469      */
30470     setValue : function(v,suppressEvent){
30471         if (!this.el.dom) {
30472             return;
30473         }
30474         var old = this.el.dom.value ;
30475         this.el.dom.value = v;
30476         if (suppressEvent) {
30477             return ;
30478         }
30479          
30480         // update display..
30481         this.viewEl.select('img',true).each(function(e,i,n)  {
30482             
30483             var on = e.is(".x-menu-item-checked");
30484             var newv = v.indexOf(String(n)) > -1;
30485             if (on != newv) {
30486                 e.toggleClass('x-menu-item-checked');
30487             }
30488             
30489         });
30490         
30491         
30492         this.fireEvent('change', this, v, old);
30493         
30494         
30495     },
30496    
30497     // handle setting of hidden value by some other method!!?!?
30498     setFromHidden: function()
30499     {
30500         if(!this.el){
30501             return;
30502         }
30503         //console.log("SET FROM HIDDEN");
30504         //alert('setFrom hidden');
30505         this.setValue(this.el.dom.value);
30506     },
30507     
30508     onDestroy : function()
30509     {
30510         if(this.viewEl){
30511             Roo.get(this.viewEl).remove();
30512         }
30513          
30514         Roo.form.DayPicker.superclass.onDestroy.call(this);
30515     }
30516
30517 });/*
30518  * RooJS Library 1.1.1
30519  * Copyright(c) 2008-2011  Alan Knowles
30520  *
30521  * License - LGPL
30522  */
30523  
30524
30525 /**
30526  * @class Roo.form.ComboCheck
30527  * @extends Roo.form.ComboBox
30528  * A combobox for multiple select items.
30529  *
30530  * FIXME - could do with a reset button..
30531  * 
30532  * @constructor
30533  * Create a new ComboCheck
30534  * @param {Object} config Configuration options
30535  */
30536 Roo.form.ComboCheck = function(config){
30537     Roo.form.ComboCheck.superclass.constructor.call(this, config);
30538     // should verify some data...
30539     // like
30540     // hiddenName = required..
30541     // displayField = required
30542     // valudField == required
30543     var req= [ 'hiddenName', 'displayField', 'valueField' ];
30544     var _t = this;
30545     Roo.each(req, function(e) {
30546         if ((typeof(_t[e]) == 'undefined' ) || !_t[e].length) {
30547             throw "Roo.form.ComboCheck : missing value for: " + e;
30548         }
30549     });
30550     
30551     
30552 };
30553
30554 Roo.extend(Roo.form.ComboCheck, Roo.form.ComboBox, {
30555      
30556      
30557     editable : false,
30558      
30559     selectedClass: 'x-menu-item-checked', 
30560     
30561     // private
30562     onRender : function(ct, position){
30563         var _t = this;
30564         
30565         
30566         
30567         if(!this.tpl){
30568             var cls = 'x-combo-list';
30569
30570             
30571             this.tpl =  new Roo.Template({
30572                 html :  '<div class="'+cls+'-item x-menu-check-item">' +
30573                    '<img class="x-menu-item-icon" style="margin: 0px;" src="' + Roo.BLANK_IMAGE_URL + '">' + 
30574                    '<span>{' + this.displayField + '}</span>' +
30575                     '</div>' 
30576                 
30577             });
30578         }
30579  
30580         
30581         Roo.form.ComboCheck.superclass.onRender.call(this, ct, position);
30582         this.view.singleSelect = false;
30583         this.view.multiSelect = true;
30584         this.view.toggleSelect = true;
30585         this.pageTb.add(new Roo.Toolbar.Fill(), {
30586             
30587             text: 'Done',
30588             handler: function()
30589             {
30590                 _t.collapse();
30591             }
30592         });
30593     },
30594     
30595     onViewOver : function(e, t){
30596         // do nothing...
30597         return;
30598         
30599     },
30600     
30601     onViewClick : function(doFocus,index){
30602         return;
30603         
30604     },
30605     select: function () {
30606         //Roo.log("SELECT CALLED");
30607     },
30608      
30609     selectByValue : function(xv, scrollIntoView){
30610         var ar = this.getValueArray();
30611         var sels = [];
30612         
30613         Roo.each(ar, function(v) {
30614             if(v === undefined || v === null){
30615                 return;
30616             }
30617             var r = this.findRecord(this.valueField, v);
30618             if(r){
30619                 sels.push(this.store.indexOf(r))
30620                 
30621             }
30622         },this);
30623         this.view.select(sels);
30624         return false;
30625     },
30626     
30627     
30628     
30629     onSelect : function(record, index){
30630        // Roo.log("onselect Called");
30631        // this is only called by the clear button now..
30632         this.view.clearSelections();
30633         this.setValue('[]');
30634         if (this.value != this.valueBefore) {
30635             this.fireEvent('change', this, this.value, this.valueBefore);
30636             this.valueBefore = this.value;
30637         }
30638     },
30639     getValueArray : function()
30640     {
30641         var ar = [] ;
30642         
30643         try {
30644             //Roo.log(this.value);
30645             if (typeof(this.value) == 'undefined') {
30646                 return [];
30647             }
30648             var ar = Roo.decode(this.value);
30649             return  ar instanceof Array ? ar : []; //?? valid?
30650             
30651         } catch(e) {
30652             Roo.log(e + "\nRoo.form.ComboCheck:getValueArray  invalid data:" + this.getValue());
30653             return [];
30654         }
30655          
30656     },
30657     expand : function ()
30658     {
30659         
30660         Roo.form.ComboCheck.superclass.expand.call(this);
30661         this.valueBefore = typeof(this.value) == 'undefined' ? '' : this.value;
30662         //this.valueBefore = typeof(this.valueBefore) == 'undefined' ? '' : this.valueBefore;
30663         
30664
30665     },
30666     
30667     collapse : function(){
30668         Roo.form.ComboCheck.superclass.collapse.call(this);
30669         var sl = this.view.getSelectedIndexes();
30670         var st = this.store;
30671         var nv = [];
30672         var tv = [];
30673         var r;
30674         Roo.each(sl, function(i) {
30675             r = st.getAt(i);
30676             nv.push(r.get(this.valueField));
30677         },this);
30678         this.setValue(Roo.encode(nv));
30679         if (this.value != this.valueBefore) {
30680
30681             this.fireEvent('change', this, this.value, this.valueBefore);
30682             this.valueBefore = this.value;
30683         }
30684         
30685     },
30686     
30687     setValue : function(v){
30688         // Roo.log(v);
30689         this.value = v;
30690         
30691         var vals = this.getValueArray();
30692         var tv = [];
30693         Roo.each(vals, function(k) {
30694             var r = this.findRecord(this.valueField, k);
30695             if(r){
30696                 tv.push(r.data[this.displayField]);
30697             }else if(this.valueNotFoundText !== undefined){
30698                 tv.push( this.valueNotFoundText );
30699             }
30700         },this);
30701        // Roo.log(tv);
30702         
30703         Roo.form.ComboBox.superclass.setValue.call(this, tv.join(', '));
30704         this.hiddenField.value = v;
30705         this.value = v;
30706     }
30707     
30708 });/*
30709  * Based on:
30710  * Ext JS Library 1.1.1
30711  * Copyright(c) 2006-2007, Ext JS, LLC.
30712  *
30713  * Originally Released Under LGPL - original licence link has changed is not relivant.
30714  *
30715  * Fork - LGPL
30716  * <script type="text/javascript">
30717  */
30718  
30719 /**
30720  * @class Roo.form.Signature
30721  * @extends Roo.form.Field
30722  * Signature field.  
30723  * @constructor
30724  * 
30725  * @param {Object} config Configuration options
30726  */
30727
30728 Roo.form.Signature = function(config){
30729     Roo.form.Signature.superclass.constructor.call(this, config);
30730     
30731     this.addEvents({// not in used??
30732          /**
30733          * @event confirm
30734          * Fires when the 'confirm' icon is pressed (add a listener to enable add button)
30735              * @param {Roo.form.ComboBox} combo This combo box
30736              */
30737         'confirm' : true,
30738         /**
30739          * @event reset
30740          * Fires when the 'edit' icon is pressed (add a listener to enable add button)
30741              * @param {Roo.form.ComboBox} combo This combo box
30742              * @param {Roo.data.Record|false} record The data record returned from the underlying store (or false on nothing selected)
30743              */
30744         'reset' : true
30745     });
30746 };
30747
30748 Roo.extend(Roo.form.Signature, Roo.form.Field,  {
30749     /**
30750      * @cfg {Object} labels Label to use when rendering a form.
30751      * defaults to 
30752      * labels : { 
30753      *      clear : "Clear",
30754      *      confirm : "Confirm"
30755      *  }
30756      */
30757     labels : { 
30758         clear : "Clear",
30759         confirm : "Confirm"
30760     },
30761     /**
30762      * @cfg {Number} width The signature panel width (defaults to 300)
30763      */
30764     width: 300,
30765     /**
30766      * @cfg {Number} height The signature panel height (defaults to 100)
30767      */
30768     height : 100,
30769     /**
30770      * @cfg {Boolean} allowBlank False to validate that the value length > 0 (defaults to false)
30771      */
30772     allowBlank : false,
30773     
30774     //private
30775     // {Object} signPanel The signature SVG panel element (defaults to {})
30776     signPanel : {},
30777     // {Boolean} isMouseDown False to validate that the mouse down event (defaults to false)
30778     isMouseDown : false,
30779     // {Boolean} isConfirmed validate the signature is confirmed or not for submitting form (defaults to false)
30780     isConfirmed : false,
30781     // {String} signatureTmp SVG mapping string (defaults to empty string)
30782     signatureTmp : '',
30783     
30784     
30785     defaultAutoCreate : { // modified by initCompnoent..
30786         tag: "input",
30787         type:"hidden"
30788     },
30789
30790     // private
30791     onRender : function(ct, position){
30792         
30793         Roo.form.Signature.superclass.onRender.call(this, ct, position);
30794         
30795         this.wrap = this.el.wrap({
30796             cls:'x-form-signature-wrap', style : 'width: ' + this.width + 'px', cn:{cls:'x-form-signature'}
30797         });
30798         
30799         this.createToolbar(this);
30800         this.signPanel = this.wrap.createChild({
30801                 tag: 'div',
30802                 style: 'width: ' + this.width + 'px; height: ' + this.height + 'px; border: 0;'
30803             }, this.el
30804         );
30805             
30806         this.svgID = Roo.id();
30807         this.svgEl = this.signPanel.createChild({
30808               xmlns : 'http://www.w3.org/2000/svg',
30809               tag : 'svg',
30810               width: this.width,
30811               height: this.height,
30812               viewBox: '0 0 '+this.width+' '+this.height,
30813               cn : [
30814                 {
30815                     tag: "rect",
30816                     id: this.svgID + "-svg-r",
30817                     width: this.width,
30818                     height: this.height,
30819                     fill: "#ffa"
30820                 },
30821                 {
30822                     tag: "line",
30823                     x1: "0", // start
30824                     y1: (this.height*0.8), // start set the line in 80% of height
30825                     x2: this.width, // end
30826                     y2: (this.height*0.8), // end set the line in 80% of height
30827                     'stroke': "#666",
30828                     'stroke-width': "1",
30829                     'stroke-dasharray': "3",
30830                     'shape-rendering': "crispEdges",
30831                     'pointer-events': "none"
30832                 },
30833                 {
30834                     tag: "path",
30835                     id: this.svgID + "-svg-p",
30836                     'stroke': "navy",
30837                     'stroke-width': "3",
30838                     'fill': "none",
30839                     'pointer-events': 'none'
30840                 }
30841               ]
30842         });
30843         this.createSVG();
30844         this.svgBox = this.svgEl.dom.getScreenCTM();
30845     },
30846     createSVG : function(){ 
30847         var svg = this.signPanel;
30848         var r = svg.select('#'+ this.svgID + '-svg-r', true).first().dom;
30849         var t = this;
30850
30851         r.addEventListener('mousedown', function(e) { return t.down(e); }, false);
30852         r.addEventListener('mousemove', function(e) { return t.move(e); }, false);
30853         r.addEventListener('mouseup', function(e) { return t.up(e); }, false);
30854         r.addEventListener('mouseout', function(e) { return t.up(e); }, false);
30855         r.addEventListener('touchstart', function(e) { return t.down(e); }, false);
30856         r.addEventListener('touchmove', function(e) { return t.move(e); }, false);
30857         r.addEventListener('touchend', function(e) { return t.up(e); }, false);
30858         
30859     },
30860     isTouchEvent : function(e){
30861         return e.type.match(/^touch/);
30862     },
30863     getCoords : function (e) {
30864         var pt    = this.svgEl.dom.createSVGPoint();
30865         pt.x = e.clientX; 
30866         pt.y = e.clientY;
30867         if (this.isTouchEvent(e)) {
30868             pt.x =  e.targetTouches[0].clientX 
30869             pt.y = e.targetTouches[0].clientY;
30870         }
30871         var a = this.svgEl.dom.getScreenCTM();
30872         var b = a.inverse();
30873         var mx = pt.matrixTransform(b);
30874         return mx.x + ',' + mx.y;
30875     },
30876     //mouse event headler 
30877     down : function (e) {
30878         this.signatureTmp += 'M' + this.getCoords(e) + ' ';
30879         this.signPanel.select('#'+ this.svgID + '-svg-p', true).first().attr('d', this.signatureTmp);
30880         
30881         this.isMouseDown = true;
30882         
30883         e.preventDefault();
30884     },
30885     move : function (e) {
30886         if (this.isMouseDown) {
30887             this.signatureTmp += 'L' + this.getCoords(e) + ' ';
30888             this.signPanel.select('#'+ this.svgID + '-svg-p', true).first().attr( 'd', this.signatureTmp);
30889         }
30890         
30891         e.preventDefault();
30892     },
30893     up : function (e) {
30894         this.isMouseDown = false;
30895         var sp = this.signatureTmp.split(' ');
30896         
30897         if(sp.length > 1){
30898             if(!sp[sp.length-2].match(/^L/)){
30899                 sp.pop();
30900                 sp.pop();
30901                 sp.push("");
30902                 this.signatureTmp = sp.join(" ");
30903             }
30904         }
30905         if(this.getValue() != this.signatureTmp){
30906             this.signPanel.select('#'+ this.svgID + '-svg-r', true).first().attr('fill', '#ffa');
30907             this.isConfirmed = false;
30908         }
30909         e.preventDefault();
30910     },
30911     
30912     /**
30913      * Protected method that will not generally be called directly. It
30914      * is called when the editor creates its toolbar. Override this method if you need to
30915      * add custom toolbar buttons.
30916      * @param {HtmlEditor} editor
30917      */
30918     createToolbar : function(editor){
30919          function btn(id, toggle, handler){
30920             var xid = fid + '-'+ id ;
30921             return {
30922                 id : xid,
30923                 cmd : id,
30924                 cls : 'x-btn-icon x-edit-'+id,
30925                 enableToggle:toggle !== false,
30926                 scope: editor, // was editor...
30927                 handler:handler||editor.relayBtnCmd,
30928                 clickEvent:'mousedown',
30929                 tooltip: etb.buttonTips[id] || undefined, ///tips ???
30930                 tabIndex:-1
30931             };
30932         }
30933         
30934         
30935         var tb = new Roo.Toolbar(editor.wrap.dom.firstChild);
30936         this.tb = tb;
30937         this.tb.add(
30938            {
30939                 cls : ' x-signature-btn x-signature-'+id,
30940                 scope: editor, // was editor...
30941                 handler: this.reset,
30942                 clickEvent:'mousedown',
30943                 text: this.labels.clear
30944             },
30945             {
30946                  xtype : 'Fill',
30947                  xns: Roo.Toolbar
30948             }, 
30949             {
30950                 cls : '  x-signature-btn x-signature-'+id,
30951                 scope: editor, // was editor...
30952                 handler: this.setConfirmed,
30953                 clickEvent:'mousedown',
30954                 text: this.labels.confirm
30955             }
30956         );
30957     
30958     },
30959     //public
30960     /**
30961      * 
30962      * @return {String} Image Data URI
30963      */
30964     getImageDataURI : function(){
30965         var svg = this.svgEl.dom.outerHTML;
30966         var src = 'data:image/svg+xml;base64,'+window.btoa(svg);
30967         return src;
30968     },
30969     /**
30970      * 
30971      * @return {Boolean} this.isConfirmed
30972      */
30973     getConfirmed : function(){
30974         return this.isConfirmed;
30975     },
30976     /**
30977      * 
30978      * @return {Number} this.width
30979      */
30980     getWidth : function(){
30981         return this.width;
30982     },
30983     /**
30984      * 
30985      * @return {Number} this.height
30986      */
30987     getHeight : function(){
30988         return this.height;
30989     },
30990     // private
30991     getSignature : function(){
30992         return this.signatureTmp;
30993     },
30994     // private
30995     reset : function(){
30996         this.signatureTmp = '';
30997         this.signPanel.select('#'+ this.svgID + '-svg-r', true).first().attr('fill', '#ffa');
30998         this.signPanel.select('#'+ this.svgID + '-svg-p', true).first().attr( 'd', '');
30999         this.isConfirmed = false;
31000         Roo.form.Signature.superclass.reset.call(this);
31001     },
31002     test : function(){
31003 //        Roo.log(this.signPanel.dom.contentWindow.up())
31004     },
31005     //private
31006     setConfirmed : function(){
31007         if(!this.getSignature()){
31008             return;
31009         }
31010         this.signPanel.select('#'+ this.svgID + '-svg-r', true).first().attr('fill', '#cfc');
31011         this.setValue(this.getSignature());
31012         this.isConfirmed = true;
31013 //        Roo.log(Roo.get(this.signPanel.dom.contentWindow.r).attr('fill', '#cfc'));
31014     },
31015     // private
31016     // Subclasses should provide the validation implementation by overriding this
31017     validateValue : function(value){
31018         if(this.allowBlank){
31019             return true;
31020         }
31021         
31022         if(this.isConfirmed){
31023             return true;
31024         }
31025         return false;
31026     }
31027 });//<script type="text/javasscript">
31028  
31029
31030 /**
31031  * @class Roo.DDView
31032  * A DnD enabled version of Roo.View.
31033  * @param {Element/String} container The Element in which to create the View.
31034  * @param {String} tpl The template string used to create the markup for each element of the View
31035  * @param {Object} config The configuration properties. These include all the config options of
31036  * {@link Roo.View} plus some specific to this class.<br>
31037  * <p>
31038  * Drag/drop is implemented by adding {@link Roo.data.Record}s to the target DDView. If copying is
31039  * not being performed, the original {@link Roo.data.Record} is removed from the source DDView.<br>
31040  * <p>
31041  * The following extra CSS rules are needed to provide insertion point highlighting:<pre><code>
31042 .x-view-drag-insert-above {
31043         border-top:1px dotted #3366cc;
31044 }
31045 .x-view-drag-insert-below {
31046         border-bottom:1px dotted #3366cc;
31047 }
31048 </code></pre>
31049  * 
31050  */
31051  
31052 Roo.DDView = function(container, tpl, config) {
31053     Roo.DDView.superclass.constructor.apply(this, arguments);
31054     this.getEl().setStyle("outline", "0px none");
31055     this.getEl().unselectable();
31056     if (this.dragGroup) {
31057                 this.setDraggable(this.dragGroup.split(","));
31058     }
31059     if (this.dropGroup) {
31060                 this.setDroppable(this.dropGroup.split(","));
31061     }
31062     if (this.deletable) {
31063         this.setDeletable();
31064     }
31065     this.isDirtyFlag = false;
31066         this.addEvents({
31067                 "drop" : true
31068         });
31069 };
31070
31071 Roo.extend(Roo.DDView, Roo.View, {
31072 /**     @cfg {String/Array} dragGroup The ddgroup name(s) for the View's DragZone. */
31073 /**     @cfg {String/Array} dropGroup The ddgroup name(s) for the View's DropZone. */
31074 /**     @cfg {Boolean} copy Causes drag operations to copy nodes rather than move. */
31075 /**     @cfg {Boolean} allowCopy Causes ctrl/drag operations to copy nodes rather than move. */
31076
31077         isFormField: true,
31078
31079         reset: Roo.emptyFn,
31080         
31081         clearInvalid: Roo.form.Field.prototype.clearInvalid,
31082
31083         validate: function() {
31084                 return true;
31085         },
31086         
31087         destroy: function() {
31088                 this.purgeListeners();
31089                 this.getEl.removeAllListeners();
31090                 this.getEl().remove();
31091                 if (this.dragZone) {
31092                         if (this.dragZone.destroy) {
31093                                 this.dragZone.destroy();
31094                         }
31095                 }
31096                 if (this.dropZone) {
31097                         if (this.dropZone.destroy) {
31098                                 this.dropZone.destroy();
31099                         }
31100                 }
31101         },
31102
31103 /**     Allows this class to be an Roo.form.Field so it can be found using {@link Roo.form.BasicForm#findField}. */
31104         getName: function() {
31105                 return this.name;
31106         },
31107
31108 /**     Loads the View from a JSON string representing the Records to put into the Store. */
31109         setValue: function(v) {
31110                 if (!this.store) {
31111                         throw "DDView.setValue(). DDView must be constructed with a valid Store";
31112                 }
31113                 var data = {};
31114                 data[this.store.reader.meta.root] = v ? [].concat(v) : [];
31115                 this.store.proxy = new Roo.data.MemoryProxy(data);
31116                 this.store.load();
31117         },
31118
31119 /**     @return {String} a parenthesised list of the ids of the Records in the View. */
31120         getValue: function() {
31121                 var result = '(';
31122                 this.store.each(function(rec) {
31123                         result += rec.id + ',';
31124                 });
31125                 return result.substr(0, result.length - 1) + ')';
31126         },
31127         
31128         getIds: function() {
31129                 var i = 0, result = new Array(this.store.getCount());
31130                 this.store.each(function(rec) {
31131                         result[i++] = rec.id;
31132                 });
31133                 return result;
31134         },
31135         
31136         isDirty: function() {
31137                 return this.isDirtyFlag;
31138         },
31139
31140 /**
31141  *      Part of the Roo.dd.DropZone interface. If no target node is found, the
31142  *      whole Element becomes the target, and this causes the drop gesture to append.
31143  */
31144     getTargetFromEvent : function(e) {
31145                 var target = e.getTarget();
31146                 while ((target !== null) && (target.parentNode != this.el.dom)) {
31147                 target = target.parentNode;
31148                 }
31149                 if (!target) {
31150                         target = this.el.dom.lastChild || this.el.dom;
31151                 }
31152                 return target;
31153     },
31154
31155 /**
31156  *      Create the drag data which consists of an object which has the property "ddel" as
31157  *      the drag proxy element. 
31158  */
31159     getDragData : function(e) {
31160         var target = this.findItemFromChild(e.getTarget());
31161                 if(target) {
31162                         this.handleSelection(e);
31163                         var selNodes = this.getSelectedNodes();
31164             var dragData = {
31165                 source: this,
31166                 copy: this.copy || (this.allowCopy && e.ctrlKey),
31167                 nodes: selNodes,
31168                 records: []
31169                         };
31170                         var selectedIndices = this.getSelectedIndexes();
31171                         for (var i = 0; i < selectedIndices.length; i++) {
31172                                 dragData.records.push(this.store.getAt(selectedIndices[i]));
31173                         }
31174                         if (selNodes.length == 1) {
31175                                 dragData.ddel = target.cloneNode(true); // the div element
31176                         } else {
31177                                 var div = document.createElement('div'); // create the multi element drag "ghost"
31178                                 div.className = 'multi-proxy';
31179                                 for (var i = 0, len = selNodes.length; i < len; i++) {
31180                                         div.appendChild(selNodes[i].cloneNode(true));
31181                                 }
31182                                 dragData.ddel = div;
31183                         }
31184             //console.log(dragData)
31185             //console.log(dragData.ddel.innerHTML)
31186                         return dragData;
31187                 }
31188         //console.log('nodragData')
31189                 return false;
31190     },
31191     
31192 /**     Specify to which ddGroup items in this DDView may be dragged. */
31193     setDraggable: function(ddGroup) {
31194         if (ddGroup instanceof Array) {
31195                 Roo.each(ddGroup, this.setDraggable, this);
31196                 return;
31197         }
31198         if (this.dragZone) {
31199                 this.dragZone.addToGroup(ddGroup);
31200         } else {
31201                         this.dragZone = new Roo.dd.DragZone(this.getEl(), {
31202                                 containerScroll: true,
31203                                 ddGroup: ddGroup 
31204
31205                         });
31206 //                      Draggability implies selection. DragZone's mousedown selects the element.
31207                         if (!this.multiSelect) { this.singleSelect = true; }
31208
31209 //                      Wire the DragZone's handlers up to methods in *this*
31210                         this.dragZone.getDragData = this.getDragData.createDelegate(this);
31211                 }
31212     },
31213
31214 /**     Specify from which ddGroup this DDView accepts drops. */
31215     setDroppable: function(ddGroup) {
31216         if (ddGroup instanceof Array) {
31217                 Roo.each(ddGroup, this.setDroppable, this);
31218                 return;
31219         }
31220         if (this.dropZone) {
31221                 this.dropZone.addToGroup(ddGroup);
31222         } else {
31223                         this.dropZone = new Roo.dd.DropZone(this.getEl(), {
31224                                 containerScroll: true,
31225                                 ddGroup: ddGroup
31226                         });
31227
31228 //                      Wire the DropZone's handlers up to methods in *this*
31229                         this.dropZone.getTargetFromEvent = this.getTargetFromEvent.createDelegate(this);
31230                         this.dropZone.onNodeEnter = this.onNodeEnter.createDelegate(this);
31231                         this.dropZone.onNodeOver = this.onNodeOver.createDelegate(this);
31232                         this.dropZone.onNodeOut = this.onNodeOut.createDelegate(this);
31233                         this.dropZone.onNodeDrop = this.onNodeDrop.createDelegate(this);
31234                 }
31235     },
31236
31237 /**     Decide whether to drop above or below a View node. */
31238     getDropPoint : function(e, n, dd){
31239         if (n == this.el.dom) { return "above"; }
31240                 var t = Roo.lib.Dom.getY(n), b = t + n.offsetHeight;
31241                 var c = t + (b - t) / 2;
31242                 var y = Roo.lib.Event.getPageY(e);
31243                 if(y <= c) {
31244                         return "above";
31245                 }else{
31246                         return "below";
31247                 }
31248     },
31249
31250     onNodeEnter : function(n, dd, e, data){
31251                 return false;
31252     },
31253     
31254     onNodeOver : function(n, dd, e, data){
31255                 var pt = this.getDropPoint(e, n, dd);
31256                 // set the insert point style on the target node
31257                 var dragElClass = this.dropNotAllowed;
31258                 if (pt) {
31259                         var targetElClass;
31260                         if (pt == "above"){
31261                                 dragElClass = n.previousSibling ? "x-tree-drop-ok-between" : "x-tree-drop-ok-above";
31262                                 targetElClass = "x-view-drag-insert-above";
31263                         } else {
31264                                 dragElClass = n.nextSibling ? "x-tree-drop-ok-between" : "x-tree-drop-ok-below";
31265                                 targetElClass = "x-view-drag-insert-below";
31266                         }
31267                         if (this.lastInsertClass != targetElClass){
31268                                 Roo.fly(n).replaceClass(this.lastInsertClass, targetElClass);
31269                                 this.lastInsertClass = targetElClass;
31270                         }
31271                 }
31272                 return dragElClass;
31273         },
31274
31275     onNodeOut : function(n, dd, e, data){
31276                 this.removeDropIndicators(n);
31277     },
31278
31279     onNodeDrop : function(n, dd, e, data){
31280         if (this.fireEvent("drop", this, n, dd, e, data) === false) {
31281                 return false;
31282         }
31283         var pt = this.getDropPoint(e, n, dd);
31284                 var insertAt = (n == this.el.dom) ? this.nodes.length : n.nodeIndex;
31285                 if (pt == "below") { insertAt++; }
31286                 for (var i = 0; i < data.records.length; i++) {
31287                         var r = data.records[i];
31288                         var dup = this.store.getById(r.id);
31289                         if (dup && (dd != this.dragZone)) {
31290                                 Roo.fly(this.getNode(this.store.indexOf(dup))).frame("red", 1);
31291                         } else {
31292                                 if (data.copy) {
31293                                         this.store.insert(insertAt++, r.copy());
31294                                 } else {
31295                                         data.source.isDirtyFlag = true;
31296                                         r.store.remove(r);
31297                                         this.store.insert(insertAt++, r);
31298                                 }
31299                                 this.isDirtyFlag = true;
31300                         }
31301                 }
31302                 this.dragZone.cachedTarget = null;
31303                 return true;
31304     },
31305
31306     removeDropIndicators : function(n){
31307                 if(n){
31308                         Roo.fly(n).removeClass([
31309                                 "x-view-drag-insert-above",
31310                                 "x-view-drag-insert-below"]);
31311                         this.lastInsertClass = "_noclass";
31312                 }
31313     },
31314
31315 /**
31316  *      Utility method. Add a delete option to the DDView's context menu.
31317  *      @param {String} imageUrl The URL of the "delete" icon image.
31318  */
31319         setDeletable: function(imageUrl) {
31320                 if (!this.singleSelect && !this.multiSelect) {
31321                         this.singleSelect = true;
31322                 }
31323                 var c = this.getContextMenu();
31324                 this.contextMenu.on("itemclick", function(item) {
31325                         switch (item.id) {
31326                                 case "delete":
31327                                         this.remove(this.getSelectedIndexes());
31328                                         break;
31329                         }
31330                 }, this);
31331                 this.contextMenu.add({
31332                         icon: imageUrl,
31333                         id: "delete",
31334                         text: 'Delete'
31335                 });
31336         },
31337         
31338 /**     Return the context menu for this DDView. */
31339         getContextMenu: function() {
31340                 if (!this.contextMenu) {
31341 //                      Create the View's context menu
31342                         this.contextMenu = new Roo.menu.Menu({
31343                                 id: this.id + "-contextmenu"
31344                         });
31345                         this.el.on("contextmenu", this.showContextMenu, this);
31346                 }
31347                 return this.contextMenu;
31348         },
31349         
31350         disableContextMenu: function() {
31351                 if (this.contextMenu) {
31352                         this.el.un("contextmenu", this.showContextMenu, this);
31353                 }
31354         },
31355
31356         showContextMenu: function(e, item) {
31357         item = this.findItemFromChild(e.getTarget());
31358                 if (item) {
31359                         e.stopEvent();
31360                         this.select(this.getNode(item), this.multiSelect && e.ctrlKey, true);
31361                         this.contextMenu.showAt(e.getXY());
31362             }
31363     },
31364
31365 /**
31366  *      Remove {@link Roo.data.Record}s at the specified indices.
31367  *      @param {Array/Number} selectedIndices The index (or Array of indices) of Records to remove.
31368  */
31369     remove: function(selectedIndices) {
31370                 selectedIndices = [].concat(selectedIndices);
31371                 for (var i = 0; i < selectedIndices.length; i++) {
31372                         var rec = this.store.getAt(selectedIndices[i]);
31373                         this.store.remove(rec);
31374                 }
31375     },
31376
31377 /**
31378  *      Double click fires the event, but also, if this is draggable, and there is only one other
31379  *      related DropZone, it transfers the selected node.
31380  */
31381     onDblClick : function(e){
31382         var item = this.findItemFromChild(e.getTarget());
31383         if(item){
31384             if (this.fireEvent("dblclick", this, this.indexOf(item), item, e) === false) {
31385                 return false;
31386             }
31387             if (this.dragGroup) {
31388                     var targets = Roo.dd.DragDropMgr.getRelated(this.dragZone, true);
31389                     while (targets.indexOf(this.dropZone) > -1) {
31390                             targets.remove(this.dropZone);
31391                                 }
31392                     if (targets.length == 1) {
31393                                         this.dragZone.cachedTarget = null;
31394                         var el = Roo.get(targets[0].getEl());
31395                         var box = el.getBox(true);
31396                         targets[0].onNodeDrop(el.dom, {
31397                                 target: el.dom,
31398                                 xy: [box.x, box.y + box.height - 1]
31399                         }, null, this.getDragData(e));
31400                     }
31401                 }
31402         }
31403     },
31404     
31405     handleSelection: function(e) {
31406                 this.dragZone.cachedTarget = null;
31407         var item = this.findItemFromChild(e.getTarget());
31408         if (!item) {
31409                 this.clearSelections(true);
31410                 return;
31411         }
31412                 if (item && (this.multiSelect || this.singleSelect)){
31413                         if(this.multiSelect && e.shiftKey && (!e.ctrlKey) && this.lastSelection){
31414                                 this.select(this.getNodes(this.indexOf(this.lastSelection), item.nodeIndex), false);
31415                         }else if (this.isSelected(this.getNode(item)) && e.ctrlKey){
31416                                 this.unselect(item);
31417                         } else {
31418                                 this.select(item, this.multiSelect && e.ctrlKey);
31419                                 this.lastSelection = item;
31420                         }
31421                 }
31422     },
31423
31424     onItemClick : function(item, index, e){
31425                 if(this.fireEvent("beforeclick", this, index, item, e) === false){
31426                         return false;
31427                 }
31428                 return true;
31429     },
31430
31431     unselect : function(nodeInfo, suppressEvent){
31432                 var node = this.getNode(nodeInfo);
31433                 if(node && this.isSelected(node)){
31434                         if(this.fireEvent("beforeselect", this, node, this.selections) !== false){
31435                                 Roo.fly(node).removeClass(this.selectedClass);
31436                                 this.selections.remove(node);
31437                                 if(!suppressEvent){
31438                                         this.fireEvent("selectionchange", this, this.selections);
31439                                 }
31440                         }
31441                 }
31442     }
31443 });
31444 /*
31445  * Based on:
31446  * Ext JS Library 1.1.1
31447  * Copyright(c) 2006-2007, Ext JS, LLC.
31448  *
31449  * Originally Released Under LGPL - original licence link has changed is not relivant.
31450  *
31451  * Fork - LGPL
31452  * <script type="text/javascript">
31453  */
31454  
31455 /**
31456  * @class Roo.LayoutManager
31457  * @extends Roo.util.Observable
31458  * Base class for layout managers.
31459  */
31460 Roo.LayoutManager = function(container, config){
31461     Roo.LayoutManager.superclass.constructor.call(this);
31462     this.el = Roo.get(container);
31463     // ie scrollbar fix
31464     if(this.el.dom == document.body && Roo.isIE && !config.allowScroll){
31465         document.body.scroll = "no";
31466     }else if(this.el.dom != document.body && this.el.getStyle('position') == 'static'){
31467         this.el.position('relative');
31468     }
31469     this.id = this.el.id;
31470     this.el.addClass("x-layout-container");
31471     /** false to disable window resize monitoring @type Boolean */
31472     this.monitorWindowResize = true;
31473     this.regions = {};
31474     this.addEvents({
31475         /**
31476          * @event layout
31477          * Fires when a layout is performed. 
31478          * @param {Roo.LayoutManager} this
31479          */
31480         "layout" : true,
31481         /**
31482          * @event regionresized
31483          * Fires when the user resizes a region. 
31484          * @param {Roo.LayoutRegion} region The resized region
31485          * @param {Number} newSize The new size (width for east/west, height for north/south)
31486          */
31487         "regionresized" : true,
31488         /**
31489          * @event regioncollapsed
31490          * Fires when a region is collapsed. 
31491          * @param {Roo.LayoutRegion} region The collapsed region
31492          */
31493         "regioncollapsed" : true,
31494         /**
31495          * @event regionexpanded
31496          * Fires when a region is expanded.  
31497          * @param {Roo.LayoutRegion} region The expanded region
31498          */
31499         "regionexpanded" : true
31500     });
31501     this.updating = false;
31502     Roo.EventManager.onWindowResize(this.onWindowResize, this, true);
31503 };
31504
31505 Roo.extend(Roo.LayoutManager, Roo.util.Observable, {
31506     /**
31507      * Returns true if this layout is currently being updated
31508      * @return {Boolean}
31509      */
31510     isUpdating : function(){
31511         return this.updating; 
31512     },
31513     
31514     /**
31515      * Suspend the LayoutManager from doing auto-layouts while
31516      * making multiple add or remove calls
31517      */
31518     beginUpdate : function(){
31519         this.updating = true;    
31520     },
31521     
31522     /**
31523      * Restore auto-layouts and optionally disable the manager from performing a layout
31524      * @param {Boolean} noLayout true to disable a layout update 
31525      */
31526     endUpdate : function(noLayout){
31527         this.updating = false;
31528         if(!noLayout){
31529             this.layout();
31530         }    
31531     },
31532     
31533     layout: function(){
31534         
31535     },
31536     
31537     onRegionResized : function(region, newSize){
31538         this.fireEvent("regionresized", region, newSize);
31539         this.layout();
31540     },
31541     
31542     onRegionCollapsed : function(region){
31543         this.fireEvent("regioncollapsed", region);
31544     },
31545     
31546     onRegionExpanded : function(region){
31547         this.fireEvent("regionexpanded", region);
31548     },
31549         
31550     /**
31551      * Returns the size of the current view. This method normalizes document.body and element embedded layouts and
31552      * performs box-model adjustments.
31553      * @return {Object} The size as an object {width: (the width), height: (the height)}
31554      */
31555     getViewSize : function(){
31556         var size;
31557         if(this.el.dom != document.body){
31558             size = this.el.getSize();
31559         }else{
31560             size = {width: Roo.lib.Dom.getViewWidth(), height: Roo.lib.Dom.getViewHeight()};
31561         }
31562         size.width -= this.el.getBorderWidth("lr")-this.el.getPadding("lr");
31563         size.height -= this.el.getBorderWidth("tb")-this.el.getPadding("tb");
31564         return size;
31565     },
31566     
31567     /**
31568      * Returns the Element this layout is bound to.
31569      * @return {Roo.Element}
31570      */
31571     getEl : function(){
31572         return this.el;
31573     },
31574     
31575     /**
31576      * Returns the specified region.
31577      * @param {String} target The region key ('center', 'north', 'south', 'east' or 'west')
31578      * @return {Roo.LayoutRegion}
31579      */
31580     getRegion : function(target){
31581         return this.regions[target.toLowerCase()];
31582     },
31583     
31584     onWindowResize : function(){
31585         if(this.monitorWindowResize){
31586             this.layout();
31587         }
31588     }
31589 });/*
31590  * Based on:
31591  * Ext JS Library 1.1.1
31592  * Copyright(c) 2006-2007, Ext JS, LLC.
31593  *
31594  * Originally Released Under LGPL - original licence link has changed is not relivant.
31595  *
31596  * Fork - LGPL
31597  * <script type="text/javascript">
31598  */
31599 /**
31600  * @class Roo.BorderLayout
31601  * @extends Roo.LayoutManager
31602  * This class represents a common layout manager used in desktop applications. For screenshots and more details,
31603  * please see: <br><br>
31604  * <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>
31605  * <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>
31606  * Example:
31607  <pre><code>
31608  var layout = new Roo.BorderLayout(document.body, {
31609     north: {
31610         initialSize: 25,
31611         titlebar: false
31612     },
31613     west: {
31614         split:true,
31615         initialSize: 200,
31616         minSize: 175,
31617         maxSize: 400,
31618         titlebar: true,
31619         collapsible: true
31620     },
31621     east: {
31622         split:true,
31623         initialSize: 202,
31624         minSize: 175,
31625         maxSize: 400,
31626         titlebar: true,
31627         collapsible: true
31628     },
31629     south: {
31630         split:true,
31631         initialSize: 100,
31632         minSize: 100,
31633         maxSize: 200,
31634         titlebar: true,
31635         collapsible: true
31636     },
31637     center: {
31638         titlebar: true,
31639         autoScroll:true,
31640         resizeTabs: true,
31641         minTabWidth: 50,
31642         preferredTabWidth: 150
31643     }
31644 });
31645
31646 // shorthand
31647 var CP = Roo.ContentPanel;
31648
31649 layout.beginUpdate();
31650 layout.add("north", new CP("north", "North"));
31651 layout.add("south", new CP("south", {title: "South", closable: true}));
31652 layout.add("west", new CP("west", {title: "West"}));
31653 layout.add("east", new CP("autoTabs", {title: "Auto Tabs", closable: true}));
31654 layout.add("center", new CP("center1", {title: "Close Me", closable: true}));
31655 layout.add("center", new CP("center2", {title: "Center Panel", closable: false}));
31656 layout.getRegion("center").showPanel("center1");
31657 layout.endUpdate();
31658 </code></pre>
31659
31660 <b>The container the layout is rendered into can be either the body element or any other element.
31661 If it is not the body element, the container needs to either be an absolute positioned element,
31662 or you will need to add "position:relative" to the css of the container.  You will also need to specify
31663 the container size if it is not the body element.</b>
31664
31665 * @constructor
31666 * Create a new BorderLayout
31667 * @param {String/HTMLElement/Element} container The container this layout is bound to
31668 * @param {Object} config Configuration options
31669  */
31670 Roo.BorderLayout = function(container, config){
31671     config = config || {};
31672     Roo.BorderLayout.superclass.constructor.call(this, container, config);
31673     this.factory = config.factory || Roo.BorderLayout.RegionFactory;
31674     for(var i = 0, len = this.factory.validRegions.length; i < len; i++) {
31675         var target = this.factory.validRegions[i];
31676         if(config[target]){
31677             this.addRegion(target, config[target]);
31678         }
31679     }
31680 };
31681
31682 Roo.extend(Roo.BorderLayout, Roo.LayoutManager, {
31683     /**
31684      * Creates and adds a new region if it doesn't already exist.
31685      * @param {String} target The target region key (north, south, east, west or center).
31686      * @param {Object} config The regions config object
31687      * @return {BorderLayoutRegion} The new region
31688      */
31689     addRegion : function(target, config){
31690         if(!this.regions[target]){
31691             var r = this.factory.create(target, this, config);
31692             this.bindRegion(target, r);
31693         }
31694         return this.regions[target];
31695     },
31696
31697     // private (kinda)
31698     bindRegion : function(name, r){
31699         this.regions[name] = r;
31700         r.on("visibilitychange", this.layout, this);
31701         r.on("paneladded", this.layout, this);
31702         r.on("panelremoved", this.layout, this);
31703         r.on("invalidated", this.layout, this);
31704         r.on("resized", this.onRegionResized, this);
31705         r.on("collapsed", this.onRegionCollapsed, this);
31706         r.on("expanded", this.onRegionExpanded, this);
31707     },
31708
31709     /**
31710      * Performs a layout update.
31711      */
31712     layout : function(){
31713         if(this.updating) return;
31714         var size = this.getViewSize();
31715         var w = size.width;
31716         var h = size.height;
31717         var centerW = w;
31718         var centerH = h;
31719         var centerY = 0;
31720         var centerX = 0;
31721         //var x = 0, y = 0;
31722
31723         var rs = this.regions;
31724         var north = rs["north"];
31725         var south = rs["south"]; 
31726         var west = rs["west"];
31727         var east = rs["east"];
31728         var center = rs["center"];
31729         //if(this.hideOnLayout){ // not supported anymore
31730             //c.el.setStyle("display", "none");
31731         //}
31732         if(north && north.isVisible()){
31733             var b = north.getBox();
31734             var m = north.getMargins();
31735             b.width = w - (m.left+m.right);
31736             b.x = m.left;
31737             b.y = m.top;
31738             centerY = b.height + b.y + m.bottom;
31739             centerH -= centerY;
31740             north.updateBox(this.safeBox(b));
31741         }
31742         if(south && south.isVisible()){
31743             var b = south.getBox();
31744             var m = south.getMargins();
31745             b.width = w - (m.left+m.right);
31746             b.x = m.left;
31747             var totalHeight = (b.height + m.top + m.bottom);
31748             b.y = h - totalHeight + m.top;
31749             centerH -= totalHeight;
31750             south.updateBox(this.safeBox(b));
31751         }
31752         if(west && west.isVisible()){
31753             var b = west.getBox();
31754             var m = west.getMargins();
31755             b.height = centerH - (m.top+m.bottom);
31756             b.x = m.left;
31757             b.y = centerY + m.top;
31758             var totalWidth = (b.width + m.left + m.right);
31759             centerX += totalWidth;
31760             centerW -= totalWidth;
31761             west.updateBox(this.safeBox(b));
31762         }
31763         if(east && east.isVisible()){
31764             var b = east.getBox();
31765             var m = east.getMargins();
31766             b.height = centerH - (m.top+m.bottom);
31767             var totalWidth = (b.width + m.left + m.right);
31768             b.x = w - totalWidth + m.left;
31769             b.y = centerY + m.top;
31770             centerW -= totalWidth;
31771             east.updateBox(this.safeBox(b));
31772         }
31773         if(center){
31774             var m = center.getMargins();
31775             var centerBox = {
31776                 x: centerX + m.left,
31777                 y: centerY + m.top,
31778                 width: centerW - (m.left+m.right),
31779                 height: centerH - (m.top+m.bottom)
31780             };
31781             //if(this.hideOnLayout){
31782                 //center.el.setStyle("display", "block");
31783             //}
31784             center.updateBox(this.safeBox(centerBox));
31785         }
31786         this.el.repaint();
31787         this.fireEvent("layout", this);
31788     },
31789
31790     // private
31791     safeBox : function(box){
31792         box.width = Math.max(0, box.width);
31793         box.height = Math.max(0, box.height);
31794         return box;
31795     },
31796
31797     /**
31798      * Adds a ContentPanel (or subclass) to this layout.
31799      * @param {String} target The target region key (north, south, east, west or center).
31800      * @param {Roo.ContentPanel} panel The panel to add
31801      * @return {Roo.ContentPanel} The added panel
31802      */
31803     add : function(target, panel){
31804          
31805         target = target.toLowerCase();
31806         return this.regions[target].add(panel);
31807     },
31808
31809     /**
31810      * Remove a ContentPanel (or subclass) to this layout.
31811      * @param {String} target The target region key (north, south, east, west or center).
31812      * @param {Number/String/Roo.ContentPanel} panel The index, id or panel to remove
31813      * @return {Roo.ContentPanel} The removed panel
31814      */
31815     remove : function(target, panel){
31816         target = target.toLowerCase();
31817         return this.regions[target].remove(panel);
31818     },
31819
31820     /**
31821      * Searches all regions for a panel with the specified id
31822      * @param {String} panelId
31823      * @return {Roo.ContentPanel} The panel or null if it wasn't found
31824      */
31825     findPanel : function(panelId){
31826         var rs = this.regions;
31827         for(var target in rs){
31828             if(typeof rs[target] != "function"){
31829                 var p = rs[target].getPanel(panelId);
31830                 if(p){
31831                     return p;
31832                 }
31833             }
31834         }
31835         return null;
31836     },
31837
31838     /**
31839      * Searches all regions for a panel with the specified id and activates (shows) it.
31840      * @param {String/ContentPanel} panelId The panels id or the panel itself
31841      * @return {Roo.ContentPanel} The shown panel or null
31842      */
31843     showPanel : function(panelId) {
31844       var rs = this.regions;
31845       for(var target in rs){
31846          var r = rs[target];
31847          if(typeof r != "function"){
31848             if(r.hasPanel(panelId)){
31849                return r.showPanel(panelId);
31850             }
31851          }
31852       }
31853       return null;
31854    },
31855
31856    /**
31857      * Restores this layout's state using Roo.state.Manager or the state provided by the passed provider.
31858      * @param {Roo.state.Provider} provider (optional) An alternate state provider
31859      */
31860     restoreState : function(provider){
31861         if(!provider){
31862             provider = Roo.state.Manager;
31863         }
31864         var sm = new Roo.LayoutStateManager();
31865         sm.init(this, provider);
31866     },
31867
31868     /**
31869      * Adds a batch of multiple ContentPanels dynamically by passing a special regions config object.  This config
31870      * object should contain properties for each region to add ContentPanels to, and each property's value should be
31871      * a valid ContentPanel config object.  Example:
31872      * <pre><code>
31873 // Create the main layout
31874 var layout = new Roo.BorderLayout('main-ct', {
31875     west: {
31876         split:true,
31877         minSize: 175,
31878         titlebar: true
31879     },
31880     center: {
31881         title:'Components'
31882     }
31883 }, 'main-ct');
31884
31885 // Create and add multiple ContentPanels at once via configs
31886 layout.batchAdd({
31887    west: {
31888        id: 'source-files',
31889        autoCreate:true,
31890        title:'Ext Source Files',
31891        autoScroll:true,
31892        fitToFrame:true
31893    },
31894    center : {
31895        el: cview,
31896        autoScroll:true,
31897        fitToFrame:true,
31898        toolbar: tb,
31899        resizeEl:'cbody'
31900    }
31901 });
31902 </code></pre>
31903      * @param {Object} regions An object containing ContentPanel configs by region name
31904      */
31905     batchAdd : function(regions){
31906         this.beginUpdate();
31907         for(var rname in regions){
31908             var lr = this.regions[rname];
31909             if(lr){
31910                 this.addTypedPanels(lr, regions[rname]);
31911             }
31912         }
31913         this.endUpdate();
31914     },
31915
31916     // private
31917     addTypedPanels : function(lr, ps){
31918         if(typeof ps == 'string'){
31919             lr.add(new Roo.ContentPanel(ps));
31920         }
31921         else if(ps instanceof Array){
31922             for(var i =0, len = ps.length; i < len; i++){
31923                 this.addTypedPanels(lr, ps[i]);
31924             }
31925         }
31926         else if(!ps.events){ // raw config?
31927             var el = ps.el;
31928             delete ps.el; // prevent conflict
31929             lr.add(new Roo.ContentPanel(el || Roo.id(), ps));
31930         }
31931         else {  // panel object assumed!
31932             lr.add(ps);
31933         }
31934     },
31935     /**
31936      * Adds a xtype elements to the layout.
31937      * <pre><code>
31938
31939 layout.addxtype({
31940        xtype : 'ContentPanel',
31941        region: 'west',
31942        items: [ .... ]
31943    }
31944 );
31945
31946 layout.addxtype({
31947         xtype : 'NestedLayoutPanel',
31948         region: 'west',
31949         layout: {
31950            center: { },
31951            west: { }   
31952         },
31953         items : [ ... list of content panels or nested layout panels.. ]
31954    }
31955 );
31956 </code></pre>
31957      * @param {Object} cfg Xtype definition of item to add.
31958      */
31959     addxtype : function(cfg)
31960     {
31961         // basically accepts a pannel...
31962         // can accept a layout region..!?!?
31963         //Roo.log('Roo.BorderLayout add ' + cfg.xtype)
31964         
31965         if (!cfg.xtype.match(/Panel$/)) {
31966             return false;
31967         }
31968         var ret = false;
31969         
31970         if (typeof(cfg.region) == 'undefined') {
31971             Roo.log("Failed to add Panel, region was not set");
31972             Roo.log(cfg);
31973             return false;
31974         }
31975         var region = cfg.region;
31976         delete cfg.region;
31977         
31978           
31979         var xitems = [];
31980         if (cfg.items) {
31981             xitems = cfg.items;
31982             delete cfg.items;
31983         }
31984         var nb = false;
31985         
31986         switch(cfg.xtype) 
31987         {
31988             case 'ContentPanel':  // ContentPanel (el, cfg)
31989             case 'ScrollPanel':  // ContentPanel (el, cfg)
31990             case 'ViewPanel': 
31991                 if(cfg.autoCreate) {
31992                     ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
31993                 } else {
31994                     var el = this.el.createChild();
31995                     ret = new Roo[cfg.xtype](el, cfg); // new panel!!!!!
31996                 }
31997                 
31998                 this.add(region, ret);
31999                 break;
32000             
32001             
32002             case 'TreePanel': // our new panel!
32003                 cfg.el = this.el.createChild();
32004                 ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
32005                 this.add(region, ret);
32006                 break;
32007             
32008             case 'NestedLayoutPanel': 
32009                 // create a new Layout (which is  a Border Layout...
32010                 var el = this.el.createChild();
32011                 var clayout = cfg.layout;
32012                 delete cfg.layout;
32013                 clayout.items   = clayout.items  || [];
32014                 // replace this exitems with the clayout ones..
32015                 xitems = clayout.items;
32016                  
32017                 
32018                 if (region == 'center' && this.active && this.getRegion('center').panels.length < 1) {
32019                     cfg.background = false;
32020                 }
32021                 var layout = new Roo.BorderLayout(el, clayout);
32022                 
32023                 ret = new Roo[cfg.xtype](layout, cfg); // new panel!!!!!
32024                 //console.log('adding nested layout panel '  + cfg.toSource());
32025                 this.add(region, ret);
32026                 nb = {}; /// find first...
32027                 break;
32028                 
32029             case 'GridPanel': 
32030             
32031                 // needs grid and region
32032                 
32033                 //var el = this.getRegion(region).el.createChild();
32034                 var el = this.el.createChild();
32035                 // create the grid first...
32036                 
32037                 var grid = new Roo.grid[cfg.grid.xtype](el, cfg.grid);
32038                 delete cfg.grid;
32039                 if (region == 'center' && this.active ) {
32040                     cfg.background = false;
32041                 }
32042                 ret = new Roo[cfg.xtype](grid, cfg); // new panel!!!!!
32043                 
32044                 this.add(region, ret);
32045                 if (cfg.background) {
32046                     ret.on('activate', function(gp) {
32047                         if (!gp.grid.rendered) {
32048                             gp.grid.render();
32049                         }
32050                     });
32051                 } else {
32052                     grid.render();
32053                 }
32054                 break;
32055            
32056            
32057            
32058                 
32059                 
32060                 
32061             default: 
32062                 alert("Can not add '" + cfg.xtype + "' to BorderLayout");
32063                 return null;
32064              // GridPanel (grid, cfg)
32065             
32066         }
32067         this.beginUpdate();
32068         // add children..
32069         var region = '';
32070         var abn = {};
32071         Roo.each(xitems, function(i)  {
32072             region = nb && i.region ? i.region : false;
32073             
32074             var add = ret.addxtype(i);
32075            
32076             if (region) {
32077                 nb[region] = nb[region] == undefined ? 0 : nb[region]+1;
32078                 if (!i.background) {
32079                     abn[region] = nb[region] ;
32080                 }
32081             }
32082             
32083         });
32084         this.endUpdate();
32085
32086         // make the last non-background panel active..
32087         //if (nb) { Roo.log(abn); }
32088         if (nb) {
32089             
32090             for(var r in abn) {
32091                 region = this.getRegion(r);
32092                 if (region) {
32093                     // tried using nb[r], but it does not work..
32094                      
32095                     region.showPanel(abn[r]);
32096                    
32097                 }
32098             }
32099         }
32100         return ret;
32101         
32102     }
32103 });
32104
32105 /**
32106  * Shortcut for creating a new BorderLayout object and adding one or more ContentPanels to it in a single step, handling
32107  * the beginUpdate and endUpdate calls internally.  The key to this method is the <b>panels</b> property that can be
32108  * provided with each region config, which allows you to add ContentPanel configs in addition to the region configs
32109  * during creation.  The following code is equivalent to the constructor-based example at the beginning of this class:
32110  * <pre><code>
32111 // shorthand
32112 var CP = Roo.ContentPanel;
32113
32114 var layout = Roo.BorderLayout.create({
32115     north: {
32116         initialSize: 25,
32117         titlebar: false,
32118         panels: [new CP("north", "North")]
32119     },
32120     west: {
32121         split:true,
32122         initialSize: 200,
32123         minSize: 175,
32124         maxSize: 400,
32125         titlebar: true,
32126         collapsible: true,
32127         panels: [new CP("west", {title: "West"})]
32128     },
32129     east: {
32130         split:true,
32131         initialSize: 202,
32132         minSize: 175,
32133         maxSize: 400,
32134         titlebar: true,
32135         collapsible: true,
32136         panels: [new CP("autoTabs", {title: "Auto Tabs", closable: true})]
32137     },
32138     south: {
32139         split:true,
32140         initialSize: 100,
32141         minSize: 100,
32142         maxSize: 200,
32143         titlebar: true,
32144         collapsible: true,
32145         panels: [new CP("south", {title: "South", closable: true})]
32146     },
32147     center: {
32148         titlebar: true,
32149         autoScroll:true,
32150         resizeTabs: true,
32151         minTabWidth: 50,
32152         preferredTabWidth: 150,
32153         panels: [
32154             new CP("center1", {title: "Close Me", closable: true}),
32155             new CP("center2", {title: "Center Panel", closable: false})
32156         ]
32157     }
32158 }, document.body);
32159
32160 layout.getRegion("center").showPanel("center1");
32161 </code></pre>
32162  * @param config
32163  * @param targetEl
32164  */
32165 Roo.BorderLayout.create = function(config, targetEl){
32166     var layout = new Roo.BorderLayout(targetEl || document.body, config);
32167     layout.beginUpdate();
32168     var regions = Roo.BorderLayout.RegionFactory.validRegions;
32169     for(var j = 0, jlen = regions.length; j < jlen; j++){
32170         var lr = regions[j];
32171         if(layout.regions[lr] && config[lr].panels){
32172             var r = layout.regions[lr];
32173             var ps = config[lr].panels;
32174             layout.addTypedPanels(r, ps);
32175         }
32176     }
32177     layout.endUpdate();
32178     return layout;
32179 };
32180
32181 // private
32182 Roo.BorderLayout.RegionFactory = {
32183     // private
32184     validRegions : ["north","south","east","west","center"],
32185
32186     // private
32187     create : function(target, mgr, config){
32188         target = target.toLowerCase();
32189         if(config.lightweight || config.basic){
32190             return new Roo.BasicLayoutRegion(mgr, config, target);
32191         }
32192         switch(target){
32193             case "north":
32194                 return new Roo.NorthLayoutRegion(mgr, config);
32195             case "south":
32196                 return new Roo.SouthLayoutRegion(mgr, config);
32197             case "east":
32198                 return new Roo.EastLayoutRegion(mgr, config);
32199             case "west":
32200                 return new Roo.WestLayoutRegion(mgr, config);
32201             case "center":
32202                 return new Roo.CenterLayoutRegion(mgr, config);
32203         }
32204         throw 'Layout region "'+target+'" not supported.';
32205     }
32206 };/*
32207  * Based on:
32208  * Ext JS Library 1.1.1
32209  * Copyright(c) 2006-2007, Ext JS, LLC.
32210  *
32211  * Originally Released Under LGPL - original licence link has changed is not relivant.
32212  *
32213  * Fork - LGPL
32214  * <script type="text/javascript">
32215  */
32216  
32217 /**
32218  * @class Roo.BasicLayoutRegion
32219  * @extends Roo.util.Observable
32220  * This class represents a lightweight region in a layout manager. This region does not move dom nodes
32221  * and does not have a titlebar, tabs or any other features. All it does is size and position 
32222  * panels. To create a BasicLayoutRegion, add lightweight:true or basic:true to your regions config.
32223  */
32224 Roo.BasicLayoutRegion = function(mgr, config, pos, skipConfig){
32225     this.mgr = mgr;
32226     this.position  = pos;
32227     this.events = {
32228         /**
32229          * @scope Roo.BasicLayoutRegion
32230          */
32231         
32232         /**
32233          * @event beforeremove
32234          * Fires before a panel is removed (or closed). To cancel the removal set "e.cancel = true" on the event argument.
32235          * @param {Roo.LayoutRegion} this
32236          * @param {Roo.ContentPanel} panel The panel
32237          * @param {Object} e The cancel event object
32238          */
32239         "beforeremove" : true,
32240         /**
32241          * @event invalidated
32242          * Fires when the layout for this region is changed.
32243          * @param {Roo.LayoutRegion} this
32244          */
32245         "invalidated" : true,
32246         /**
32247          * @event visibilitychange
32248          * Fires when this region is shown or hidden 
32249          * @param {Roo.LayoutRegion} this
32250          * @param {Boolean} visibility true or false
32251          */
32252         "visibilitychange" : true,
32253         /**
32254          * @event paneladded
32255          * Fires when a panel is added. 
32256          * @param {Roo.LayoutRegion} this
32257          * @param {Roo.ContentPanel} panel The panel
32258          */
32259         "paneladded" : true,
32260         /**
32261          * @event panelremoved
32262          * Fires when a panel is removed. 
32263          * @param {Roo.LayoutRegion} this
32264          * @param {Roo.ContentPanel} panel The panel
32265          */
32266         "panelremoved" : true,
32267         /**
32268          * @event collapsed
32269          * Fires when this region is collapsed.
32270          * @param {Roo.LayoutRegion} this
32271          */
32272         "collapsed" : true,
32273         /**
32274          * @event expanded
32275          * Fires when this region is expanded.
32276          * @param {Roo.LayoutRegion} this
32277          */
32278         "expanded" : true,
32279         /**
32280          * @event slideshow
32281          * Fires when this region is slid into view.
32282          * @param {Roo.LayoutRegion} this
32283          */
32284         "slideshow" : true,
32285         /**
32286          * @event slidehide
32287          * Fires when this region slides out of view. 
32288          * @param {Roo.LayoutRegion} this
32289          */
32290         "slidehide" : true,
32291         /**
32292          * @event panelactivated
32293          * Fires when a panel is activated. 
32294          * @param {Roo.LayoutRegion} this
32295          * @param {Roo.ContentPanel} panel The activated panel
32296          */
32297         "panelactivated" : true,
32298         /**
32299          * @event resized
32300          * Fires when the user resizes this region. 
32301          * @param {Roo.LayoutRegion} this
32302          * @param {Number} newSize The new size (width for east/west, height for north/south)
32303          */
32304         "resized" : true
32305     };
32306     /** A collection of panels in this region. @type Roo.util.MixedCollection */
32307     this.panels = new Roo.util.MixedCollection();
32308     this.panels.getKey = this.getPanelId.createDelegate(this);
32309     this.box = null;
32310     this.activePanel = null;
32311     // ensure listeners are added...
32312     
32313     if (config.listeners || config.events) {
32314         Roo.BasicLayoutRegion.superclass.constructor.call(this, {
32315             listeners : config.listeners || {},
32316             events : config.events || {}
32317         });
32318     }
32319     
32320     if(skipConfig !== true){
32321         this.applyConfig(config);
32322     }
32323 };
32324
32325 Roo.extend(Roo.BasicLayoutRegion, Roo.util.Observable, {
32326     getPanelId : function(p){
32327         return p.getId();
32328     },
32329     
32330     applyConfig : function(config){
32331         this.margins = config.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
32332         this.config = config;
32333         
32334     },
32335     
32336     /**
32337      * Resizes the region to the specified size. For vertical regions (west, east) this adjusts 
32338      * the width, for horizontal (north, south) the height.
32339      * @param {Number} newSize The new width or height
32340      */
32341     resizeTo : function(newSize){
32342         var el = this.el ? this.el :
32343                  (this.activePanel ? this.activePanel.getEl() : null);
32344         if(el){
32345             switch(this.position){
32346                 case "east":
32347                 case "west":
32348                     el.setWidth(newSize);
32349                     this.fireEvent("resized", this, newSize);
32350                 break;
32351                 case "north":
32352                 case "south":
32353                     el.setHeight(newSize);
32354                     this.fireEvent("resized", this, newSize);
32355                 break;                
32356             }
32357         }
32358     },
32359     
32360     getBox : function(){
32361         return this.activePanel ? this.activePanel.getEl().getBox(false, true) : null;
32362     },
32363     
32364     getMargins : function(){
32365         return this.margins;
32366     },
32367     
32368     updateBox : function(box){
32369         this.box = box;
32370         var el = this.activePanel.getEl();
32371         el.dom.style.left = box.x + "px";
32372         el.dom.style.top = box.y + "px";
32373         this.activePanel.setSize(box.width, box.height);
32374     },
32375     
32376     /**
32377      * Returns the container element for this region.
32378      * @return {Roo.Element}
32379      */
32380     getEl : function(){
32381         return this.activePanel;
32382     },
32383     
32384     /**
32385      * Returns true if this region is currently visible.
32386      * @return {Boolean}
32387      */
32388     isVisible : function(){
32389         return this.activePanel ? true : false;
32390     },
32391     
32392     setActivePanel : function(panel){
32393         panel = this.getPanel(panel);
32394         if(this.activePanel && this.activePanel != panel){
32395             this.activePanel.setActiveState(false);
32396             this.activePanel.getEl().setLeftTop(-10000,-10000);
32397         }
32398         this.activePanel = panel;
32399         panel.setActiveState(true);
32400         if(this.box){
32401             panel.setSize(this.box.width, this.box.height);
32402         }
32403         this.fireEvent("panelactivated", this, panel);
32404         this.fireEvent("invalidated");
32405     },
32406     
32407     /**
32408      * Show the specified panel.
32409      * @param {Number/String/ContentPanel} panelId The panels index, id or the panel itself
32410      * @return {Roo.ContentPanel} The shown panel or null
32411      */
32412     showPanel : function(panel){
32413         if(panel = this.getPanel(panel)){
32414             this.setActivePanel(panel);
32415         }
32416         return panel;
32417     },
32418     
32419     /**
32420      * Get the active panel for this region.
32421      * @return {Roo.ContentPanel} The active panel or null
32422      */
32423     getActivePanel : function(){
32424         return this.activePanel;
32425     },
32426     
32427     /**
32428      * Add the passed ContentPanel(s)
32429      * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
32430      * @return {Roo.ContentPanel} The panel added (if only one was added)
32431      */
32432     add : function(panel){
32433         if(arguments.length > 1){
32434             for(var i = 0, len = arguments.length; i < len; i++) {
32435                 this.add(arguments[i]);
32436             }
32437             return null;
32438         }
32439         if(this.hasPanel(panel)){
32440             this.showPanel(panel);
32441             return panel;
32442         }
32443         var el = panel.getEl();
32444         if(el.dom.parentNode != this.mgr.el.dom){
32445             this.mgr.el.dom.appendChild(el.dom);
32446         }
32447         if(panel.setRegion){
32448             panel.setRegion(this);
32449         }
32450         this.panels.add(panel);
32451         el.setStyle("position", "absolute");
32452         if(!panel.background){
32453             this.setActivePanel(panel);
32454             if(this.config.initialSize && this.panels.getCount()==1){
32455                 this.resizeTo(this.config.initialSize);
32456             }
32457         }
32458         this.fireEvent("paneladded", this, panel);
32459         return panel;
32460     },
32461     
32462     /**
32463      * Returns true if the panel is in this region.
32464      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
32465      * @return {Boolean}
32466      */
32467     hasPanel : function(panel){
32468         if(typeof panel == "object"){ // must be panel obj
32469             panel = panel.getId();
32470         }
32471         return this.getPanel(panel) ? true : false;
32472     },
32473     
32474     /**
32475      * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
32476      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
32477      * @param {Boolean} preservePanel Overrides the config preservePanel option
32478      * @return {Roo.ContentPanel} The panel that was removed
32479      */
32480     remove : function(panel, preservePanel){
32481         panel = this.getPanel(panel);
32482         if(!panel){
32483             return null;
32484         }
32485         var e = {};
32486         this.fireEvent("beforeremove", this, panel, e);
32487         if(e.cancel === true){
32488             return null;
32489         }
32490         var panelId = panel.getId();
32491         this.panels.removeKey(panelId);
32492         return panel;
32493     },
32494     
32495     /**
32496      * Returns the panel specified or null if it's not in this region.
32497      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
32498      * @return {Roo.ContentPanel}
32499      */
32500     getPanel : function(id){
32501         if(typeof id == "object"){ // must be panel obj
32502             return id;
32503         }
32504         return this.panels.get(id);
32505     },
32506     
32507     /**
32508      * Returns this regions position (north/south/east/west/center).
32509      * @return {String} 
32510      */
32511     getPosition: function(){
32512         return this.position;    
32513     }
32514 });/*
32515  * Based on:
32516  * Ext JS Library 1.1.1
32517  * Copyright(c) 2006-2007, Ext JS, LLC.
32518  *
32519  * Originally Released Under LGPL - original licence link has changed is not relivant.
32520  *
32521  * Fork - LGPL
32522  * <script type="text/javascript">
32523  */
32524  
32525 /**
32526  * @class Roo.LayoutRegion
32527  * @extends Roo.BasicLayoutRegion
32528  * This class represents a region in a layout manager.
32529  * @cfg {Boolean}   collapsible     False to disable collapsing (defaults to true)
32530  * @cfg {Boolean}   collapsed       True to set the initial display to collapsed (defaults to false)
32531  * @cfg {Boolean}   floatable       False to disable floating (defaults to true)
32532  * @cfg {Object}    margins         Margins for the element (defaults to {top: 0, left: 0, right:0, bottom: 0})
32533  * @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})
32534  * @cfg {String}    tabPosition     "top" or "bottom" (defaults to "bottom")
32535  * @cfg {String}    collapsedTitle  Optional string message to display in the collapsed block of a north or south region
32536  * @cfg {Boolean}   alwaysShowTabs  True to always display tabs even when there is only 1 panel (defaults to false)
32537  * @cfg {Boolean}   autoScroll      True to enable overflow scrolling (defaults to false)
32538  * @cfg {Boolean}   titlebar        True to display a title bar (defaults to true)
32539  * @cfg {String}    title           The title for the region (overrides panel titles)
32540  * @cfg {Boolean}   animate         True to animate expand/collapse (defaults to false)
32541  * @cfg {Boolean}   autoHide        False to disable auto hiding when the mouse leaves the "floated" region (defaults to true)
32542  * @cfg {Boolean}   preservePanels  True to preserve removed panels so they can be readded later (defaults to false)
32543  * @cfg {Boolean}   closeOnTab      True to place the close icon on the tabs instead of the region titlebar (defaults to false)
32544  * @cfg {Boolean}   hideTabs        True to hide the tab strip (defaults to false)
32545  * @cfg {Boolean}   resizeTabs      True to enable automatic tab resizing. This will resize the tabs so they are all the same size and fit within
32546  *                      the space available, similar to FireFox 1.5 tabs (defaults to false)
32547  * @cfg {Number}    minTabWidth     The minimum tab width (defaults to 40)
32548  * @cfg {Number}    preferredTabWidth The preferred tab width (defaults to 150)
32549  * @cfg {Boolean}   showPin         True to show a pin button
32550  * @cfg {Boolean}   hidden          True to start the region hidden (defaults to false)
32551  * @cfg {Boolean}   hideWhenEmpty   True to hide the region when it has no panels
32552  * @cfg {Boolean}   disableTabTips  True to disable tab tooltips
32553  * @cfg {Number}    width           For East/West panels
32554  * @cfg {Number}    height          For North/South panels
32555  * @cfg {Boolean}   split           To show the splitter
32556  * @cfg {Boolean}   toolbar         xtype configuration for a toolbar - shows on right of tabbar
32557  */
32558 Roo.LayoutRegion = function(mgr, config, pos){
32559     Roo.LayoutRegion.superclass.constructor.call(this, mgr, config, pos, true);
32560     var dh = Roo.DomHelper;
32561     /** This region's container element 
32562     * @type Roo.Element */
32563     this.el = dh.append(mgr.el.dom, {tag: "div", cls: "x-layout-panel x-layout-panel-" + this.position}, true);
32564     /** This region's title element 
32565     * @type Roo.Element */
32566
32567     this.titleEl = dh.append(this.el.dom, {tag: "div", unselectable: "on", cls: "x-unselectable x-layout-panel-hd x-layout-title-"+this.position, children:[
32568         {tag: "span", cls: "x-unselectable x-layout-panel-hd-text", unselectable: "on", html: "&#160;"},
32569         {tag: "div", cls: "x-unselectable x-layout-panel-hd-tools", unselectable: "on"}
32570     ]}, true);
32571     this.titleEl.enableDisplayMode();
32572     /** This region's title text element 
32573     * @type HTMLElement */
32574     this.titleTextEl = this.titleEl.dom.firstChild;
32575     this.tools = Roo.get(this.titleEl.dom.childNodes[1], true);
32576     this.closeBtn = this.createTool(this.tools.dom, "x-layout-close");
32577     this.closeBtn.enableDisplayMode();
32578     this.closeBtn.on("click", this.closeClicked, this);
32579     this.closeBtn.hide();
32580
32581     this.createBody(config);
32582     this.visible = true;
32583     this.collapsed = false;
32584
32585     if(config.hideWhenEmpty){
32586         this.hide();
32587         this.on("paneladded", this.validateVisibility, this);
32588         this.on("panelremoved", this.validateVisibility, this);
32589     }
32590     this.applyConfig(config);
32591 };
32592
32593 Roo.extend(Roo.LayoutRegion, Roo.BasicLayoutRegion, {
32594
32595     createBody : function(){
32596         /** This region's body element 
32597         * @type Roo.Element */
32598         this.bodyEl = this.el.createChild({tag: "div", cls: "x-layout-panel-body"});
32599     },
32600
32601     applyConfig : function(c){
32602         if(c.collapsible && this.position != "center" && !this.collapsedEl){
32603             var dh = Roo.DomHelper;
32604             if(c.titlebar !== false){
32605                 this.collapseBtn = this.createTool(this.tools.dom, "x-layout-collapse-"+this.position);
32606                 this.collapseBtn.on("click", this.collapse, this);
32607                 this.collapseBtn.enableDisplayMode();
32608
32609                 if(c.showPin === true || this.showPin){
32610                     this.stickBtn = this.createTool(this.tools.dom, "x-layout-stick");
32611                     this.stickBtn.enableDisplayMode();
32612                     this.stickBtn.on("click", this.expand, this);
32613                     this.stickBtn.hide();
32614                 }
32615             }
32616             /** This region's collapsed element
32617             * @type Roo.Element */
32618             this.collapsedEl = dh.append(this.mgr.el.dom, {cls: "x-layout-collapsed x-layout-collapsed-"+this.position, children:[
32619                 {cls: "x-layout-collapsed-tools", children:[{cls: "x-layout-ctools-inner"}]}
32620             ]}, true);
32621             if(c.floatable !== false){
32622                this.collapsedEl.addClassOnOver("x-layout-collapsed-over");
32623                this.collapsedEl.on("click", this.collapseClick, this);
32624             }
32625
32626             if(c.collapsedTitle && (this.position == "north" || this.position== "south")) {
32627                 this.collapsedTitleTextEl = dh.append(this.collapsedEl.dom, {tag: "div", cls: "x-unselectable x-layout-panel-hd-text",
32628                    id: "message", unselectable: "on", style:{"float":"left"}});
32629                this.collapsedTitleTextEl.innerHTML = c.collapsedTitle;
32630              }
32631             this.expandBtn = this.createTool(this.collapsedEl.dom.firstChild.firstChild, "x-layout-expand-"+this.position);
32632             this.expandBtn.on("click", this.expand, this);
32633         }
32634         if(this.collapseBtn){
32635             this.collapseBtn.setVisible(c.collapsible == true);
32636         }
32637         this.cmargins = c.cmargins || this.cmargins ||
32638                          (this.position == "west" || this.position == "east" ?
32639                              {top: 0, left: 2, right:2, bottom: 0} :
32640                              {top: 2, left: 0, right:0, bottom: 2});
32641         this.margins = c.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
32642         this.bottomTabs = c.tabPosition != "top";
32643         this.autoScroll = c.autoScroll || false;
32644         if(this.autoScroll){
32645             this.bodyEl.setStyle("overflow", "auto");
32646         }else{
32647             this.bodyEl.setStyle("overflow", "hidden");
32648         }
32649         //if(c.titlebar !== false){
32650             if((!c.titlebar && !c.title) || c.titlebar === false){
32651                 this.titleEl.hide();
32652             }else{
32653                 this.titleEl.show();
32654                 if(c.title){
32655                     this.titleTextEl.innerHTML = c.title;
32656                 }
32657             }
32658         //}
32659         this.duration = c.duration || .30;
32660         this.slideDuration = c.slideDuration || .45;
32661         this.config = c;
32662         if(c.collapsed){
32663             this.collapse(true);
32664         }
32665         if(c.hidden){
32666             this.hide();
32667         }
32668     },
32669     /**
32670      * Returns true if this region is currently visible.
32671      * @return {Boolean}
32672      */
32673     isVisible : function(){
32674         return this.visible;
32675     },
32676
32677     /**
32678      * Updates the title for collapsed north/south regions (used with {@link #collapsedTitle} config option)
32679      * @param {String} title (optional) The title text (accepts HTML markup, defaults to the numeric character reference for a non-breaking space, "&amp;#160;")
32680      */
32681     setCollapsedTitle : function(title){
32682         title = title || "&#160;";
32683         if(this.collapsedTitleTextEl){
32684             this.collapsedTitleTextEl.innerHTML = title;
32685         }
32686     },
32687
32688     getBox : function(){
32689         var b;
32690         if(!this.collapsed){
32691             b = this.el.getBox(false, true);
32692         }else{
32693             b = this.collapsedEl.getBox(false, true);
32694         }
32695         return b;
32696     },
32697
32698     getMargins : function(){
32699         return this.collapsed ? this.cmargins : this.margins;
32700     },
32701
32702     highlight : function(){
32703         this.el.addClass("x-layout-panel-dragover");
32704     },
32705
32706     unhighlight : function(){
32707         this.el.removeClass("x-layout-panel-dragover");
32708     },
32709
32710     updateBox : function(box){
32711         this.box = box;
32712         if(!this.collapsed){
32713             this.el.dom.style.left = box.x + "px";
32714             this.el.dom.style.top = box.y + "px";
32715             this.updateBody(box.width, box.height);
32716         }else{
32717             this.collapsedEl.dom.style.left = box.x + "px";
32718             this.collapsedEl.dom.style.top = box.y + "px";
32719             this.collapsedEl.setSize(box.width, box.height);
32720         }
32721         if(this.tabs){
32722             this.tabs.autoSizeTabs();
32723         }
32724     },
32725
32726     updateBody : function(w, h){
32727         if(w !== null){
32728             this.el.setWidth(w);
32729             w -= this.el.getBorderWidth("rl");
32730             if(this.config.adjustments){
32731                 w += this.config.adjustments[0];
32732             }
32733         }
32734         if(h !== null){
32735             this.el.setHeight(h);
32736             h = this.titleEl && this.titleEl.isDisplayed() ? h - (this.titleEl.getHeight()||0) : h;
32737             h -= this.el.getBorderWidth("tb");
32738             if(this.config.adjustments){
32739                 h += this.config.adjustments[1];
32740             }
32741             this.bodyEl.setHeight(h);
32742             if(this.tabs){
32743                 h = this.tabs.syncHeight(h);
32744             }
32745         }
32746         if(this.panelSize){
32747             w = w !== null ? w : this.panelSize.width;
32748             h = h !== null ? h : this.panelSize.height;
32749         }
32750         if(this.activePanel){
32751             var el = this.activePanel.getEl();
32752             w = w !== null ? w : el.getWidth();
32753             h = h !== null ? h : el.getHeight();
32754             this.panelSize = {width: w, height: h};
32755             this.activePanel.setSize(w, h);
32756         }
32757         if(Roo.isIE && this.tabs){
32758             this.tabs.el.repaint();
32759         }
32760     },
32761
32762     /**
32763      * Returns the container element for this region.
32764      * @return {Roo.Element}
32765      */
32766     getEl : function(){
32767         return this.el;
32768     },
32769
32770     /**
32771      * Hides this region.
32772      */
32773     hide : function(){
32774         if(!this.collapsed){
32775             this.el.dom.style.left = "-2000px";
32776             this.el.hide();
32777         }else{
32778             this.collapsedEl.dom.style.left = "-2000px";
32779             this.collapsedEl.hide();
32780         }
32781         this.visible = false;
32782         this.fireEvent("visibilitychange", this, false);
32783     },
32784
32785     /**
32786      * Shows this region if it was previously hidden.
32787      */
32788     show : function(){
32789         if(!this.collapsed){
32790             this.el.show();
32791         }else{
32792             this.collapsedEl.show();
32793         }
32794         this.visible = true;
32795         this.fireEvent("visibilitychange", this, true);
32796     },
32797
32798     closeClicked : function(){
32799         if(this.activePanel){
32800             this.remove(this.activePanel);
32801         }
32802     },
32803
32804     collapseClick : function(e){
32805         if(this.isSlid){
32806            e.stopPropagation();
32807            this.slideIn();
32808         }else{
32809            e.stopPropagation();
32810            this.slideOut();
32811         }
32812     },
32813
32814     /**
32815      * Collapses this region.
32816      * @param {Boolean} skipAnim (optional) true to collapse the element without animation (if animate is true)
32817      */
32818     collapse : function(skipAnim){
32819         if(this.collapsed) return;
32820         this.collapsed = true;
32821         if(this.split){
32822             this.split.el.hide();
32823         }
32824         if(this.config.animate && skipAnim !== true){
32825             this.fireEvent("invalidated", this);
32826             this.animateCollapse();
32827         }else{
32828             this.el.setLocation(-20000,-20000);
32829             this.el.hide();
32830             this.collapsedEl.show();
32831             this.fireEvent("collapsed", this);
32832             this.fireEvent("invalidated", this);
32833         }
32834     },
32835
32836     animateCollapse : function(){
32837         // overridden
32838     },
32839
32840     /**
32841      * Expands this region if it was previously collapsed.
32842      * @param {Roo.EventObject} e The event that triggered the expand (or null if calling manually)
32843      * @param {Boolean} skipAnim (optional) true to expand the element without animation (if animate is true)
32844      */
32845     expand : function(e, skipAnim){
32846         if(e) e.stopPropagation();
32847         if(!this.collapsed || this.el.hasActiveFx()) return;
32848         if(this.isSlid){
32849             this.afterSlideIn();
32850             skipAnim = true;
32851         }
32852         this.collapsed = false;
32853         if(this.config.animate && skipAnim !== true){
32854             this.animateExpand();
32855         }else{
32856             this.el.show();
32857             if(this.split){
32858                 this.split.el.show();
32859             }
32860             this.collapsedEl.setLocation(-2000,-2000);
32861             this.collapsedEl.hide();
32862             this.fireEvent("invalidated", this);
32863             this.fireEvent("expanded", this);
32864         }
32865     },
32866
32867     animateExpand : function(){
32868         // overridden
32869     },
32870
32871     initTabs : function()
32872     {
32873         this.bodyEl.setStyle("overflow", "hidden");
32874         var ts = new Roo.TabPanel(
32875                 this.bodyEl.dom,
32876                 {
32877                     tabPosition: this.bottomTabs ? 'bottom' : 'top',
32878                     disableTooltips: this.config.disableTabTips,
32879                     toolbar : this.config.toolbar
32880                 }
32881         );
32882         if(this.config.hideTabs){
32883             ts.stripWrap.setDisplayed(false);
32884         }
32885         this.tabs = ts;
32886         ts.resizeTabs = this.config.resizeTabs === true;
32887         ts.minTabWidth = this.config.minTabWidth || 40;
32888         ts.maxTabWidth = this.config.maxTabWidth || 250;
32889         ts.preferredTabWidth = this.config.preferredTabWidth || 150;
32890         ts.monitorResize = false;
32891         ts.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden");
32892         ts.bodyEl.addClass('x-layout-tabs-body');
32893         this.panels.each(this.initPanelAsTab, this);
32894     },
32895
32896     initPanelAsTab : function(panel){
32897         var ti = this.tabs.addTab(panel.getEl().id, panel.getTitle(), null,
32898                     this.config.closeOnTab && panel.isClosable());
32899         if(panel.tabTip !== undefined){
32900             ti.setTooltip(panel.tabTip);
32901         }
32902         ti.on("activate", function(){
32903               this.setActivePanel(panel);
32904         }, this);
32905         if(this.config.closeOnTab){
32906             ti.on("beforeclose", function(t, e){
32907                 e.cancel = true;
32908                 this.remove(panel);
32909             }, this);
32910         }
32911         return ti;
32912     },
32913
32914     updatePanelTitle : function(panel, title){
32915         if(this.activePanel == panel){
32916             this.updateTitle(title);
32917         }
32918         if(this.tabs){
32919             var ti = this.tabs.getTab(panel.getEl().id);
32920             ti.setText(title);
32921             if(panel.tabTip !== undefined){
32922                 ti.setTooltip(panel.tabTip);
32923             }
32924         }
32925     },
32926
32927     updateTitle : function(title){
32928         if(this.titleTextEl && !this.config.title){
32929             this.titleTextEl.innerHTML = (typeof title != "undefined" && title.length > 0 ? title : "&#160;");
32930         }
32931     },
32932
32933     setActivePanel : function(panel){
32934         panel = this.getPanel(panel);
32935         if(this.activePanel && this.activePanel != panel){
32936             this.activePanel.setActiveState(false);
32937         }
32938         this.activePanel = panel;
32939         panel.setActiveState(true);
32940         if(this.panelSize){
32941             panel.setSize(this.panelSize.width, this.panelSize.height);
32942         }
32943         if(this.closeBtn){
32944             this.closeBtn.setVisible(!this.config.closeOnTab && !this.isSlid && panel.isClosable());
32945         }
32946         this.updateTitle(panel.getTitle());
32947         if(this.tabs){
32948             this.fireEvent("invalidated", this);
32949         }
32950         this.fireEvent("panelactivated", this, panel);
32951     },
32952
32953     /**
32954      * Shows the specified panel.
32955      * @param {Number/String/ContentPanel} panelId The panel's index, id or the panel itself
32956      * @return {Roo.ContentPanel} The shown panel, or null if a panel could not be found from panelId
32957      */
32958     showPanel : function(panel){
32959         if(panel = this.getPanel(panel)){
32960             if(this.tabs){
32961                 var tab = this.tabs.getTab(panel.getEl().id);
32962                 if(tab.isHidden()){
32963                     this.tabs.unhideTab(tab.id);
32964                 }
32965                 tab.activate();
32966             }else{
32967                 this.setActivePanel(panel);
32968             }
32969         }
32970         return panel;
32971     },
32972
32973     /**
32974      * Get the active panel for this region.
32975      * @return {Roo.ContentPanel} The active panel or null
32976      */
32977     getActivePanel : function(){
32978         return this.activePanel;
32979     },
32980
32981     validateVisibility : function(){
32982         if(this.panels.getCount() < 1){
32983             this.updateTitle("&#160;");
32984             this.closeBtn.hide();
32985             this.hide();
32986         }else{
32987             if(!this.isVisible()){
32988                 this.show();
32989             }
32990         }
32991     },
32992
32993     /**
32994      * Adds the passed ContentPanel(s) to this region.
32995      * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
32996      * @return {Roo.ContentPanel} The panel added (if only one was added; null otherwise)
32997      */
32998     add : function(panel){
32999         if(arguments.length > 1){
33000             for(var i = 0, len = arguments.length; i < len; i++) {
33001                 this.add(arguments[i]);
33002             }
33003             return null;
33004         }
33005         if(this.hasPanel(panel)){
33006             this.showPanel(panel);
33007             return panel;
33008         }
33009         panel.setRegion(this);
33010         this.panels.add(panel);
33011         if(this.panels.getCount() == 1 && !this.config.alwaysShowTabs){
33012             this.bodyEl.dom.appendChild(panel.getEl().dom);
33013             if(panel.background !== true){
33014                 this.setActivePanel(panel);
33015             }
33016             this.fireEvent("paneladded", this, panel);
33017             return panel;
33018         }
33019         if(!this.tabs){
33020             this.initTabs();
33021         }else{
33022             this.initPanelAsTab(panel);
33023         }
33024         if(panel.background !== true){
33025             this.tabs.activate(panel.getEl().id);
33026         }
33027         this.fireEvent("paneladded", this, panel);
33028         return panel;
33029     },
33030
33031     /**
33032      * Hides the tab for the specified panel.
33033      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
33034      */
33035     hidePanel : function(panel){
33036         if(this.tabs && (panel = this.getPanel(panel))){
33037             this.tabs.hideTab(panel.getEl().id);
33038         }
33039     },
33040
33041     /**
33042      * Unhides the tab for a previously hidden panel.
33043      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
33044      */
33045     unhidePanel : function(panel){
33046         if(this.tabs && (panel = this.getPanel(panel))){
33047             this.tabs.unhideTab(panel.getEl().id);
33048         }
33049     },
33050
33051     clearPanels : function(){
33052         while(this.panels.getCount() > 0){
33053              this.remove(this.panels.first());
33054         }
33055     },
33056
33057     /**
33058      * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
33059      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
33060      * @param {Boolean} preservePanel Overrides the config preservePanel option
33061      * @return {Roo.ContentPanel} The panel that was removed
33062      */
33063     remove : function(panel, preservePanel){
33064         panel = this.getPanel(panel);
33065         if(!panel){
33066             return null;
33067         }
33068         var e = {};
33069         this.fireEvent("beforeremove", this, panel, e);
33070         if(e.cancel === true){
33071             return null;
33072         }
33073         preservePanel = (typeof preservePanel != "undefined" ? preservePanel : (this.config.preservePanels === true || panel.preserve === true));
33074         var panelId = panel.getId();
33075         this.panels.removeKey(panelId);
33076         if(preservePanel){
33077             document.body.appendChild(panel.getEl().dom);
33078         }
33079         if(this.tabs){
33080             this.tabs.removeTab(panel.getEl().id);
33081         }else if (!preservePanel){
33082             this.bodyEl.dom.removeChild(panel.getEl().dom);
33083         }
33084         if(this.panels.getCount() == 1 && this.tabs && !this.config.alwaysShowTabs){
33085             var p = this.panels.first();
33086             var tempEl = document.createElement("div"); // temp holder to keep IE from deleting the node
33087             tempEl.appendChild(p.getEl().dom);
33088             this.bodyEl.update("");
33089             this.bodyEl.dom.appendChild(p.getEl().dom);
33090             tempEl = null;
33091             this.updateTitle(p.getTitle());
33092             this.tabs = null;
33093             this.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden");
33094             this.setActivePanel(p);
33095         }
33096         panel.setRegion(null);
33097         if(this.activePanel == panel){
33098             this.activePanel = null;
33099         }
33100         if(this.config.autoDestroy !== false && preservePanel !== true){
33101             try{panel.destroy();}catch(e){}
33102         }
33103         this.fireEvent("panelremoved", this, panel);
33104         return panel;
33105     },
33106
33107     /**
33108      * Returns the TabPanel component used by this region
33109      * @return {Roo.TabPanel}
33110      */
33111     getTabs : function(){
33112         return this.tabs;
33113     },
33114
33115     createTool : function(parentEl, className){
33116         var btn = Roo.DomHelper.append(parentEl, {tag: "div", cls: "x-layout-tools-button",
33117             children: [{tag: "div", cls: "x-layout-tools-button-inner " + className, html: "&#160;"}]}, true);
33118         btn.addClassOnOver("x-layout-tools-button-over");
33119         return btn;
33120     }
33121 });/*
33122  * Based on:
33123  * Ext JS Library 1.1.1
33124  * Copyright(c) 2006-2007, Ext JS, LLC.
33125  *
33126  * Originally Released Under LGPL - original licence link has changed is not relivant.
33127  *
33128  * Fork - LGPL
33129  * <script type="text/javascript">
33130  */
33131  
33132
33133
33134 /**
33135  * @class Roo.SplitLayoutRegion
33136  * @extends Roo.LayoutRegion
33137  * Adds a splitbar and other (private) useful functionality to a {@link Roo.LayoutRegion}.
33138  */
33139 Roo.SplitLayoutRegion = function(mgr, config, pos, cursor){
33140     this.cursor = cursor;
33141     Roo.SplitLayoutRegion.superclass.constructor.call(this, mgr, config, pos);
33142 };
33143
33144 Roo.extend(Roo.SplitLayoutRegion, Roo.LayoutRegion, {
33145     splitTip : "Drag to resize.",
33146     collapsibleSplitTip : "Drag to resize. Double click to hide.",
33147     useSplitTips : false,
33148
33149     applyConfig : function(config){
33150         Roo.SplitLayoutRegion.superclass.applyConfig.call(this, config);
33151         if(config.split){
33152             if(!this.split){
33153                 var splitEl = Roo.DomHelper.append(this.mgr.el.dom, 
33154                         {tag: "div", id: this.el.id + "-split", cls: "x-layout-split x-layout-split-"+this.position, html: "&#160;"});
33155                 /** The SplitBar for this region 
33156                 * @type Roo.SplitBar */
33157                 this.split = new Roo.SplitBar(splitEl, this.el, this.orientation);
33158                 this.split.on("moved", this.onSplitMove, this);
33159                 this.split.useShim = config.useShim === true;
33160                 this.split.getMaximumSize = this[this.position == 'north' || this.position == 'south' ? 'getVMaxSize' : 'getHMaxSize'].createDelegate(this);
33161                 if(this.useSplitTips){
33162                     this.split.el.dom.title = config.collapsible ? this.collapsibleSplitTip : this.splitTip;
33163                 }
33164                 if(config.collapsible){
33165                     this.split.el.on("dblclick", this.collapse,  this);
33166                 }
33167             }
33168             if(typeof config.minSize != "undefined"){
33169                 this.split.minSize = config.minSize;
33170             }
33171             if(typeof config.maxSize != "undefined"){
33172                 this.split.maxSize = config.maxSize;
33173             }
33174             if(config.hideWhenEmpty || config.hidden || config.collapsed){
33175                 this.hideSplitter();
33176             }
33177         }
33178     },
33179
33180     getHMaxSize : function(){
33181          var cmax = this.config.maxSize || 10000;
33182          var center = this.mgr.getRegion("center");
33183          return Math.min(cmax, (this.el.getWidth()+center.getEl().getWidth())-center.getMinWidth());
33184     },
33185
33186     getVMaxSize : function(){
33187          var cmax = this.config.maxSize || 10000;
33188          var center = this.mgr.getRegion("center");
33189          return Math.min(cmax, (this.el.getHeight()+center.getEl().getHeight())-center.getMinHeight());
33190     },
33191
33192     onSplitMove : function(split, newSize){
33193         this.fireEvent("resized", this, newSize);
33194     },
33195     
33196     /** 
33197      * Returns the {@link Roo.SplitBar} for this region.
33198      * @return {Roo.SplitBar}
33199      */
33200     getSplitBar : function(){
33201         return this.split;
33202     },
33203     
33204     hide : function(){
33205         this.hideSplitter();
33206         Roo.SplitLayoutRegion.superclass.hide.call(this);
33207     },
33208
33209     hideSplitter : function(){
33210         if(this.split){
33211             this.split.el.setLocation(-2000,-2000);
33212             this.split.el.hide();
33213         }
33214     },
33215
33216     show : function(){
33217         if(this.split){
33218             this.split.el.show();
33219         }
33220         Roo.SplitLayoutRegion.superclass.show.call(this);
33221     },
33222     
33223     beforeSlide: function(){
33224         if(Roo.isGecko){// firefox overflow auto bug workaround
33225             this.bodyEl.clip();
33226             if(this.tabs) this.tabs.bodyEl.clip();
33227             if(this.activePanel){
33228                 this.activePanel.getEl().clip();
33229                 
33230                 if(this.activePanel.beforeSlide){
33231                     this.activePanel.beforeSlide();
33232                 }
33233             }
33234         }
33235     },
33236     
33237     afterSlide : function(){
33238         if(Roo.isGecko){// firefox overflow auto bug workaround
33239             this.bodyEl.unclip();
33240             if(this.tabs) this.tabs.bodyEl.unclip();
33241             if(this.activePanel){
33242                 this.activePanel.getEl().unclip();
33243                 if(this.activePanel.afterSlide){
33244                     this.activePanel.afterSlide();
33245                 }
33246             }
33247         }
33248     },
33249
33250     initAutoHide : function(){
33251         if(this.autoHide !== false){
33252             if(!this.autoHideHd){
33253                 var st = new Roo.util.DelayedTask(this.slideIn, this);
33254                 this.autoHideHd = {
33255                     "mouseout": function(e){
33256                         if(!e.within(this.el, true)){
33257                             st.delay(500);
33258                         }
33259                     },
33260                     "mouseover" : function(e){
33261                         st.cancel();
33262                     },
33263                     scope : this
33264                 };
33265             }
33266             this.el.on(this.autoHideHd);
33267         }
33268     },
33269
33270     clearAutoHide : function(){
33271         if(this.autoHide !== false){
33272             this.el.un("mouseout", this.autoHideHd.mouseout);
33273             this.el.un("mouseover", this.autoHideHd.mouseover);
33274         }
33275     },
33276
33277     clearMonitor : function(){
33278         Roo.get(document).un("click", this.slideInIf, this);
33279     },
33280
33281     // these names are backwards but not changed for compat
33282     slideOut : function(){
33283         if(this.isSlid || this.el.hasActiveFx()){
33284             return;
33285         }
33286         this.isSlid = true;
33287         if(this.collapseBtn){
33288             this.collapseBtn.hide();
33289         }
33290         this.closeBtnState = this.closeBtn.getStyle('display');
33291         this.closeBtn.hide();
33292         if(this.stickBtn){
33293             this.stickBtn.show();
33294         }
33295         this.el.show();
33296         this.el.alignTo(this.collapsedEl, this.getCollapseAnchor());
33297         this.beforeSlide();
33298         this.el.setStyle("z-index", 10001);
33299         this.el.slideIn(this.getSlideAnchor(), {
33300             callback: function(){
33301                 this.afterSlide();
33302                 this.initAutoHide();
33303                 Roo.get(document).on("click", this.slideInIf, this);
33304                 this.fireEvent("slideshow", this);
33305             },
33306             scope: this,
33307             block: true
33308         });
33309     },
33310
33311     afterSlideIn : function(){
33312         this.clearAutoHide();
33313         this.isSlid = false;
33314         this.clearMonitor();
33315         this.el.setStyle("z-index", "");
33316         if(this.collapseBtn){
33317             this.collapseBtn.show();
33318         }
33319         this.closeBtn.setStyle('display', this.closeBtnState);
33320         if(this.stickBtn){
33321             this.stickBtn.hide();
33322         }
33323         this.fireEvent("slidehide", this);
33324     },
33325
33326     slideIn : function(cb){
33327         if(!this.isSlid || this.el.hasActiveFx()){
33328             Roo.callback(cb);
33329             return;
33330         }
33331         this.isSlid = false;
33332         this.beforeSlide();
33333         this.el.slideOut(this.getSlideAnchor(), {
33334             callback: function(){
33335                 this.el.setLeftTop(-10000, -10000);
33336                 this.afterSlide();
33337                 this.afterSlideIn();
33338                 Roo.callback(cb);
33339             },
33340             scope: this,
33341             block: true
33342         });
33343     },
33344     
33345     slideInIf : function(e){
33346         if(!e.within(this.el)){
33347             this.slideIn();
33348         }
33349     },
33350
33351     animateCollapse : function(){
33352         this.beforeSlide();
33353         this.el.setStyle("z-index", 20000);
33354         var anchor = this.getSlideAnchor();
33355         this.el.slideOut(anchor, {
33356             callback : function(){
33357                 this.el.setStyle("z-index", "");
33358                 this.collapsedEl.slideIn(anchor, {duration:.3});
33359                 this.afterSlide();
33360                 this.el.setLocation(-10000,-10000);
33361                 this.el.hide();
33362                 this.fireEvent("collapsed", this);
33363             },
33364             scope: this,
33365             block: true
33366         });
33367     },
33368
33369     animateExpand : function(){
33370         this.beforeSlide();
33371         this.el.alignTo(this.collapsedEl, this.getCollapseAnchor(), this.getExpandAdj());
33372         this.el.setStyle("z-index", 20000);
33373         this.collapsedEl.hide({
33374             duration:.1
33375         });
33376         this.el.slideIn(this.getSlideAnchor(), {
33377             callback : function(){
33378                 this.el.setStyle("z-index", "");
33379                 this.afterSlide();
33380                 if(this.split){
33381                     this.split.el.show();
33382                 }
33383                 this.fireEvent("invalidated", this);
33384                 this.fireEvent("expanded", this);
33385             },
33386             scope: this,
33387             block: true
33388         });
33389     },
33390
33391     anchors : {
33392         "west" : "left",
33393         "east" : "right",
33394         "north" : "top",
33395         "south" : "bottom"
33396     },
33397
33398     sanchors : {
33399         "west" : "l",
33400         "east" : "r",
33401         "north" : "t",
33402         "south" : "b"
33403     },
33404
33405     canchors : {
33406         "west" : "tl-tr",
33407         "east" : "tr-tl",
33408         "north" : "tl-bl",
33409         "south" : "bl-tl"
33410     },
33411
33412     getAnchor : function(){
33413         return this.anchors[this.position];
33414     },
33415
33416     getCollapseAnchor : function(){
33417         return this.canchors[this.position];
33418     },
33419
33420     getSlideAnchor : function(){
33421         return this.sanchors[this.position];
33422     },
33423
33424     getAlignAdj : function(){
33425         var cm = this.cmargins;
33426         switch(this.position){
33427             case "west":
33428                 return [0, 0];
33429             break;
33430             case "east":
33431                 return [0, 0];
33432             break;
33433             case "north":
33434                 return [0, 0];
33435             break;
33436             case "south":
33437                 return [0, 0];
33438             break;
33439         }
33440     },
33441
33442     getExpandAdj : function(){
33443         var c = this.collapsedEl, cm = this.cmargins;
33444         switch(this.position){
33445             case "west":
33446                 return [-(cm.right+c.getWidth()+cm.left), 0];
33447             break;
33448             case "east":
33449                 return [cm.right+c.getWidth()+cm.left, 0];
33450             break;
33451             case "north":
33452                 return [0, -(cm.top+cm.bottom+c.getHeight())];
33453             break;
33454             case "south":
33455                 return [0, cm.top+cm.bottom+c.getHeight()];
33456             break;
33457         }
33458     }
33459 });/*
33460  * Based on:
33461  * Ext JS Library 1.1.1
33462  * Copyright(c) 2006-2007, Ext JS, LLC.
33463  *
33464  * Originally Released Under LGPL - original licence link has changed is not relivant.
33465  *
33466  * Fork - LGPL
33467  * <script type="text/javascript">
33468  */
33469 /*
33470  * These classes are private internal classes
33471  */
33472 Roo.CenterLayoutRegion = function(mgr, config){
33473     Roo.LayoutRegion.call(this, mgr, config, "center");
33474     this.visible = true;
33475     this.minWidth = config.minWidth || 20;
33476     this.minHeight = config.minHeight || 20;
33477 };
33478
33479 Roo.extend(Roo.CenterLayoutRegion, Roo.LayoutRegion, {
33480     hide : function(){
33481         // center panel can't be hidden
33482     },
33483     
33484     show : function(){
33485         // center panel can't be hidden
33486     },
33487     
33488     getMinWidth: function(){
33489         return this.minWidth;
33490     },
33491     
33492     getMinHeight: function(){
33493         return this.minHeight;
33494     }
33495 });
33496
33497
33498 Roo.NorthLayoutRegion = function(mgr, config){
33499     Roo.LayoutRegion.call(this, mgr, config, "north", "n-resize");
33500     if(this.split){
33501         this.split.placement = Roo.SplitBar.TOP;
33502         this.split.orientation = Roo.SplitBar.VERTICAL;
33503         this.split.el.addClass("x-layout-split-v");
33504     }
33505     var size = config.initialSize || config.height;
33506     if(typeof size != "undefined"){
33507         this.el.setHeight(size);
33508     }
33509 };
33510 Roo.extend(Roo.NorthLayoutRegion, Roo.SplitLayoutRegion, {
33511     orientation: Roo.SplitBar.VERTICAL,
33512     getBox : function(){
33513         if(this.collapsed){
33514             return this.collapsedEl.getBox();
33515         }
33516         var box = this.el.getBox();
33517         if(this.split){
33518             box.height += this.split.el.getHeight();
33519         }
33520         return box;
33521     },
33522     
33523     updateBox : function(box){
33524         if(this.split && !this.collapsed){
33525             box.height -= this.split.el.getHeight();
33526             this.split.el.setLeft(box.x);
33527             this.split.el.setTop(box.y+box.height);
33528             this.split.el.setWidth(box.width);
33529         }
33530         if(this.collapsed){
33531             this.updateBody(box.width, null);
33532         }
33533         Roo.LayoutRegion.prototype.updateBox.call(this, box);
33534     }
33535 });
33536
33537 Roo.SouthLayoutRegion = function(mgr, config){
33538     Roo.SplitLayoutRegion.call(this, mgr, config, "south", "s-resize");
33539     if(this.split){
33540         this.split.placement = Roo.SplitBar.BOTTOM;
33541         this.split.orientation = Roo.SplitBar.VERTICAL;
33542         this.split.el.addClass("x-layout-split-v");
33543     }
33544     var size = config.initialSize || config.height;
33545     if(typeof size != "undefined"){
33546         this.el.setHeight(size);
33547     }
33548 };
33549 Roo.extend(Roo.SouthLayoutRegion, Roo.SplitLayoutRegion, {
33550     orientation: Roo.SplitBar.VERTICAL,
33551     getBox : function(){
33552         if(this.collapsed){
33553             return this.collapsedEl.getBox();
33554         }
33555         var box = this.el.getBox();
33556         if(this.split){
33557             var sh = this.split.el.getHeight();
33558             box.height += sh;
33559             box.y -= sh;
33560         }
33561         return box;
33562     },
33563     
33564     updateBox : function(box){
33565         if(this.split && !this.collapsed){
33566             var sh = this.split.el.getHeight();
33567             box.height -= sh;
33568             box.y += sh;
33569             this.split.el.setLeft(box.x);
33570             this.split.el.setTop(box.y-sh);
33571             this.split.el.setWidth(box.width);
33572         }
33573         if(this.collapsed){
33574             this.updateBody(box.width, null);
33575         }
33576         Roo.LayoutRegion.prototype.updateBox.call(this, box);
33577     }
33578 });
33579
33580 Roo.EastLayoutRegion = function(mgr, config){
33581     Roo.SplitLayoutRegion.call(this, mgr, config, "east", "e-resize");
33582     if(this.split){
33583         this.split.placement = Roo.SplitBar.RIGHT;
33584         this.split.orientation = Roo.SplitBar.HORIZONTAL;
33585         this.split.el.addClass("x-layout-split-h");
33586     }
33587     var size = config.initialSize || config.width;
33588     if(typeof size != "undefined"){
33589         this.el.setWidth(size);
33590     }
33591 };
33592 Roo.extend(Roo.EastLayoutRegion, Roo.SplitLayoutRegion, {
33593     orientation: Roo.SplitBar.HORIZONTAL,
33594     getBox : function(){
33595         if(this.collapsed){
33596             return this.collapsedEl.getBox();
33597         }
33598         var box = this.el.getBox();
33599         if(this.split){
33600             var sw = this.split.el.getWidth();
33601             box.width += sw;
33602             box.x -= sw;
33603         }
33604         return box;
33605     },
33606
33607     updateBox : function(box){
33608         if(this.split && !this.collapsed){
33609             var sw = this.split.el.getWidth();
33610             box.width -= sw;
33611             this.split.el.setLeft(box.x);
33612             this.split.el.setTop(box.y);
33613             this.split.el.setHeight(box.height);
33614             box.x += sw;
33615         }
33616         if(this.collapsed){
33617             this.updateBody(null, box.height);
33618         }
33619         Roo.LayoutRegion.prototype.updateBox.call(this, box);
33620     }
33621 });
33622
33623 Roo.WestLayoutRegion = function(mgr, config){
33624     Roo.SplitLayoutRegion.call(this, mgr, config, "west", "w-resize");
33625     if(this.split){
33626         this.split.placement = Roo.SplitBar.LEFT;
33627         this.split.orientation = Roo.SplitBar.HORIZONTAL;
33628         this.split.el.addClass("x-layout-split-h");
33629     }
33630     var size = config.initialSize || config.width;
33631     if(typeof size != "undefined"){
33632         this.el.setWidth(size);
33633     }
33634 };
33635 Roo.extend(Roo.WestLayoutRegion, Roo.SplitLayoutRegion, {
33636     orientation: Roo.SplitBar.HORIZONTAL,
33637     getBox : function(){
33638         if(this.collapsed){
33639             return this.collapsedEl.getBox();
33640         }
33641         var box = this.el.getBox();
33642         if(this.split){
33643             box.width += this.split.el.getWidth();
33644         }
33645         return box;
33646     },
33647     
33648     updateBox : function(box){
33649         if(this.split && !this.collapsed){
33650             var sw = this.split.el.getWidth();
33651             box.width -= sw;
33652             this.split.el.setLeft(box.x+box.width);
33653             this.split.el.setTop(box.y);
33654             this.split.el.setHeight(box.height);
33655         }
33656         if(this.collapsed){
33657             this.updateBody(null, box.height);
33658         }
33659         Roo.LayoutRegion.prototype.updateBox.call(this, box);
33660     }
33661 });
33662 /*
33663  * Based on:
33664  * Ext JS Library 1.1.1
33665  * Copyright(c) 2006-2007, Ext JS, LLC.
33666  *
33667  * Originally Released Under LGPL - original licence link has changed is not relivant.
33668  *
33669  * Fork - LGPL
33670  * <script type="text/javascript">
33671  */
33672  
33673  
33674 /*
33675  * Private internal class for reading and applying state
33676  */
33677 Roo.LayoutStateManager = function(layout){
33678      // default empty state
33679      this.state = {
33680         north: {},
33681         south: {},
33682         east: {},
33683         west: {}       
33684     };
33685 };
33686
33687 Roo.LayoutStateManager.prototype = {
33688     init : function(layout, provider){
33689         this.provider = provider;
33690         var state = provider.get(layout.id+"-layout-state");
33691         if(state){
33692             var wasUpdating = layout.isUpdating();
33693             if(!wasUpdating){
33694                 layout.beginUpdate();
33695             }
33696             for(var key in state){
33697                 if(typeof state[key] != "function"){
33698                     var rstate = state[key];
33699                     var r = layout.getRegion(key);
33700                     if(r && rstate){
33701                         if(rstate.size){
33702                             r.resizeTo(rstate.size);
33703                         }
33704                         if(rstate.collapsed == true){
33705                             r.collapse(true);
33706                         }else{
33707                             r.expand(null, true);
33708                         }
33709                     }
33710                 }
33711             }
33712             if(!wasUpdating){
33713                 layout.endUpdate();
33714             }
33715             this.state = state; 
33716         }
33717         this.layout = layout;
33718         layout.on("regionresized", this.onRegionResized, this);
33719         layout.on("regioncollapsed", this.onRegionCollapsed, this);
33720         layout.on("regionexpanded", this.onRegionExpanded, this);
33721     },
33722     
33723     storeState : function(){
33724         this.provider.set(this.layout.id+"-layout-state", this.state);
33725     },
33726     
33727     onRegionResized : function(region, newSize){
33728         this.state[region.getPosition()].size = newSize;
33729         this.storeState();
33730     },
33731     
33732     onRegionCollapsed : function(region){
33733         this.state[region.getPosition()].collapsed = true;
33734         this.storeState();
33735     },
33736     
33737     onRegionExpanded : function(region){
33738         this.state[region.getPosition()].collapsed = false;
33739         this.storeState();
33740     }
33741 };/*
33742  * Based on:
33743  * Ext JS Library 1.1.1
33744  * Copyright(c) 2006-2007, Ext JS, LLC.
33745  *
33746  * Originally Released Under LGPL - original licence link has changed is not relivant.
33747  *
33748  * Fork - LGPL
33749  * <script type="text/javascript">
33750  */
33751 /**
33752  * @class Roo.ContentPanel
33753  * @extends Roo.util.Observable
33754  * A basic ContentPanel element.
33755  * @cfg {Boolean}   fitToFrame    True for this panel to adjust its size to fit when the region resizes  (defaults to false)
33756  * @cfg {Boolean}   fitContainer   When using {@link #fitToFrame} and {@link #resizeEl}, you can also fit the parent container  (defaults to false)
33757  * @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
33758  * @cfg {Boolean}   closable      True if the panel can be closed/removed
33759  * @cfg {Boolean}   background    True if the panel should not be activated when it is added (defaults to false)
33760  * @cfg {String/HTMLElement/Element} resizeEl An element to resize if {@link #fitToFrame} is true (instead of this panel's element)
33761  * @cfg {Toolbar}   toolbar       A toolbar for this panel
33762  * @cfg {Boolean} autoScroll    True to scroll overflow in this panel (use with {@link #fitToFrame})
33763  * @cfg {String} title          The title for this panel
33764  * @cfg {Array} adjustments     Values to <b>add</b> to the width/height when doing a {@link #fitToFrame} (default is [0, 0])
33765  * @cfg {String} url            Calls {@link #setUrl} with this value
33766  * @cfg {String} region         (center|north|south|east|west) which region to put this panel on (when used with xtype constructors)
33767  * @cfg {String/Object} params  When used with {@link #url}, calls {@link #setUrl} with this value
33768  * @cfg {Boolean} loadOnce      When used with {@link #url}, calls {@link #setUrl} with this value
33769  * @cfg {String}    content        Raw content to fill content panel with (uses setContent on construction.)
33770
33771  * @constructor
33772  * Create a new ContentPanel.
33773  * @param {String/HTMLElement/Roo.Element} el The container element for this panel
33774  * @param {String/Object} config A string to set only the title or a config object
33775  * @param {String} content (optional) Set the HTML content for this panel
33776  * @param {String} region (optional) Used by xtype constructors to add to regions. (values center,east,west,south,north)
33777  */
33778 Roo.ContentPanel = function(el, config, content){
33779     
33780      
33781     /*
33782     if(el.autoCreate || el.xtype){ // xtype is available if this is called from factory
33783         config = el;
33784         el = Roo.id();
33785     }
33786     if (config && config.parentLayout) { 
33787         el = config.parentLayout.el.createChild(); 
33788     }
33789     */
33790     if(el.autoCreate){ // xtype is available if this is called from factory
33791         config = el;
33792         el = Roo.id();
33793     }
33794     this.el = Roo.get(el);
33795     if(!this.el && config && config.autoCreate){
33796         if(typeof config.autoCreate == "object"){
33797             if(!config.autoCreate.id){
33798                 config.autoCreate.id = config.id||el;
33799             }
33800             this.el = Roo.DomHelper.append(document.body,
33801                         config.autoCreate, true);
33802         }else{
33803             this.el = Roo.DomHelper.append(document.body,
33804                         {tag: "div", cls: "x-layout-inactive-content", id: config.id||el}, true);
33805         }
33806     }
33807     this.closable = false;
33808     this.loaded = false;
33809     this.active = false;
33810     if(typeof config == "string"){
33811         this.title = config;
33812     }else{
33813         Roo.apply(this, config);
33814     }
33815     
33816     if (this.toolbar && !this.toolbar.el && this.toolbar.xtype) {
33817         this.wrapEl = this.el.wrap();
33818         this.toolbar.container = this.el.insertSibling(false, 'before');
33819         this.toolbar = new Roo.Toolbar(this.toolbar);
33820     }
33821     
33822     // xtype created footer. - not sure if will work as we normally have to render first..
33823     if (this.footer && !this.footer.el && this.footer.xtype) {
33824         if (!this.wrapEl) {
33825             this.wrapEl = this.el.wrap();
33826         }
33827     
33828         this.footer.container = this.wrapEl.createChild();
33829          
33830         this.footer = Roo.factory(this.footer, Roo);
33831         
33832     }
33833     
33834     if(this.resizeEl){
33835         this.resizeEl = Roo.get(this.resizeEl, true);
33836     }else{
33837         this.resizeEl = this.el;
33838     }
33839     // handle view.xtype
33840     
33841  
33842     
33843     
33844     this.addEvents({
33845         /**
33846          * @event activate
33847          * Fires when this panel is activated. 
33848          * @param {Roo.ContentPanel} this
33849          */
33850         "activate" : true,
33851         /**
33852          * @event deactivate
33853          * Fires when this panel is activated. 
33854          * @param {Roo.ContentPanel} this
33855          */
33856         "deactivate" : true,
33857
33858         /**
33859          * @event resize
33860          * Fires when this panel is resized if fitToFrame is true.
33861          * @param {Roo.ContentPanel} this
33862          * @param {Number} width The width after any component adjustments
33863          * @param {Number} height The height after any component adjustments
33864          */
33865         "resize" : true,
33866         
33867          /**
33868          * @event render
33869          * Fires when this tab is created
33870          * @param {Roo.ContentPanel} this
33871          */
33872         "render" : true
33873         
33874         
33875         
33876     });
33877     
33878     if (this.view && typeof(this.view.xtype) != 'undefined') {
33879         this.view.el = this.el.appendChild(document.createElement("div"));
33880         this.view = Roo.factory(this.view); 
33881         this.view.render && this.on('render', function() { this.view.render(false, ''); }, this) // render blank..
33882     }
33883     
33884     
33885     
33886     if(this.autoScroll){
33887         this.resizeEl.setStyle("overflow", "auto");
33888     } else {
33889         // fix randome scrolling
33890         this.el.on('scroll', function() {
33891             Roo.log('fix random scolling');
33892             this.scrollTo('top',0); 
33893         });
33894     }
33895     content = content || this.content;
33896     if(content){
33897         this.setContent(content);
33898     }
33899     if(config && config.url){
33900         this.setUrl(this.url, this.params, this.loadOnce);
33901     }
33902     
33903     
33904     
33905     Roo.ContentPanel.superclass.constructor.call(this);
33906     
33907     this.fireEvent('render', this);
33908 };
33909
33910 Roo.extend(Roo.ContentPanel, Roo.util.Observable, {
33911     tabTip:'',
33912     setRegion : function(region){
33913         this.region = region;
33914         if(region){
33915            this.el.replaceClass("x-layout-inactive-content", "x-layout-active-content");
33916         }else{
33917            this.el.replaceClass("x-layout-active-content", "x-layout-inactive-content");
33918         } 
33919     },
33920     
33921     /**
33922      * Returns the toolbar for this Panel if one was configured. 
33923      * @return {Roo.Toolbar} 
33924      */
33925     getToolbar : function(){
33926         return this.toolbar;
33927     },
33928     
33929     setActiveState : function(active){
33930         this.active = active;
33931         if(!active){
33932             this.fireEvent("deactivate", this);
33933         }else{
33934             this.fireEvent("activate", this);
33935         }
33936     },
33937     /**
33938      * Updates this panel's element
33939      * @param {String} content The new content
33940      * @param {Boolean} loadScripts (optional) true to look for and process scripts
33941     */
33942     setContent : function(content, loadScripts){
33943         this.el.update(content, loadScripts);
33944     },
33945
33946     ignoreResize : function(w, h){
33947         if(this.lastSize && this.lastSize.width == w && this.lastSize.height == h){
33948             return true;
33949         }else{
33950             this.lastSize = {width: w, height: h};
33951             return false;
33952         }
33953     },
33954     /**
33955      * Get the {@link Roo.UpdateManager} for this panel. Enables you to perform Ajax updates.
33956      * @return {Roo.UpdateManager} The UpdateManager
33957      */
33958     getUpdateManager : function(){
33959         return this.el.getUpdateManager();
33960     },
33961      /**
33962      * Loads this content panel immediately with content from XHR. Note: to delay loading until the panel is activated, use {@link #setUrl}.
33963      * @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:
33964 <pre><code>
33965 panel.load({
33966     url: "your-url.php",
33967     params: {param1: "foo", param2: "bar"}, // or a URL encoded string
33968     callback: yourFunction,
33969     scope: yourObject, //(optional scope)
33970     discardUrl: false,
33971     nocache: false,
33972     text: "Loading...",
33973     timeout: 30,
33974     scripts: false
33975 });
33976 </code></pre>
33977      * The only required property is <i>url</i>. The optional properties <i>nocache</i>, <i>text</i> and <i>scripts</i>
33978      * 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.
33979      * @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}
33980      * @param {Function} callback (optional) Callback when transaction is complete -- called with signature (oElement, bSuccess, oResponse)
33981      * @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.
33982      * @return {Roo.ContentPanel} this
33983      */
33984     load : function(){
33985         var um = this.el.getUpdateManager();
33986         um.update.apply(um, arguments);
33987         return this;
33988     },
33989
33990
33991     /**
33992      * 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.
33993      * @param {String/Function} url The URL to load the content from or a function to call to get the URL
33994      * @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)
33995      * @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)
33996      * @return {Roo.UpdateManager} The UpdateManager
33997      */
33998     setUrl : function(url, params, loadOnce){
33999         if(this.refreshDelegate){
34000             this.removeListener("activate", this.refreshDelegate);
34001         }
34002         this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
34003         this.on("activate", this.refreshDelegate);
34004         return this.el.getUpdateManager();
34005     },
34006     
34007     _handleRefresh : function(url, params, loadOnce){
34008         if(!loadOnce || !this.loaded){
34009             var updater = this.el.getUpdateManager();
34010             updater.update(url, params, this._setLoaded.createDelegate(this));
34011         }
34012     },
34013     
34014     _setLoaded : function(){
34015         this.loaded = true;
34016     }, 
34017     
34018     /**
34019      * Returns this panel's id
34020      * @return {String} 
34021      */
34022     getId : function(){
34023         return this.el.id;
34024     },
34025     
34026     /** 
34027      * Returns this panel's element - used by regiosn to add.
34028      * @return {Roo.Element} 
34029      */
34030     getEl : function(){
34031         return this.wrapEl || this.el;
34032     },
34033     
34034     adjustForComponents : function(width, height)
34035     {
34036         //Roo.log('adjustForComponents ');
34037         if(this.resizeEl != this.el){
34038             width -= this.el.getFrameWidth('lr');
34039             height -= this.el.getFrameWidth('tb');
34040         }
34041         if(this.toolbar){
34042             var te = this.toolbar.getEl();
34043             height -= te.getHeight();
34044             te.setWidth(width);
34045         }
34046         if(this.footer){
34047             var te = this.footer.getEl();
34048             Roo.log("footer:" + te.getHeight());
34049             
34050             height -= te.getHeight();
34051             te.setWidth(width);
34052         }
34053         
34054         
34055         if(this.adjustments){
34056             width += this.adjustments[0];
34057             height += this.adjustments[1];
34058         }
34059         return {"width": width, "height": height};
34060     },
34061     
34062     setSize : function(width, height){
34063         if(this.fitToFrame && !this.ignoreResize(width, height)){
34064             if(this.fitContainer && this.resizeEl != this.el){
34065                 this.el.setSize(width, height);
34066             }
34067             var size = this.adjustForComponents(width, height);
34068             this.resizeEl.setSize(this.autoWidth ? "auto" : size.width, this.autoHeight ? "auto" : size.height);
34069             this.fireEvent('resize', this, size.width, size.height);
34070         }
34071     },
34072     
34073     /**
34074      * Returns this panel's title
34075      * @return {String} 
34076      */
34077     getTitle : function(){
34078         return this.title;
34079     },
34080     
34081     /**
34082      * Set this panel's title
34083      * @param {String} title
34084      */
34085     setTitle : function(title){
34086         this.title = title;
34087         if(this.region){
34088             this.region.updatePanelTitle(this, title);
34089         }
34090     },
34091     
34092     /**
34093      * Returns true is this panel was configured to be closable
34094      * @return {Boolean} 
34095      */
34096     isClosable : function(){
34097         return this.closable;
34098     },
34099     
34100     beforeSlide : function(){
34101         this.el.clip();
34102         this.resizeEl.clip();
34103     },
34104     
34105     afterSlide : function(){
34106         this.el.unclip();
34107         this.resizeEl.unclip();
34108     },
34109     
34110     /**
34111      *   Force a content refresh from the URL specified in the {@link #setUrl} method.
34112      *   Will fail silently if the {@link #setUrl} method has not been called.
34113      *   This does not activate the panel, just updates its content.
34114      */
34115     refresh : function(){
34116         if(this.refreshDelegate){
34117            this.loaded = false;
34118            this.refreshDelegate();
34119         }
34120     },
34121     
34122     /**
34123      * Destroys this panel
34124      */
34125     destroy : function(){
34126         this.el.removeAllListeners();
34127         var tempEl = document.createElement("span");
34128         tempEl.appendChild(this.el.dom);
34129         tempEl.innerHTML = "";
34130         this.el.remove();
34131         this.el = null;
34132     },
34133     
34134     /**
34135      * form - if the content panel contains a form - this is a reference to it.
34136      * @type {Roo.form.Form}
34137      */
34138     form : false,
34139     /**
34140      * view - if the content panel contains a view (Roo.DatePicker / Roo.View / Roo.JsonView)
34141      *    This contains a reference to it.
34142      * @type {Roo.View}
34143      */
34144     view : false,
34145     
34146       /**
34147      * Adds a xtype elements to the panel - currently only supports Forms, View, JsonView.
34148      * <pre><code>
34149
34150 layout.addxtype({
34151        xtype : 'Form',
34152        items: [ .... ]
34153    }
34154 );
34155
34156 </code></pre>
34157      * @param {Object} cfg Xtype definition of item to add.
34158      */
34159     
34160     addxtype : function(cfg) {
34161         // add form..
34162         if (cfg.xtype.match(/^Form$/)) {
34163             
34164             var el;
34165             //if (this.footer) {
34166             //    el = this.footer.container.insertSibling(false, 'before');
34167             //} else {
34168                 el = this.el.createChild();
34169             //}
34170
34171             this.form = new  Roo.form.Form(cfg);
34172             
34173             
34174             if ( this.form.allItems.length) this.form.render(el.dom);
34175             return this.form;
34176         }
34177         // should only have one of theses..
34178         if (['View', 'JsonView', 'DatePicker'].indexOf(cfg.xtype) > -1) {
34179             // views..
34180             cfg.el = this.el.appendChild(document.createElement("div"));
34181             // factory?
34182             
34183             var ret = new Roo.factory(cfg);
34184             ret.render && ret.render(false, ''); // render blank..
34185             this.view = ret;
34186             return ret;
34187         }
34188         return false;
34189     }
34190 });
34191
34192 /**
34193  * @class Roo.GridPanel
34194  * @extends Roo.ContentPanel
34195  * @constructor
34196  * Create a new GridPanel.
34197  * @param {Roo.grid.Grid} grid The grid for this panel
34198  * @param {String/Object} config A string to set only the panel's title, or a config object
34199  */
34200 Roo.GridPanel = function(grid, config){
34201     
34202   
34203     this.wrapper = Roo.DomHelper.append(document.body, // wrapper for IE7 strict & safari scroll issue
34204         {tag: "div", cls: "x-layout-grid-wrapper x-layout-inactive-content"}, true);
34205         
34206     this.wrapper.dom.appendChild(grid.getGridEl().dom);
34207     
34208     Roo.GridPanel.superclass.constructor.call(this, this.wrapper, config);
34209     
34210     if(this.toolbar){
34211         this.toolbar.el.insertBefore(this.wrapper.dom.firstChild);
34212     }
34213     // xtype created footer. - not sure if will work as we normally have to render first..
34214     if (this.footer && !this.footer.el && this.footer.xtype) {
34215         
34216         this.footer.container = this.grid.getView().getFooterPanel(true);
34217         this.footer.dataSource = this.grid.dataSource;
34218         this.footer = Roo.factory(this.footer, Roo);
34219         
34220     }
34221     
34222     grid.monitorWindowResize = false; // turn off autosizing
34223     grid.autoHeight = false;
34224     grid.autoWidth = false;
34225     this.grid = grid;
34226     this.grid.getGridEl().replaceClass("x-layout-inactive-content", "x-layout-component-panel");
34227 };
34228
34229 Roo.extend(Roo.GridPanel, Roo.ContentPanel, {
34230     getId : function(){
34231         return this.grid.id;
34232     },
34233     
34234     /**
34235      * Returns the grid for this panel
34236      * @return {Roo.grid.Grid} 
34237      */
34238     getGrid : function(){
34239         return this.grid;    
34240     },
34241     
34242     setSize : function(width, height){
34243         if(!this.ignoreResize(width, height)){
34244             var grid = this.grid;
34245             var size = this.adjustForComponents(width, height);
34246             grid.getGridEl().setSize(size.width, size.height);
34247             grid.autoSize();
34248         }
34249     },
34250     
34251     beforeSlide : function(){
34252         this.grid.getView().scroller.clip();
34253     },
34254     
34255     afterSlide : function(){
34256         this.grid.getView().scroller.unclip();
34257     },
34258     
34259     destroy : function(){
34260         this.grid.destroy();
34261         delete this.grid;
34262         Roo.GridPanel.superclass.destroy.call(this); 
34263     }
34264 });
34265
34266
34267 /**
34268  * @class Roo.NestedLayoutPanel
34269  * @extends Roo.ContentPanel
34270  * @constructor
34271  * Create a new NestedLayoutPanel.
34272  * 
34273  * 
34274  * @param {Roo.BorderLayout} layout The layout for this panel
34275  * @param {String/Object} config A string to set only the title or a config object
34276  */
34277 Roo.NestedLayoutPanel = function(layout, config)
34278 {
34279     // construct with only one argument..
34280     /* FIXME - implement nicer consturctors
34281     if (layout.layout) {
34282         config = layout;
34283         layout = config.layout;
34284         delete config.layout;
34285     }
34286     if (layout.xtype && !layout.getEl) {
34287         // then layout needs constructing..
34288         layout = Roo.factory(layout, Roo);
34289     }
34290     */
34291     
34292     
34293     Roo.NestedLayoutPanel.superclass.constructor.call(this, layout.getEl(), config);
34294     
34295     layout.monitorWindowResize = false; // turn off autosizing
34296     this.layout = layout;
34297     this.layout.getEl().addClass("x-layout-nested-layout");
34298     
34299     
34300     
34301     
34302 };
34303
34304 Roo.extend(Roo.NestedLayoutPanel, Roo.ContentPanel, {
34305
34306     setSize : function(width, height){
34307         if(!this.ignoreResize(width, height)){
34308             var size = this.adjustForComponents(width, height);
34309             var el = this.layout.getEl();
34310             el.setSize(size.width, size.height);
34311             var touch = el.dom.offsetWidth;
34312             this.layout.layout();
34313             // ie requires a double layout on the first pass
34314             if(Roo.isIE && !this.initialized){
34315                 this.initialized = true;
34316                 this.layout.layout();
34317             }
34318         }
34319     },
34320     
34321     // activate all subpanels if not currently active..
34322     
34323     setActiveState : function(active){
34324         this.active = active;
34325         if(!active){
34326             this.fireEvent("deactivate", this);
34327             return;
34328         }
34329         
34330         this.fireEvent("activate", this);
34331         // not sure if this should happen before or after..
34332         if (!this.layout) {
34333             return; // should not happen..
34334         }
34335         var reg = false;
34336         for (var r in this.layout.regions) {
34337             reg = this.layout.getRegion(r);
34338             if (reg.getActivePanel()) {
34339                 //reg.showPanel(reg.getActivePanel()); // force it to activate.. 
34340                 reg.setActivePanel(reg.getActivePanel());
34341                 continue;
34342             }
34343             if (!reg.panels.length) {
34344                 continue;
34345             }
34346             reg.showPanel(reg.getPanel(0));
34347         }
34348         
34349         
34350         
34351         
34352     },
34353     
34354     /**
34355      * Returns the nested BorderLayout for this panel
34356      * @return {Roo.BorderLayout} 
34357      */
34358     getLayout : function(){
34359         return this.layout;
34360     },
34361     
34362      /**
34363      * Adds a xtype elements to the layout of the nested panel
34364      * <pre><code>
34365
34366 panel.addxtype({
34367        xtype : 'ContentPanel',
34368        region: 'west',
34369        items: [ .... ]
34370    }
34371 );
34372
34373 panel.addxtype({
34374         xtype : 'NestedLayoutPanel',
34375         region: 'west',
34376         layout: {
34377            center: { },
34378            west: { }   
34379         },
34380         items : [ ... list of content panels or nested layout panels.. ]
34381    }
34382 );
34383 </code></pre>
34384      * @param {Object} cfg Xtype definition of item to add.
34385      */
34386     addxtype : function(cfg) {
34387         return this.layout.addxtype(cfg);
34388     
34389     }
34390 });
34391
34392 Roo.ScrollPanel = function(el, config, content){
34393     config = config || {};
34394     config.fitToFrame = true;
34395     Roo.ScrollPanel.superclass.constructor.call(this, el, config, content);
34396     
34397     this.el.dom.style.overflow = "hidden";
34398     var wrap = this.el.wrap({cls: "x-scroller x-layout-inactive-content"});
34399     this.el.removeClass("x-layout-inactive-content");
34400     this.el.on("mousewheel", this.onWheel, this);
34401
34402     var up = wrap.createChild({cls: "x-scroller-up", html: "&#160;"}, this.el.dom);
34403     var down = wrap.createChild({cls: "x-scroller-down", html: "&#160;"});
34404     up.unselectable(); down.unselectable();
34405     up.on("click", this.scrollUp, this);
34406     down.on("click", this.scrollDown, this);
34407     up.addClassOnOver("x-scroller-btn-over");
34408     down.addClassOnOver("x-scroller-btn-over");
34409     up.addClassOnClick("x-scroller-btn-click");
34410     down.addClassOnClick("x-scroller-btn-click");
34411     this.adjustments = [0, -(up.getHeight() + down.getHeight())];
34412
34413     this.resizeEl = this.el;
34414     this.el = wrap; this.up = up; this.down = down;
34415 };
34416
34417 Roo.extend(Roo.ScrollPanel, Roo.ContentPanel, {
34418     increment : 100,
34419     wheelIncrement : 5,
34420     scrollUp : function(){
34421         this.resizeEl.scroll("up", this.increment, {callback: this.afterScroll, scope: this});
34422     },
34423
34424     scrollDown : function(){
34425         this.resizeEl.scroll("down", this.increment, {callback: this.afterScroll, scope: this});
34426     },
34427
34428     afterScroll : function(){
34429         var el = this.resizeEl;
34430         var t = el.dom.scrollTop, h = el.dom.scrollHeight, ch = el.dom.clientHeight;
34431         this.up[t == 0 ? "addClass" : "removeClass"]("x-scroller-btn-disabled");
34432         this.down[h - t <= ch ? "addClass" : "removeClass"]("x-scroller-btn-disabled");
34433     },
34434
34435     setSize : function(){
34436         Roo.ScrollPanel.superclass.setSize.apply(this, arguments);
34437         this.afterScroll();
34438     },
34439
34440     onWheel : function(e){
34441         var d = e.getWheelDelta();
34442         this.resizeEl.dom.scrollTop -= (d*this.wheelIncrement);
34443         this.afterScroll();
34444         e.stopEvent();
34445     },
34446
34447     setContent : function(content, loadScripts){
34448         this.resizeEl.update(content, loadScripts);
34449     }
34450
34451 });
34452
34453
34454
34455
34456
34457
34458
34459
34460
34461 /**
34462  * @class Roo.TreePanel
34463  * @extends Roo.ContentPanel
34464  * @constructor
34465  * Create a new TreePanel. - defaults to fit/scoll contents.
34466  * @param {String/Object} config A string to set only the panel's title, or a config object
34467  * @cfg {Roo.tree.TreePanel} tree The tree TreePanel, with config etc.
34468  */
34469 Roo.TreePanel = function(config){
34470     var el = config.el;
34471     var tree = config.tree;
34472     delete config.tree; 
34473     delete config.el; // hopefull!
34474     
34475     // wrapper for IE7 strict & safari scroll issue
34476     
34477     var treeEl = el.createChild();
34478     config.resizeEl = treeEl;
34479     
34480     
34481     
34482     Roo.TreePanel.superclass.constructor.call(this, el, config);
34483  
34484  
34485     this.tree = new Roo.tree.TreePanel(treeEl , tree);
34486     //console.log(tree);
34487     this.on('activate', function()
34488     {
34489         if (this.tree.rendered) {
34490             return;
34491         }
34492         //console.log('render tree');
34493         this.tree.render();
34494     });
34495     // this should not be needed.. - it's actually the 'el' that resizes?
34496     // actuall it breaks the containerScroll - dragging nodes auto scroll at top
34497     
34498     //this.on('resize',  function (cp, w, h) {
34499     //        this.tree.innerCt.setWidth(w);
34500     //        this.tree.innerCt.setHeight(h);
34501     //        //this.tree.innerCt.setStyle('overflow-y', 'auto');
34502     //});
34503
34504         
34505     
34506 };
34507
34508 Roo.extend(Roo.TreePanel, Roo.ContentPanel, {   
34509     fitToFrame : true,
34510     autoScroll : true
34511 });
34512
34513
34514
34515
34516
34517
34518
34519
34520
34521
34522
34523 /*
34524  * Based on:
34525  * Ext JS Library 1.1.1
34526  * Copyright(c) 2006-2007, Ext JS, LLC.
34527  *
34528  * Originally Released Under LGPL - original licence link has changed is not relivant.
34529  *
34530  * Fork - LGPL
34531  * <script type="text/javascript">
34532  */
34533  
34534
34535 /**
34536  * @class Roo.ReaderLayout
34537  * @extends Roo.BorderLayout
34538  * This is a pre-built layout that represents a classic, 5-pane application.  It consists of a header, a primary
34539  * center region containing two nested regions (a top one for a list view and one for item preview below),
34540  * and regions on either side that can be used for navigation, application commands, informational displays, etc.
34541  * The setup and configuration work exactly the same as it does for a {@link Roo.BorderLayout} - this class simply
34542  * expedites the setup of the overall layout and regions for this common application style.
34543  * Example:
34544  <pre><code>
34545 var reader = new Roo.ReaderLayout();
34546 var CP = Roo.ContentPanel;  // shortcut for adding
34547
34548 reader.beginUpdate();
34549 reader.add("north", new CP("north", "North"));
34550 reader.add("west", new CP("west", {title: "West"}));
34551 reader.add("east", new CP("east", {title: "East"}));
34552
34553 reader.regions.listView.add(new CP("listView", "List"));
34554 reader.regions.preview.add(new CP("preview", "Preview"));
34555 reader.endUpdate();
34556 </code></pre>
34557 * @constructor
34558 * Create a new ReaderLayout
34559 * @param {Object} config Configuration options
34560 * @param {String/HTMLElement/Element} container (optional) The container this layout is bound to (defaults to
34561 * document.body if omitted)
34562 */
34563 Roo.ReaderLayout = function(config, renderTo){
34564     var c = config || {size:{}};
34565     Roo.ReaderLayout.superclass.constructor.call(this, renderTo || document.body, {
34566         north: c.north !== false ? Roo.apply({
34567             split:false,
34568             initialSize: 32,
34569             titlebar: false
34570         }, c.north) : false,
34571         west: c.west !== false ? Roo.apply({
34572             split:true,
34573             initialSize: 200,
34574             minSize: 175,
34575             maxSize: 400,
34576             titlebar: true,
34577             collapsible: true,
34578             animate: true,
34579             margins:{left:5,right:0,bottom:5,top:5},
34580             cmargins:{left:5,right:5,bottom:5,top:5}
34581         }, c.west) : false,
34582         east: c.east !== false ? Roo.apply({
34583             split:true,
34584             initialSize: 200,
34585             minSize: 175,
34586             maxSize: 400,
34587             titlebar: true,
34588             collapsible: true,
34589             animate: true,
34590             margins:{left:0,right:5,bottom:5,top:5},
34591             cmargins:{left:5,right:5,bottom:5,top:5}
34592         }, c.east) : false,
34593         center: Roo.apply({
34594             tabPosition: 'top',
34595             autoScroll:false,
34596             closeOnTab: true,
34597             titlebar:false,
34598             margins:{left:c.west!==false ? 0 : 5,right:c.east!==false ? 0 : 5,bottom:5,top:2}
34599         }, c.center)
34600     });
34601
34602     this.el.addClass('x-reader');
34603
34604     this.beginUpdate();
34605
34606     var inner = new Roo.BorderLayout(Roo.get(document.body).createChild(), {
34607         south: c.preview !== false ? Roo.apply({
34608             split:true,
34609             initialSize: 200,
34610             minSize: 100,
34611             autoScroll:true,
34612             collapsible:true,
34613             titlebar: true,
34614             cmargins:{top:5,left:0, right:0, bottom:0}
34615         }, c.preview) : false,
34616         center: Roo.apply({
34617             autoScroll:false,
34618             titlebar:false,
34619             minHeight:200
34620         }, c.listView)
34621     });
34622     this.add('center', new Roo.NestedLayoutPanel(inner,
34623             Roo.apply({title: c.mainTitle || '',tabTip:''},c.innerPanelCfg)));
34624
34625     this.endUpdate();
34626
34627     this.regions.preview = inner.getRegion('south');
34628     this.regions.listView = inner.getRegion('center');
34629 };
34630
34631 Roo.extend(Roo.ReaderLayout, Roo.BorderLayout);/*
34632  * Based on:
34633  * Ext JS Library 1.1.1
34634  * Copyright(c) 2006-2007, Ext JS, LLC.
34635  *
34636  * Originally Released Under LGPL - original licence link has changed is not relivant.
34637  *
34638  * Fork - LGPL
34639  * <script type="text/javascript">
34640  */
34641  
34642 /**
34643  * @class Roo.grid.Grid
34644  * @extends Roo.util.Observable
34645  * This class represents the primary interface of a component based grid control.
34646  * <br><br>Usage:<pre><code>
34647  var grid = new Roo.grid.Grid("my-container-id", {
34648      ds: myDataStore,
34649      cm: myColModel,
34650      selModel: mySelectionModel,
34651      autoSizeColumns: true,
34652      monitorWindowResize: false,
34653      trackMouseOver: true
34654  });
34655  // set any options
34656  grid.render();
34657  * </code></pre>
34658  * <b>Common Problems:</b><br/>
34659  * - Grid does not resize properly when going smaller: Setting overflow hidden on the container
34660  * element will correct this<br/>
34661  * - If you get el.style[camel]= NaNpx or -2px or something related, be certain you have given your container element
34662  * dimensions. The grid adapts to your container's size, if your container has no size defined then the results
34663  * are unpredictable.<br/>
34664  * - Do not render the grid into an element with display:none. Try using visibility:hidden. Otherwise there is no way for the
34665  * grid to calculate dimensions/offsets.<br/>
34666   * @constructor
34667  * @param {String/HTMLElement/Roo.Element} container The element into which this grid will be rendered -
34668  * The container MUST have some type of size defined for the grid to fill. The container will be
34669  * automatically set to position relative if it isn't already.
34670  * @param {Object} config A config object that sets properties on this grid.
34671  */
34672 Roo.grid.Grid = function(container, config){
34673         // initialize the container
34674         this.container = Roo.get(container);
34675         this.container.update("");
34676         this.container.setStyle("overflow", "hidden");
34677     this.container.addClass('x-grid-container');
34678
34679     this.id = this.container.id;
34680
34681     Roo.apply(this, config);
34682     // check and correct shorthanded configs
34683     if(this.ds){
34684         this.dataSource = this.ds;
34685         delete this.ds;
34686     }
34687     if(this.cm){
34688         this.colModel = this.cm;
34689         delete this.cm;
34690     }
34691     if(this.sm){
34692         this.selModel = this.sm;
34693         delete this.sm;
34694     }
34695
34696     if (this.selModel) {
34697         this.selModel = Roo.factory(this.selModel, Roo.grid);
34698         this.sm = this.selModel;
34699         this.sm.xmodule = this.xmodule || false;
34700     }
34701     if (typeof(this.colModel.config) == 'undefined') {
34702         this.colModel = new Roo.grid.ColumnModel(this.colModel);
34703         this.cm = this.colModel;
34704         this.cm.xmodule = this.xmodule || false;
34705     }
34706     if (this.dataSource) {
34707         this.dataSource= Roo.factory(this.dataSource, Roo.data);
34708         this.ds = this.dataSource;
34709         this.ds.xmodule = this.xmodule || false;
34710          
34711     }
34712     
34713     
34714     
34715     if(this.width){
34716         this.container.setWidth(this.width);
34717     }
34718
34719     if(this.height){
34720         this.container.setHeight(this.height);
34721     }
34722     /** @private */
34723         this.addEvents({
34724         // raw events
34725         /**
34726          * @event click
34727          * The raw click event for the entire grid.
34728          * @param {Roo.EventObject} e
34729          */
34730         "click" : true,
34731         /**
34732          * @event dblclick
34733          * The raw dblclick event for the entire grid.
34734          * @param {Roo.EventObject} e
34735          */
34736         "dblclick" : true,
34737         /**
34738          * @event contextmenu
34739          * The raw contextmenu event for the entire grid.
34740          * @param {Roo.EventObject} e
34741          */
34742         "contextmenu" : true,
34743         /**
34744          * @event mousedown
34745          * The raw mousedown event for the entire grid.
34746          * @param {Roo.EventObject} e
34747          */
34748         "mousedown" : true,
34749         /**
34750          * @event mouseup
34751          * The raw mouseup event for the entire grid.
34752          * @param {Roo.EventObject} e
34753          */
34754         "mouseup" : true,
34755         /**
34756          * @event mouseover
34757          * The raw mouseover event for the entire grid.
34758          * @param {Roo.EventObject} e
34759          */
34760         "mouseover" : true,
34761         /**
34762          * @event mouseout
34763          * The raw mouseout event for the entire grid.
34764          * @param {Roo.EventObject} e
34765          */
34766         "mouseout" : true,
34767         /**
34768          * @event keypress
34769          * The raw keypress event for the entire grid.
34770          * @param {Roo.EventObject} e
34771          */
34772         "keypress" : true,
34773         /**
34774          * @event keydown
34775          * The raw keydown event for the entire grid.
34776          * @param {Roo.EventObject} e
34777          */
34778         "keydown" : true,
34779
34780         // custom events
34781
34782         /**
34783          * @event cellclick
34784          * Fires when a cell is clicked
34785          * @param {Grid} this
34786          * @param {Number} rowIndex
34787          * @param {Number} columnIndex
34788          * @param {Roo.EventObject} e
34789          */
34790         "cellclick" : true,
34791         /**
34792          * @event celldblclick
34793          * Fires when a cell is double clicked
34794          * @param {Grid} this
34795          * @param {Number} rowIndex
34796          * @param {Number} columnIndex
34797          * @param {Roo.EventObject} e
34798          */
34799         "celldblclick" : true,
34800         /**
34801          * @event rowclick
34802          * Fires when a row is clicked
34803          * @param {Grid} this
34804          * @param {Number} rowIndex
34805          * @param {Roo.EventObject} e
34806          */
34807         "rowclick" : true,
34808         /**
34809          * @event rowdblclick
34810          * Fires when a row is double clicked
34811          * @param {Grid} this
34812          * @param {Number} rowIndex
34813          * @param {Roo.EventObject} e
34814          */
34815         "rowdblclick" : true,
34816         /**
34817          * @event headerclick
34818          * Fires when a header is clicked
34819          * @param {Grid} this
34820          * @param {Number} columnIndex
34821          * @param {Roo.EventObject} e
34822          */
34823         "headerclick" : true,
34824         /**
34825          * @event headerdblclick
34826          * Fires when a header cell is double clicked
34827          * @param {Grid} this
34828          * @param {Number} columnIndex
34829          * @param {Roo.EventObject} e
34830          */
34831         "headerdblclick" : true,
34832         /**
34833          * @event rowcontextmenu
34834          * Fires when a row is right clicked
34835          * @param {Grid} this
34836          * @param {Number} rowIndex
34837          * @param {Roo.EventObject} e
34838          */
34839         "rowcontextmenu" : true,
34840         /**
34841          * @event cellcontextmenu
34842          * Fires when a cell is right clicked
34843          * @param {Grid} this
34844          * @param {Number} rowIndex
34845          * @param {Number} cellIndex
34846          * @param {Roo.EventObject} e
34847          */
34848          "cellcontextmenu" : true,
34849         /**
34850          * @event headercontextmenu
34851          * Fires when a header is right clicked
34852          * @param {Grid} this
34853          * @param {Number} columnIndex
34854          * @param {Roo.EventObject} e
34855          */
34856         "headercontextmenu" : true,
34857         /**
34858          * @event bodyscroll
34859          * Fires when the body element is scrolled
34860          * @param {Number} scrollLeft
34861          * @param {Number} scrollTop
34862          */
34863         "bodyscroll" : true,
34864         /**
34865          * @event columnresize
34866          * Fires when the user resizes a column
34867          * @param {Number} columnIndex
34868          * @param {Number} newSize
34869          */
34870         "columnresize" : true,
34871         /**
34872          * @event columnmove
34873          * Fires when the user moves a column
34874          * @param {Number} oldIndex
34875          * @param {Number} newIndex
34876          */
34877         "columnmove" : true,
34878         /**
34879          * @event startdrag
34880          * Fires when row(s) start being dragged
34881          * @param {Grid} this
34882          * @param {Roo.GridDD} dd The drag drop object
34883          * @param {event} e The raw browser event
34884          */
34885         "startdrag" : true,
34886         /**
34887          * @event enddrag
34888          * Fires when a drag operation is complete
34889          * @param {Grid} this
34890          * @param {Roo.GridDD} dd The drag drop object
34891          * @param {event} e The raw browser event
34892          */
34893         "enddrag" : true,
34894         /**
34895          * @event dragdrop
34896          * Fires when dragged row(s) are dropped on a valid DD target
34897          * @param {Grid} this
34898          * @param {Roo.GridDD} dd The drag drop object
34899          * @param {String} targetId The target drag drop object
34900          * @param {event} e The raw browser event
34901          */
34902         "dragdrop" : true,
34903         /**
34904          * @event dragover
34905          * Fires while row(s) are being dragged. "targetId" is the id of the Yahoo.util.DD object the selected rows are being dragged over.
34906          * @param {Grid} this
34907          * @param {Roo.GridDD} dd The drag drop object
34908          * @param {String} targetId The target drag drop object
34909          * @param {event} e The raw browser event
34910          */
34911         "dragover" : true,
34912         /**
34913          * @event dragenter
34914          *  Fires when the dragged row(s) first cross another DD target while being dragged
34915          * @param {Grid} this
34916          * @param {Roo.GridDD} dd The drag drop object
34917          * @param {String} targetId The target drag drop object
34918          * @param {event} e The raw browser event
34919          */
34920         "dragenter" : true,
34921         /**
34922          * @event dragout
34923          * Fires when the dragged row(s) leave another DD target while being dragged
34924          * @param {Grid} this
34925          * @param {Roo.GridDD} dd The drag drop object
34926          * @param {String} targetId The target drag drop object
34927          * @param {event} e The raw browser event
34928          */
34929         "dragout" : true,
34930         /**
34931          * @event rowclass
34932          * Fires when a row is rendered, so you can change add a style to it.
34933          * @param {GridView} gridview   The grid view
34934          * @param {Object} rowcfg   contains record  rowIndex and rowClass - set rowClass to add a style.
34935          */
34936         'rowclass' : true,
34937
34938         /**
34939          * @event render
34940          * Fires when the grid is rendered
34941          * @param {Grid} grid
34942          */
34943         'render' : true
34944     });
34945
34946     Roo.grid.Grid.superclass.constructor.call(this);
34947 };
34948 Roo.extend(Roo.grid.Grid, Roo.util.Observable, {
34949     
34950     /**
34951      * @cfg {String} ddGroup - drag drop group.
34952      */
34953
34954     /**
34955      * @cfg {Number} minColumnWidth The minimum width a column can be resized to. Default is 25.
34956      */
34957     minColumnWidth : 25,
34958
34959     /**
34960      * @cfg {Boolean} autoSizeColumns True to automatically resize the columns to fit their content
34961      * <b>on initial render.</b> It is more efficient to explicitly size the columns
34962      * through the ColumnModel's {@link Roo.grid.ColumnModel#width} config option.  Default is false.
34963      */
34964     autoSizeColumns : false,
34965
34966     /**
34967      * @cfg {Boolean} autoSizeHeaders True to measure headers with column data when auto sizing columns. Default is true.
34968      */
34969     autoSizeHeaders : true,
34970
34971     /**
34972      * @cfg {Boolean} monitorWindowResize True to autoSize the grid when the window resizes. Default is true.
34973      */
34974     monitorWindowResize : true,
34975
34976     /**
34977      * @cfg {Boolean} maxRowsToMeasure If autoSizeColumns is on, maxRowsToMeasure can be used to limit the number of
34978      * rows measured to get a columns size. Default is 0 (all rows).
34979      */
34980     maxRowsToMeasure : 0,
34981
34982     /**
34983      * @cfg {Boolean} trackMouseOver True to highlight rows when the mouse is over. Default is true.
34984      */
34985     trackMouseOver : true,
34986
34987     /**
34988     * @cfg {Boolean} enableDrag  True to enable drag of rows. Default is false. (double check if this is needed?)
34989     */
34990     
34991     /**
34992     * @cfg {Boolean} enableDragDrop True to enable drag and drop of rows. Default is false.
34993     */
34994     enableDragDrop : false,
34995     
34996     /**
34997     * @cfg {Boolean} enableColumnMove True to enable drag and drop reorder of columns. Default is true.
34998     */
34999     enableColumnMove : true,
35000     
35001     /**
35002     * @cfg {Boolean} enableColumnHide True to enable hiding of columns with the header context menu. Default is true.
35003     */
35004     enableColumnHide : true,
35005     
35006     /**
35007     * @cfg {Boolean} enableRowHeightSync True to manually sync row heights across locked and not locked rows. Default is false.
35008     */
35009     enableRowHeightSync : false,
35010     
35011     /**
35012     * @cfg {Boolean} stripeRows True to stripe the rows.  Default is true.
35013     */
35014     stripeRows : true,
35015     
35016     /**
35017     * @cfg {Boolean} autoHeight True to fit the height of the grid container to the height of the data. Default is false.
35018     */
35019     autoHeight : false,
35020
35021     /**
35022      * @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.
35023      */
35024     autoExpandColumn : false,
35025
35026     /**
35027     * @cfg {Number} autoExpandMin The minimum width the autoExpandColumn can have (if enabled).
35028     * Default is 50.
35029     */
35030     autoExpandMin : 50,
35031
35032     /**
35033     * @cfg {Number} autoExpandMax The maximum width the autoExpandColumn can have (if enabled). Default is 1000.
35034     */
35035     autoExpandMax : 1000,
35036
35037     /**
35038     * @cfg {Object} view The {@link Roo.grid.GridView} used by the grid. This can be set before a call to render().
35039     */
35040     view : null,
35041
35042     /**
35043     * @cfg {Object} loadMask An {@link Roo.LoadMask} config or true to mask the grid while loading. Default is false.
35044     */
35045     loadMask : false,
35046     /**
35047     * @cfg {Roo.dd.DropTarget} dropTarget An {@link Roo.dd.DropTarget} config
35048     */
35049     dropTarget: false,
35050     
35051    
35052     
35053     // private
35054     rendered : false,
35055
35056     /**
35057     * @cfg {Boolean} autoWidth True to set the grid's width to the default total width of the grid's columns instead
35058     * of a fixed width. Default is false.
35059     */
35060     /**
35061     * @cfg {Number} maxHeight Sets the maximum height of the grid - ignored if autoHeight is not on.
35062     */
35063     /**
35064      * Called once after all setup has been completed and the grid is ready to be rendered.
35065      * @return {Roo.grid.Grid} this
35066      */
35067     render : function()
35068     {
35069         var c = this.container;
35070         // try to detect autoHeight/width mode
35071         if((!c.dom.offsetHeight || c.dom.offsetHeight < 20) || c.getStyle("height") == "auto"){
35072             this.autoHeight = true;
35073         }
35074         var view = this.getView();
35075         view.init(this);
35076
35077         c.on("click", this.onClick, this);
35078         c.on("dblclick", this.onDblClick, this);
35079         c.on("contextmenu", this.onContextMenu, this);
35080         c.on("keydown", this.onKeyDown, this);
35081
35082         this.relayEvents(c, ["mousedown","mouseup","mouseover","mouseout","keypress"]);
35083
35084         this.getSelectionModel().init(this);
35085
35086         view.render();
35087
35088         if(this.loadMask){
35089             this.loadMask = new Roo.LoadMask(this.container,
35090                     Roo.apply({store:this.dataSource}, this.loadMask));
35091         }
35092         
35093         
35094         if (this.toolbar && this.toolbar.xtype) {
35095             this.toolbar.container = this.getView().getHeaderPanel(true);
35096             this.toolbar = new Roo.Toolbar(this.toolbar);
35097         }
35098         if (this.footer && this.footer.xtype) {
35099             this.footer.dataSource = this.getDataSource();
35100             this.footer.container = this.getView().getFooterPanel(true);
35101             this.footer = Roo.factory(this.footer, Roo);
35102         }
35103         if (this.dropTarget && this.dropTarget.xtype) {
35104             delete this.dropTarget.xtype;
35105             this.dropTarget =  new Roo.dd.DropTarget(this.getView().mainBody, this.dropTarget);
35106         }
35107         
35108         
35109         this.rendered = true;
35110         this.fireEvent('render', this);
35111         return this;
35112     },
35113
35114         /**
35115          * Reconfigures the grid to use a different Store and Column Model.
35116          * The View will be bound to the new objects and refreshed.
35117          * @param {Roo.data.Store} dataSource The new {@link Roo.data.Store} object
35118          * @param {Roo.grid.ColumnModel} The new {@link Roo.grid.ColumnModel} object
35119          */
35120     reconfigure : function(dataSource, colModel){
35121         if(this.loadMask){
35122             this.loadMask.destroy();
35123             this.loadMask = new Roo.LoadMask(this.container,
35124                     Roo.apply({store:dataSource}, this.loadMask));
35125         }
35126         this.view.bind(dataSource, colModel);
35127         this.dataSource = dataSource;
35128         this.colModel = colModel;
35129         this.view.refresh(true);
35130     },
35131
35132     // private
35133     onKeyDown : function(e){
35134         this.fireEvent("keydown", e);
35135     },
35136
35137     /**
35138      * Destroy this grid.
35139      * @param {Boolean} removeEl True to remove the element
35140      */
35141     destroy : function(removeEl, keepListeners){
35142         if(this.loadMask){
35143             this.loadMask.destroy();
35144         }
35145         var c = this.container;
35146         c.removeAllListeners();
35147         this.view.destroy();
35148         this.colModel.purgeListeners();
35149         if(!keepListeners){
35150             this.purgeListeners();
35151         }
35152         c.update("");
35153         if(removeEl === true){
35154             c.remove();
35155         }
35156     },
35157
35158     // private
35159     processEvent : function(name, e){
35160         this.fireEvent(name, e);
35161         var t = e.getTarget();
35162         var v = this.view;
35163         var header = v.findHeaderIndex(t);
35164         if(header !== false){
35165             this.fireEvent("header" + name, this, header, e);
35166         }else{
35167             var row = v.findRowIndex(t);
35168             var cell = v.findCellIndex(t);
35169             if(row !== false){
35170                 this.fireEvent("row" + name, this, row, e);
35171                 if(cell !== false){
35172                     this.fireEvent("cell" + name, this, row, cell, e);
35173                 }
35174             }
35175         }
35176     },
35177
35178     // private
35179     onClick : function(e){
35180         this.processEvent("click", e);
35181     },
35182
35183     // private
35184     onContextMenu : function(e, t){
35185         this.processEvent("contextmenu", e);
35186     },
35187
35188     // private
35189     onDblClick : function(e){
35190         this.processEvent("dblclick", e);
35191     },
35192
35193     // private
35194     walkCells : function(row, col, step, fn, scope){
35195         var cm = this.colModel, clen = cm.getColumnCount();
35196         var ds = this.dataSource, rlen = ds.getCount(), first = true;
35197         if(step < 0){
35198             if(col < 0){
35199                 row--;
35200                 first = false;
35201             }
35202             while(row >= 0){
35203                 if(!first){
35204                     col = clen-1;
35205                 }
35206                 first = false;
35207                 while(col >= 0){
35208                     if(fn.call(scope || this, row, col, cm) === true){
35209                         return [row, col];
35210                     }
35211                     col--;
35212                 }
35213                 row--;
35214             }
35215         } else {
35216             if(col >= clen){
35217                 row++;
35218                 first = false;
35219             }
35220             while(row < rlen){
35221                 if(!first){
35222                     col = 0;
35223                 }
35224                 first = false;
35225                 while(col < clen){
35226                     if(fn.call(scope || this, row, col, cm) === true){
35227                         return [row, col];
35228                     }
35229                     col++;
35230                 }
35231                 row++;
35232             }
35233         }
35234         return null;
35235     },
35236
35237     // private
35238     getSelections : function(){
35239         return this.selModel.getSelections();
35240     },
35241
35242     /**
35243      * Causes the grid to manually recalculate its dimensions. Generally this is done automatically,
35244      * but if manual update is required this method will initiate it.
35245      */
35246     autoSize : function(){
35247         if(this.rendered){
35248             this.view.layout();
35249             if(this.view.adjustForScroll){
35250                 this.view.adjustForScroll();
35251             }
35252         }
35253     },
35254
35255     /**
35256      * Returns the grid's underlying element.
35257      * @return {Element} The element
35258      */
35259     getGridEl : function(){
35260         return this.container;
35261     },
35262
35263     // private for compatibility, overridden by editor grid
35264     stopEditing : function(){},
35265
35266     /**
35267      * Returns the grid's SelectionModel.
35268      * @return {SelectionModel}
35269      */
35270     getSelectionModel : function(){
35271         if(!this.selModel){
35272             this.selModel = new Roo.grid.RowSelectionModel();
35273         }
35274         return this.selModel;
35275     },
35276
35277     /**
35278      * Returns the grid's DataSource.
35279      * @return {DataSource}
35280      */
35281     getDataSource : function(){
35282         return this.dataSource;
35283     },
35284
35285     /**
35286      * Returns the grid's ColumnModel.
35287      * @return {ColumnModel}
35288      */
35289     getColumnModel : function(){
35290         return this.colModel;
35291     },
35292
35293     /**
35294      * Returns the grid's GridView object.
35295      * @return {GridView}
35296      */
35297     getView : function(){
35298         if(!this.view){
35299             this.view = new Roo.grid.GridView(this.viewConfig);
35300         }
35301         return this.view;
35302     },
35303     /**
35304      * Called to get grid's drag proxy text, by default returns this.ddText.
35305      * @return {String}
35306      */
35307     getDragDropText : function(){
35308         var count = this.selModel.getCount();
35309         return String.format(this.ddText, count, count == 1 ? '' : 's');
35310     }
35311 });
35312 /**
35313  * Configures the text is the drag proxy (defaults to "%0 selected row(s)").
35314  * %0 is replaced with the number of selected rows.
35315  * @type String
35316  */
35317 Roo.grid.Grid.prototype.ddText = "{0} selected row{1}";/*
35318  * Based on:
35319  * Ext JS Library 1.1.1
35320  * Copyright(c) 2006-2007, Ext JS, LLC.
35321  *
35322  * Originally Released Under LGPL - original licence link has changed is not relivant.
35323  *
35324  * Fork - LGPL
35325  * <script type="text/javascript">
35326  */
35327  
35328 Roo.grid.AbstractGridView = function(){
35329         this.grid = null;
35330         
35331         this.events = {
35332             "beforerowremoved" : true,
35333             "beforerowsinserted" : true,
35334             "beforerefresh" : true,
35335             "rowremoved" : true,
35336             "rowsinserted" : true,
35337             "rowupdated" : true,
35338             "refresh" : true
35339         };
35340     Roo.grid.AbstractGridView.superclass.constructor.call(this);
35341 };
35342
35343 Roo.extend(Roo.grid.AbstractGridView, Roo.util.Observable, {
35344     rowClass : "x-grid-row",
35345     cellClass : "x-grid-cell",
35346     tdClass : "x-grid-td",
35347     hdClass : "x-grid-hd",
35348     splitClass : "x-grid-hd-split",
35349     
35350         init: function(grid){
35351         this.grid = grid;
35352                 var cid = this.grid.getGridEl().id;
35353         this.colSelector = "#" + cid + " ." + this.cellClass + "-";
35354         this.tdSelector = "#" + cid + " ." + this.tdClass + "-";
35355         this.hdSelector = "#" + cid + " ." + this.hdClass + "-";
35356         this.splitSelector = "#" + cid + " ." + this.splitClass + "-";
35357         },
35358         
35359         getColumnRenderers : function(){
35360         var renderers = [];
35361         var cm = this.grid.colModel;
35362         var colCount = cm.getColumnCount();
35363         for(var i = 0; i < colCount; i++){
35364             renderers[i] = cm.getRenderer(i);
35365         }
35366         return renderers;
35367     },
35368     
35369     getColumnIds : function(){
35370         var ids = [];
35371         var cm = this.grid.colModel;
35372         var colCount = cm.getColumnCount();
35373         for(var i = 0; i < colCount; i++){
35374             ids[i] = cm.getColumnId(i);
35375         }
35376         return ids;
35377     },
35378     
35379     getDataIndexes : function(){
35380         if(!this.indexMap){
35381             this.indexMap = this.buildIndexMap();
35382         }
35383         return this.indexMap.colToData;
35384     },
35385     
35386     getColumnIndexByDataIndex : function(dataIndex){
35387         if(!this.indexMap){
35388             this.indexMap = this.buildIndexMap();
35389         }
35390         return this.indexMap.dataToCol[dataIndex];
35391     },
35392     
35393     /**
35394      * Set a css style for a column dynamically. 
35395      * @param {Number} colIndex The index of the column
35396      * @param {String} name The css property name
35397      * @param {String} value The css value
35398      */
35399     setCSSStyle : function(colIndex, name, value){
35400         var selector = "#" + this.grid.id + " .x-grid-col-" + colIndex;
35401         Roo.util.CSS.updateRule(selector, name, value);
35402     },
35403     
35404     generateRules : function(cm){
35405         var ruleBuf = [], rulesId = this.grid.id + '-cssrules';
35406         Roo.util.CSS.removeStyleSheet(rulesId);
35407         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
35408             var cid = cm.getColumnId(i);
35409             ruleBuf.push(this.colSelector, cid, " {\n", cm.config[i].css, "}\n",
35410                          this.tdSelector, cid, " {\n}\n",
35411                          this.hdSelector, cid, " {\n}\n",
35412                          this.splitSelector, cid, " {\n}\n");
35413         }
35414         return Roo.util.CSS.createStyleSheet(ruleBuf.join(""), rulesId);
35415     }
35416 });/*
35417  * Based on:
35418  * Ext JS Library 1.1.1
35419  * Copyright(c) 2006-2007, Ext JS, LLC.
35420  *
35421  * Originally Released Under LGPL - original licence link has changed is not relivant.
35422  *
35423  * Fork - LGPL
35424  * <script type="text/javascript">
35425  */
35426
35427 // private
35428 // This is a support class used internally by the Grid components
35429 Roo.grid.HeaderDragZone = function(grid, hd, hd2){
35430     this.grid = grid;
35431     this.view = grid.getView();
35432     this.ddGroup = "gridHeader" + this.grid.getGridEl().id;
35433     Roo.grid.HeaderDragZone.superclass.constructor.call(this, hd);
35434     if(hd2){
35435         this.setHandleElId(Roo.id(hd));
35436         this.setOuterHandleElId(Roo.id(hd2));
35437     }
35438     this.scroll = false;
35439 };
35440 Roo.extend(Roo.grid.HeaderDragZone, Roo.dd.DragZone, {
35441     maxDragWidth: 120,
35442     getDragData : function(e){
35443         var t = Roo.lib.Event.getTarget(e);
35444         var h = this.view.findHeaderCell(t);
35445         if(h){
35446             return {ddel: h.firstChild, header:h};
35447         }
35448         return false;
35449     },
35450
35451     onInitDrag : function(e){
35452         this.view.headersDisabled = true;
35453         var clone = this.dragData.ddel.cloneNode(true);
35454         clone.id = Roo.id();
35455         clone.style.width = Math.min(this.dragData.header.offsetWidth,this.maxDragWidth) + "px";
35456         this.proxy.update(clone);
35457         return true;
35458     },
35459
35460     afterValidDrop : function(){
35461         var v = this.view;
35462         setTimeout(function(){
35463             v.headersDisabled = false;
35464         }, 50);
35465     },
35466
35467     afterInvalidDrop : function(){
35468         var v = this.view;
35469         setTimeout(function(){
35470             v.headersDisabled = false;
35471         }, 50);
35472     }
35473 });
35474 /*
35475  * Based on:
35476  * Ext JS Library 1.1.1
35477  * Copyright(c) 2006-2007, Ext JS, LLC.
35478  *
35479  * Originally Released Under LGPL - original licence link has changed is not relivant.
35480  *
35481  * Fork - LGPL
35482  * <script type="text/javascript">
35483  */
35484 // private
35485 // This is a support class used internally by the Grid components
35486 Roo.grid.HeaderDropZone = function(grid, hd, hd2){
35487     this.grid = grid;
35488     this.view = grid.getView();
35489     // split the proxies so they don't interfere with mouse events
35490     this.proxyTop = Roo.DomHelper.append(document.body, {
35491         cls:"col-move-top", html:"&#160;"
35492     }, true);
35493     this.proxyBottom = Roo.DomHelper.append(document.body, {
35494         cls:"col-move-bottom", html:"&#160;"
35495     }, true);
35496     this.proxyTop.hide = this.proxyBottom.hide = function(){
35497         this.setLeftTop(-100,-100);
35498         this.setStyle("visibility", "hidden");
35499     };
35500     this.ddGroup = "gridHeader" + this.grid.getGridEl().id;
35501     // temporarily disabled
35502     //Roo.dd.ScrollManager.register(this.view.scroller.dom);
35503     Roo.grid.HeaderDropZone.superclass.constructor.call(this, grid.getGridEl().dom);
35504 };
35505 Roo.extend(Roo.grid.HeaderDropZone, Roo.dd.DropZone, {
35506     proxyOffsets : [-4, -9],
35507     fly: Roo.Element.fly,
35508
35509     getTargetFromEvent : function(e){
35510         var t = Roo.lib.Event.getTarget(e);
35511         var cindex = this.view.findCellIndex(t);
35512         if(cindex !== false){
35513             return this.view.getHeaderCell(cindex);
35514         }
35515         return null;
35516     },
35517
35518     nextVisible : function(h){
35519         var v = this.view, cm = this.grid.colModel;
35520         h = h.nextSibling;
35521         while(h){
35522             if(!cm.isHidden(v.getCellIndex(h))){
35523                 return h;
35524             }
35525             h = h.nextSibling;
35526         }
35527         return null;
35528     },
35529
35530     prevVisible : function(h){
35531         var v = this.view, cm = this.grid.colModel;
35532         h = h.prevSibling;
35533         while(h){
35534             if(!cm.isHidden(v.getCellIndex(h))){
35535                 return h;
35536             }
35537             h = h.prevSibling;
35538         }
35539         return null;
35540     },
35541
35542     positionIndicator : function(h, n, e){
35543         var x = Roo.lib.Event.getPageX(e);
35544         var r = Roo.lib.Dom.getRegion(n.firstChild);
35545         var px, pt, py = r.top + this.proxyOffsets[1];
35546         if((r.right - x) <= (r.right-r.left)/2){
35547             px = r.right+this.view.borderWidth;
35548             pt = "after";
35549         }else{
35550             px = r.left;
35551             pt = "before";
35552         }
35553         var oldIndex = this.view.getCellIndex(h);
35554         var newIndex = this.view.getCellIndex(n);
35555
35556         if(this.grid.colModel.isFixed(newIndex)){
35557             return false;
35558         }
35559
35560         var locked = this.grid.colModel.isLocked(newIndex);
35561
35562         if(pt == "after"){
35563             newIndex++;
35564         }
35565         if(oldIndex < newIndex){
35566             newIndex--;
35567         }
35568         if(oldIndex == newIndex && (locked == this.grid.colModel.isLocked(oldIndex))){
35569             return false;
35570         }
35571         px +=  this.proxyOffsets[0];
35572         this.proxyTop.setLeftTop(px, py);
35573         this.proxyTop.show();
35574         if(!this.bottomOffset){
35575             this.bottomOffset = this.view.mainHd.getHeight();
35576         }
35577         this.proxyBottom.setLeftTop(px, py+this.proxyTop.dom.offsetHeight+this.bottomOffset);
35578         this.proxyBottom.show();
35579         return pt;
35580     },
35581
35582     onNodeEnter : function(n, dd, e, data){
35583         if(data.header != n){
35584             this.positionIndicator(data.header, n, e);
35585         }
35586     },
35587
35588     onNodeOver : function(n, dd, e, data){
35589         var result = false;
35590         if(data.header != n){
35591             result = this.positionIndicator(data.header, n, e);
35592         }
35593         if(!result){
35594             this.proxyTop.hide();
35595             this.proxyBottom.hide();
35596         }
35597         return result ? this.dropAllowed : this.dropNotAllowed;
35598     },
35599
35600     onNodeOut : function(n, dd, e, data){
35601         this.proxyTop.hide();
35602         this.proxyBottom.hide();
35603     },
35604
35605     onNodeDrop : function(n, dd, e, data){
35606         var h = data.header;
35607         if(h != n){
35608             var cm = this.grid.colModel;
35609             var x = Roo.lib.Event.getPageX(e);
35610             var r = Roo.lib.Dom.getRegion(n.firstChild);
35611             var pt = (r.right - x) <= ((r.right-r.left)/2) ? "after" : "before";
35612             var oldIndex = this.view.getCellIndex(h);
35613             var newIndex = this.view.getCellIndex(n);
35614             var locked = cm.isLocked(newIndex);
35615             if(pt == "after"){
35616                 newIndex++;
35617             }
35618             if(oldIndex < newIndex){
35619                 newIndex--;
35620             }
35621             if(oldIndex == newIndex && (locked == cm.isLocked(oldIndex))){
35622                 return false;
35623             }
35624             cm.setLocked(oldIndex, locked, true);
35625             cm.moveColumn(oldIndex, newIndex);
35626             this.grid.fireEvent("columnmove", oldIndex, newIndex);
35627             return true;
35628         }
35629         return false;
35630     }
35631 });
35632 /*
35633  * Based on:
35634  * Ext JS Library 1.1.1
35635  * Copyright(c) 2006-2007, Ext JS, LLC.
35636  *
35637  * Originally Released Under LGPL - original licence link has changed is not relivant.
35638  *
35639  * Fork - LGPL
35640  * <script type="text/javascript">
35641  */
35642   
35643 /**
35644  * @class Roo.grid.GridView
35645  * @extends Roo.util.Observable
35646  *
35647  * @constructor
35648  * @param {Object} config
35649  */
35650 Roo.grid.GridView = function(config){
35651     Roo.grid.GridView.superclass.constructor.call(this);
35652     this.el = null;
35653
35654     Roo.apply(this, config);
35655 };
35656
35657 Roo.extend(Roo.grid.GridView, Roo.grid.AbstractGridView, {
35658
35659     unselectable :  'unselectable="on"',
35660     unselectableCls :  'x-unselectable',
35661     
35662     
35663     rowClass : "x-grid-row",
35664
35665     cellClass : "x-grid-col",
35666
35667     tdClass : "x-grid-td",
35668
35669     hdClass : "x-grid-hd",
35670
35671     splitClass : "x-grid-split",
35672
35673     sortClasses : ["sort-asc", "sort-desc"],
35674
35675     enableMoveAnim : false,
35676
35677     hlColor: "C3DAF9",
35678
35679     dh : Roo.DomHelper,
35680
35681     fly : Roo.Element.fly,
35682
35683     css : Roo.util.CSS,
35684
35685     borderWidth: 1,
35686
35687     splitOffset: 3,
35688
35689     scrollIncrement : 22,
35690
35691     cellRE: /(?:.*?)x-grid-(?:hd|cell|csplit)-(?:[\d]+)-([\d]+)(?:.*?)/,
35692
35693     findRE: /\s?(?:x-grid-hd|x-grid-col|x-grid-csplit)\s/,
35694
35695     bind : function(ds, cm){
35696         if(this.ds){
35697             this.ds.un("load", this.onLoad, this);
35698             this.ds.un("datachanged", this.onDataChange, this);
35699             this.ds.un("add", this.onAdd, this);
35700             this.ds.un("remove", this.onRemove, this);
35701             this.ds.un("update", this.onUpdate, this);
35702             this.ds.un("clear", this.onClear, this);
35703         }
35704         if(ds){
35705             ds.on("load", this.onLoad, this);
35706             ds.on("datachanged", this.onDataChange, this);
35707             ds.on("add", this.onAdd, this);
35708             ds.on("remove", this.onRemove, this);
35709             ds.on("update", this.onUpdate, this);
35710             ds.on("clear", this.onClear, this);
35711         }
35712         this.ds = ds;
35713
35714         if(this.cm){
35715             this.cm.un("widthchange", this.onColWidthChange, this);
35716             this.cm.un("headerchange", this.onHeaderChange, this);
35717             this.cm.un("hiddenchange", this.onHiddenChange, this);
35718             this.cm.un("columnmoved", this.onColumnMove, this);
35719             this.cm.un("columnlockchange", this.onColumnLock, this);
35720         }
35721         if(cm){
35722             this.generateRules(cm);
35723             cm.on("widthchange", this.onColWidthChange, this);
35724             cm.on("headerchange", this.onHeaderChange, this);
35725             cm.on("hiddenchange", this.onHiddenChange, this);
35726             cm.on("columnmoved", this.onColumnMove, this);
35727             cm.on("columnlockchange", this.onColumnLock, this);
35728         }
35729         this.cm = cm;
35730     },
35731
35732     init: function(grid){
35733         Roo.grid.GridView.superclass.init.call(this, grid);
35734
35735         this.bind(grid.dataSource, grid.colModel);
35736
35737         grid.on("headerclick", this.handleHeaderClick, this);
35738
35739         if(grid.trackMouseOver){
35740             grid.on("mouseover", this.onRowOver, this);
35741             grid.on("mouseout", this.onRowOut, this);
35742         }
35743         grid.cancelTextSelection = function(){};
35744         this.gridId = grid.id;
35745
35746         var tpls = this.templates || {};
35747
35748         if(!tpls.master){
35749             tpls.master = new Roo.Template(
35750                '<div class="x-grid" hidefocus="true">',
35751                 '<a href="#" class="x-grid-focus" tabIndex="-1"></a>',
35752                   '<div class="x-grid-topbar"></div>',
35753                   '<div class="x-grid-scroller"><div></div></div>',
35754                   '<div class="x-grid-locked">',
35755                       '<div class="x-grid-header">{lockedHeader}</div>',
35756                       '<div class="x-grid-body">{lockedBody}</div>',
35757                   "</div>",
35758                   '<div class="x-grid-viewport">',
35759                       '<div class="x-grid-header">{header}</div>',
35760                       '<div class="x-grid-body">{body}</div>',
35761                   "</div>",
35762                   '<div class="x-grid-bottombar"></div>',
35763                  
35764                   '<div class="x-grid-resize-proxy">&#160;</div>',
35765                "</div>"
35766             );
35767             tpls.master.disableformats = true;
35768         }
35769
35770         if(!tpls.header){
35771             tpls.header = new Roo.Template(
35772                '<table border="0" cellspacing="0" cellpadding="0">',
35773                '<tbody><tr class="x-grid-hd-row">{cells}</tr></tbody>',
35774                "</table>{splits}"
35775             );
35776             tpls.header.disableformats = true;
35777         }
35778         tpls.header.compile();
35779
35780         if(!tpls.hcell){
35781             tpls.hcell = new Roo.Template(
35782                 '<td class="x-grid-hd x-grid-td-{id} {cellId}"><div title="{title}" class="x-grid-hd-inner x-grid-hd-{id}">',
35783                 '<div class="x-grid-hd-text ' + this.unselectableCls +  '" ' + this.unselectable +'>{value}<img class="x-grid-sort-icon" src="', Roo.BLANK_IMAGE_URL, '" /></div>',
35784                 "</div></td>"
35785              );
35786              tpls.hcell.disableFormats = true;
35787         }
35788         tpls.hcell.compile();
35789
35790         if(!tpls.hsplit){
35791             tpls.hsplit = new Roo.Template('<div class="x-grid-split {splitId} x-grid-split-{id}" style="{style} ' +
35792                                             this.unselectableCls +  '" ' + this.unselectable +'>&#160;</div>');
35793             tpls.hsplit.disableFormats = true;
35794         }
35795         tpls.hsplit.compile();
35796
35797         if(!tpls.body){
35798             tpls.body = new Roo.Template(
35799                '<table border="0" cellspacing="0" cellpadding="0">',
35800                "<tbody>{rows}</tbody>",
35801                "</table>"
35802             );
35803             tpls.body.disableFormats = true;
35804         }
35805         tpls.body.compile();
35806
35807         if(!tpls.row){
35808             tpls.row = new Roo.Template('<tr class="x-grid-row {alt}">{cells}</tr>');
35809             tpls.row.disableFormats = true;
35810         }
35811         tpls.row.compile();
35812
35813         if(!tpls.cell){
35814             tpls.cell = new Roo.Template(
35815                 '<td class="x-grid-col x-grid-td-{id} {cellId} {css}" tabIndex="0">',
35816                 '<div class="x-grid-col-{id} x-grid-cell-inner"><div class="x-grid-cell-text ' +
35817                     this.unselectableCls +  '" ' + this.unselectable +'" {attr}>{value}</div></div>',
35818                 "</td>"
35819             );
35820             tpls.cell.disableFormats = true;
35821         }
35822         tpls.cell.compile();
35823
35824         this.templates = tpls;
35825     },
35826
35827     // remap these for backwards compat
35828     onColWidthChange : function(){
35829         this.updateColumns.apply(this, arguments);
35830     },
35831     onHeaderChange : function(){
35832         this.updateHeaders.apply(this, arguments);
35833     }, 
35834     onHiddenChange : function(){
35835         this.handleHiddenChange.apply(this, arguments);
35836     },
35837     onColumnMove : function(){
35838         this.handleColumnMove.apply(this, arguments);
35839     },
35840     onColumnLock : function(){
35841         this.handleLockChange.apply(this, arguments);
35842     },
35843
35844     onDataChange : function(){
35845         this.refresh();
35846         this.updateHeaderSortState();
35847     },
35848
35849     onClear : function(){
35850         this.refresh();
35851     },
35852
35853     onUpdate : function(ds, record){
35854         this.refreshRow(record);
35855     },
35856
35857     refreshRow : function(record){
35858         var ds = this.ds, index;
35859         if(typeof record == 'number'){
35860             index = record;
35861             record = ds.getAt(index);
35862         }else{
35863             index = ds.indexOf(record);
35864         }
35865         this.insertRows(ds, index, index, true);
35866         this.onRemove(ds, record, index+1, true);
35867         this.syncRowHeights(index, index);
35868         this.layout();
35869         this.fireEvent("rowupdated", this, index, record);
35870     },
35871
35872     onAdd : function(ds, records, index){
35873         this.insertRows(ds, index, index + (records.length-1));
35874     },
35875
35876     onRemove : function(ds, record, index, isUpdate){
35877         if(isUpdate !== true){
35878             this.fireEvent("beforerowremoved", this, index, record);
35879         }
35880         var bt = this.getBodyTable(), lt = this.getLockedTable();
35881         if(bt.rows[index]){
35882             bt.firstChild.removeChild(bt.rows[index]);
35883         }
35884         if(lt.rows[index]){
35885             lt.firstChild.removeChild(lt.rows[index]);
35886         }
35887         if(isUpdate !== true){
35888             this.stripeRows(index);
35889             this.syncRowHeights(index, index);
35890             this.layout();
35891             this.fireEvent("rowremoved", this, index, record);
35892         }
35893     },
35894
35895     onLoad : function(){
35896         this.scrollToTop();
35897     },
35898
35899     /**
35900      * Scrolls the grid to the top
35901      */
35902     scrollToTop : function(){
35903         if(this.scroller){
35904             this.scroller.dom.scrollTop = 0;
35905             this.syncScroll();
35906         }
35907     },
35908
35909     /**
35910      * Gets a panel in the header of the grid that can be used for toolbars etc.
35911      * After modifying the contents of this panel a call to grid.autoSize() may be
35912      * required to register any changes in size.
35913      * @param {Boolean} doShow By default the header is hidden. Pass true to show the panel
35914      * @return Roo.Element
35915      */
35916     getHeaderPanel : function(doShow){
35917         if(doShow){
35918             this.headerPanel.show();
35919         }
35920         return this.headerPanel;
35921     },
35922
35923     /**
35924      * Gets a panel in the footer of the grid that can be used for toolbars etc.
35925      * After modifying the contents of this panel a call to grid.autoSize() may be
35926      * required to register any changes in size.
35927      * @param {Boolean} doShow By default the footer is hidden. Pass true to show the panel
35928      * @return Roo.Element
35929      */
35930     getFooterPanel : function(doShow){
35931         if(doShow){
35932             this.footerPanel.show();
35933         }
35934         return this.footerPanel;
35935     },
35936
35937     initElements : function(){
35938         var E = Roo.Element;
35939         var el = this.grid.getGridEl().dom.firstChild;
35940         var cs = el.childNodes;
35941
35942         this.el = new E(el);
35943         
35944          this.focusEl = new E(el.firstChild);
35945         this.focusEl.swallowEvent("click", true);
35946         
35947         this.headerPanel = new E(cs[1]);
35948         this.headerPanel.enableDisplayMode("block");
35949
35950         this.scroller = new E(cs[2]);
35951         this.scrollSizer = new E(this.scroller.dom.firstChild);
35952
35953         this.lockedWrap = new E(cs[3]);
35954         this.lockedHd = new E(this.lockedWrap.dom.firstChild);
35955         this.lockedBody = new E(this.lockedWrap.dom.childNodes[1]);
35956
35957         this.mainWrap = new E(cs[4]);
35958         this.mainHd = new E(this.mainWrap.dom.firstChild);
35959         this.mainBody = new E(this.mainWrap.dom.childNodes[1]);
35960
35961         this.footerPanel = new E(cs[5]);
35962         this.footerPanel.enableDisplayMode("block");
35963
35964         this.resizeProxy = new E(cs[6]);
35965
35966         this.headerSelector = String.format(
35967            '#{0} td.x-grid-hd, #{1} td.x-grid-hd',
35968            this.lockedHd.id, this.mainHd.id
35969         );
35970
35971         this.splitterSelector = String.format(
35972            '#{0} div.x-grid-split, #{1} div.x-grid-split',
35973            this.idToCssName(this.lockedHd.id), this.idToCssName(this.mainHd.id)
35974         );
35975     },
35976     idToCssName : function(s)
35977     {
35978         return s.replace(/[^a-z0-9]+/ig, '-');
35979     },
35980
35981     getHeaderCell : function(index){
35982         return Roo.DomQuery.select(this.headerSelector)[index];
35983     },
35984
35985     getHeaderCellMeasure : function(index){
35986         return this.getHeaderCell(index).firstChild;
35987     },
35988
35989     getHeaderCellText : function(index){
35990         return this.getHeaderCell(index).firstChild.firstChild;
35991     },
35992
35993     getLockedTable : function(){
35994         return this.lockedBody.dom.firstChild;
35995     },
35996
35997     getBodyTable : function(){
35998         return this.mainBody.dom.firstChild;
35999     },
36000
36001     getLockedRow : function(index){
36002         return this.getLockedTable().rows[index];
36003     },
36004
36005     getRow : function(index){
36006         return this.getBodyTable().rows[index];
36007     },
36008
36009     getRowComposite : function(index){
36010         if(!this.rowEl){
36011             this.rowEl = new Roo.CompositeElementLite();
36012         }
36013         var els = [], lrow, mrow;
36014         if(lrow = this.getLockedRow(index)){
36015             els.push(lrow);
36016         }
36017         if(mrow = this.getRow(index)){
36018             els.push(mrow);
36019         }
36020         this.rowEl.elements = els;
36021         return this.rowEl;
36022     },
36023     /**
36024      * Gets the 'td' of the cell
36025      * 
36026      * @param {Integer} rowIndex row to select
36027      * @param {Integer} colIndex column to select
36028      * 
36029      * @return {Object} 
36030      */
36031     getCell : function(rowIndex, colIndex){
36032         var locked = this.cm.getLockedCount();
36033         var source;
36034         if(colIndex < locked){
36035             source = this.lockedBody.dom.firstChild;
36036         }else{
36037             source = this.mainBody.dom.firstChild;
36038             colIndex -= locked;
36039         }
36040         return source.rows[rowIndex].childNodes[colIndex];
36041     },
36042
36043     getCellText : function(rowIndex, colIndex){
36044         return this.getCell(rowIndex, colIndex).firstChild.firstChild;
36045     },
36046
36047     getCellBox : function(cell){
36048         var b = this.fly(cell).getBox();
36049         if(Roo.isOpera){ // opera fails to report the Y
36050             b.y = cell.offsetTop + this.mainBody.getY();
36051         }
36052         return b;
36053     },
36054
36055     getCellIndex : function(cell){
36056         var id = String(cell.className).match(this.cellRE);
36057         if(id){
36058             return parseInt(id[1], 10);
36059         }
36060         return 0;
36061     },
36062
36063     findHeaderIndex : function(n){
36064         var r = Roo.fly(n).findParent("td." + this.hdClass, 6);
36065         return r ? this.getCellIndex(r) : false;
36066     },
36067
36068     findHeaderCell : function(n){
36069         var r = Roo.fly(n).findParent("td." + this.hdClass, 6);
36070         return r ? r : false;
36071     },
36072
36073     findRowIndex : function(n){
36074         if(!n){
36075             return false;
36076         }
36077         var r = Roo.fly(n).findParent("tr." + this.rowClass, 6);
36078         return r ? r.rowIndex : false;
36079     },
36080
36081     findCellIndex : function(node){
36082         var stop = this.el.dom;
36083         while(node && node != stop){
36084             if(this.findRE.test(node.className)){
36085                 return this.getCellIndex(node);
36086             }
36087             node = node.parentNode;
36088         }
36089         return false;
36090     },
36091
36092     getColumnId : function(index){
36093         return this.cm.getColumnId(index);
36094     },
36095
36096     getSplitters : function()
36097     {
36098         if(this.splitterSelector){
36099            return Roo.DomQuery.select(this.splitterSelector);
36100         }else{
36101             return null;
36102       }
36103     },
36104
36105     getSplitter : function(index){
36106         return this.getSplitters()[index];
36107     },
36108
36109     onRowOver : function(e, t){
36110         var row;
36111         if((row = this.findRowIndex(t)) !== false){
36112             this.getRowComposite(row).addClass("x-grid-row-over");
36113         }
36114     },
36115
36116     onRowOut : function(e, t){
36117         var row;
36118         if((row = this.findRowIndex(t)) !== false && row !== this.findRowIndex(e.getRelatedTarget())){
36119             this.getRowComposite(row).removeClass("x-grid-row-over");
36120         }
36121     },
36122
36123     renderHeaders : function(){
36124         var cm = this.cm;
36125         var ct = this.templates.hcell, ht = this.templates.header, st = this.templates.hsplit;
36126         var cb = [], lb = [], sb = [], lsb = [], p = {};
36127         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
36128             p.cellId = "x-grid-hd-0-" + i;
36129             p.splitId = "x-grid-csplit-0-" + i;
36130             p.id = cm.getColumnId(i);
36131             p.title = cm.getColumnTooltip(i) || "";
36132             p.value = cm.getColumnHeader(i) || "";
36133             p.style = (this.grid.enableColumnResize === false || !cm.isResizable(i) || cm.isFixed(i)) ? 'cursor:default' : '';
36134             if(!cm.isLocked(i)){
36135                 cb[cb.length] = ct.apply(p);
36136                 sb[sb.length] = st.apply(p);
36137             }else{
36138                 lb[lb.length] = ct.apply(p);
36139                 lsb[lsb.length] = st.apply(p);
36140             }
36141         }
36142         return [ht.apply({cells: lb.join(""), splits:lsb.join("")}),
36143                 ht.apply({cells: cb.join(""), splits:sb.join("")})];
36144     },
36145
36146     updateHeaders : function(){
36147         var html = this.renderHeaders();
36148         this.lockedHd.update(html[0]);
36149         this.mainHd.update(html[1]);
36150     },
36151
36152     /**
36153      * Focuses the specified row.
36154      * @param {Number} row The row index
36155      */
36156     focusRow : function(row)
36157     {
36158         //Roo.log('GridView.focusRow');
36159         var x = this.scroller.dom.scrollLeft;
36160         this.focusCell(row, 0, false);
36161         this.scroller.dom.scrollLeft = x;
36162     },
36163
36164     /**
36165      * Focuses the specified cell.
36166      * @param {Number} row The row index
36167      * @param {Number} col The column index
36168      * @param {Boolean} hscroll false to disable horizontal scrolling
36169      */
36170     focusCell : function(row, col, hscroll)
36171     {
36172         //Roo.log('GridView.focusCell');
36173         var el = this.ensureVisible(row, col, hscroll);
36174         this.focusEl.alignTo(el, "tl-tl");
36175         if(Roo.isGecko){
36176             this.focusEl.focus();
36177         }else{
36178             this.focusEl.focus.defer(1, this.focusEl);
36179         }
36180     },
36181
36182     /**
36183      * Scrolls the specified cell into view
36184      * @param {Number} row The row index
36185      * @param {Number} col The column index
36186      * @param {Boolean} hscroll false to disable horizontal scrolling
36187      */
36188     ensureVisible : function(row, col, hscroll)
36189     {
36190         //Roo.log('GridView.ensureVisible,' + row + ',' + col);
36191         //return null; //disable for testing.
36192         if(typeof row != "number"){
36193             row = row.rowIndex;
36194         }
36195         if(row < 0 && row >= this.ds.getCount()){
36196             return  null;
36197         }
36198         col = (col !== undefined ? col : 0);
36199         var cm = this.grid.colModel;
36200         while(cm.isHidden(col)){
36201             col++;
36202         }
36203
36204         var el = this.getCell(row, col);
36205         if(!el){
36206             return null;
36207         }
36208         var c = this.scroller.dom;
36209
36210         var ctop = parseInt(el.offsetTop, 10);
36211         var cleft = parseInt(el.offsetLeft, 10);
36212         var cbot = ctop + el.offsetHeight;
36213         var cright = cleft + el.offsetWidth;
36214         
36215         var ch = c.clientHeight - this.mainHd.dom.offsetHeight;
36216         var stop = parseInt(c.scrollTop, 10);
36217         var sleft = parseInt(c.scrollLeft, 10);
36218         var sbot = stop + ch;
36219         var sright = sleft + c.clientWidth;
36220         /*
36221         Roo.log('GridView.ensureVisible:' +
36222                 ' ctop:' + ctop +
36223                 ' c.clientHeight:' + c.clientHeight +
36224                 ' this.mainHd.dom.offsetHeight:' + this.mainHd.dom.offsetHeight +
36225                 ' stop:' + stop +
36226                 ' cbot:' + cbot +
36227                 ' sbot:' + sbot +
36228                 ' ch:' + ch  
36229                 );
36230         */
36231         if(ctop < stop){
36232              c.scrollTop = ctop;
36233             //Roo.log("set scrolltop to ctop DISABLE?");
36234         }else if(cbot > sbot){
36235             //Roo.log("set scrolltop to cbot-ch");
36236             c.scrollTop = cbot-ch;
36237         }
36238         
36239         if(hscroll !== false){
36240             if(cleft < sleft){
36241                 c.scrollLeft = cleft;
36242             }else if(cright > sright){
36243                 c.scrollLeft = cright-c.clientWidth;
36244             }
36245         }
36246          
36247         return el;
36248     },
36249
36250     updateColumns : function(){
36251         this.grid.stopEditing();
36252         var cm = this.grid.colModel, colIds = this.getColumnIds();
36253         //var totalWidth = cm.getTotalWidth();
36254         var pos = 0;
36255         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
36256             //if(cm.isHidden(i)) continue;
36257             var w = cm.getColumnWidth(i);
36258             this.css.updateRule(this.colSelector+this.idToCssName(colIds[i]), "width", (w - this.borderWidth) + "px");
36259             this.css.updateRule(this.hdSelector+this.idToCssName(colIds[i]), "width", (w - this.borderWidth) + "px");
36260         }
36261         this.updateSplitters();
36262     },
36263
36264     generateRules : function(cm){
36265         var ruleBuf = [], rulesId = this.idToCssName(this.grid.id)+ '-cssrules';
36266         Roo.util.CSS.removeStyleSheet(rulesId);
36267         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
36268             var cid = cm.getColumnId(i);
36269             var align = '';
36270             if(cm.config[i].align){
36271                 align = 'text-align:'+cm.config[i].align+';';
36272             }
36273             var hidden = '';
36274             if(cm.isHidden(i)){
36275                 hidden = 'display:none;';
36276             }
36277             var width = "width:" + (cm.getColumnWidth(i) - this.borderWidth) + "px;";
36278             ruleBuf.push(
36279                     this.colSelector, cid, " {\n", cm.config[i].css, align, width, "\n}\n",
36280                     this.hdSelector, cid, " {\n", align, width, "}\n",
36281                     this.tdSelector, cid, " {\n",hidden,"\n}\n",
36282                     this.splitSelector, cid, " {\n", hidden , "\n}\n");
36283         }
36284         return Roo.util.CSS.createStyleSheet(ruleBuf.join(""), rulesId);
36285     },
36286
36287     updateSplitters : function(){
36288         var cm = this.cm, s = this.getSplitters();
36289         if(s){ // splitters not created yet
36290             var pos = 0, locked = true;
36291             for(var i = 0, len = cm.getColumnCount(); i < len; i++){
36292                 if(cm.isHidden(i)) continue;
36293                 var w = cm.getColumnWidth(i); // make sure it's a number
36294                 if(!cm.isLocked(i) && locked){
36295                     pos = 0;
36296                     locked = false;
36297                 }
36298                 pos += w;
36299                 s[i].style.left = (pos-this.splitOffset) + "px";
36300             }
36301         }
36302     },
36303
36304     handleHiddenChange : function(colModel, colIndex, hidden){
36305         if(hidden){
36306             this.hideColumn(colIndex);
36307         }else{
36308             this.unhideColumn(colIndex);
36309         }
36310     },
36311
36312     hideColumn : function(colIndex){
36313         var cid = this.getColumnId(colIndex);
36314         this.css.updateRule(this.tdSelector+this.idToCssName(cid), "display", "none");
36315         this.css.updateRule(this.splitSelector+this.idToCssName(cid), "display", "none");
36316         if(Roo.isSafari){
36317             this.updateHeaders();
36318         }
36319         this.updateSplitters();
36320         this.layout();
36321     },
36322
36323     unhideColumn : function(colIndex){
36324         var cid = this.getColumnId(colIndex);
36325         this.css.updateRule(this.tdSelector+this.idToCssName(cid), "display", "");
36326         this.css.updateRule(this.splitSelector+this.idToCssName(cid), "display", "");
36327
36328         if(Roo.isSafari){
36329             this.updateHeaders();
36330         }
36331         this.updateSplitters();
36332         this.layout();
36333     },
36334
36335     insertRows : function(dm, firstRow, lastRow, isUpdate){
36336         if(firstRow == 0 && lastRow == dm.getCount()-1){
36337             this.refresh();
36338         }else{
36339             if(!isUpdate){
36340                 this.fireEvent("beforerowsinserted", this, firstRow, lastRow);
36341             }
36342             var s = this.getScrollState();
36343             var markup = this.renderRows(firstRow, lastRow);
36344             this.bufferRows(markup[0], this.getLockedTable(), firstRow);
36345             this.bufferRows(markup[1], this.getBodyTable(), firstRow);
36346             this.restoreScroll(s);
36347             if(!isUpdate){
36348                 this.fireEvent("rowsinserted", this, firstRow, lastRow);
36349                 this.syncRowHeights(firstRow, lastRow);
36350                 this.stripeRows(firstRow);
36351                 this.layout();
36352             }
36353         }
36354     },
36355
36356     bufferRows : function(markup, target, index){
36357         var before = null, trows = target.rows, tbody = target.tBodies[0];
36358         if(index < trows.length){
36359             before = trows[index];
36360         }
36361         var b = document.createElement("div");
36362         b.innerHTML = "<table><tbody>"+markup+"</tbody></table>";
36363         var rows = b.firstChild.rows;
36364         for(var i = 0, len = rows.length; i < len; i++){
36365             if(before){
36366                 tbody.insertBefore(rows[0], before);
36367             }else{
36368                 tbody.appendChild(rows[0]);
36369             }
36370         }
36371         b.innerHTML = "";
36372         b = null;
36373     },
36374
36375     deleteRows : function(dm, firstRow, lastRow){
36376         if(dm.getRowCount()<1){
36377             this.fireEvent("beforerefresh", this);
36378             this.mainBody.update("");
36379             this.lockedBody.update("");
36380             this.fireEvent("refresh", this);
36381         }else{
36382             this.fireEvent("beforerowsdeleted", this, firstRow, lastRow);
36383             var bt = this.getBodyTable();
36384             var tbody = bt.firstChild;
36385             var rows = bt.rows;
36386             for(var rowIndex = firstRow; rowIndex <= lastRow; rowIndex++){
36387                 tbody.removeChild(rows[firstRow]);
36388             }
36389             this.stripeRows(firstRow);
36390             this.fireEvent("rowsdeleted", this, firstRow, lastRow);
36391         }
36392     },
36393
36394     updateRows : function(dataSource, firstRow, lastRow){
36395         var s = this.getScrollState();
36396         this.refresh();
36397         this.restoreScroll(s);
36398     },
36399
36400     handleSort : function(dataSource, sortColumnIndex, sortDir, noRefresh){
36401         if(!noRefresh){
36402            this.refresh();
36403         }
36404         this.updateHeaderSortState();
36405     },
36406
36407     getScrollState : function(){
36408         
36409         var sb = this.scroller.dom;
36410         return {left: sb.scrollLeft, top: sb.scrollTop};
36411     },
36412
36413     stripeRows : function(startRow){
36414         if(!this.grid.stripeRows || this.ds.getCount() < 1){
36415             return;
36416         }
36417         startRow = startRow || 0;
36418         var rows = this.getBodyTable().rows;
36419         var lrows = this.getLockedTable().rows;
36420         var cls = ' x-grid-row-alt ';
36421         for(var i = startRow, len = rows.length; i < len; i++){
36422             var row = rows[i], lrow = lrows[i];
36423             var isAlt = ((i+1) % 2 == 0);
36424             var hasAlt = (' '+row.className + ' ').indexOf(cls) != -1;
36425             if(isAlt == hasAlt){
36426                 continue;
36427             }
36428             if(isAlt){
36429                 row.className += " x-grid-row-alt";
36430             }else{
36431                 row.className = row.className.replace("x-grid-row-alt", "");
36432             }
36433             if(lrow){
36434                 lrow.className = row.className;
36435             }
36436         }
36437     },
36438
36439     restoreScroll : function(state){
36440         //Roo.log('GridView.restoreScroll');
36441         var sb = this.scroller.dom;
36442         sb.scrollLeft = state.left;
36443         sb.scrollTop = state.top;
36444         this.syncScroll();
36445     },
36446
36447     syncScroll : function(){
36448         //Roo.log('GridView.syncScroll');
36449         var sb = this.scroller.dom;
36450         var sh = this.mainHd.dom;
36451         var bs = this.mainBody.dom;
36452         var lv = this.lockedBody.dom;
36453         sh.scrollLeft = bs.scrollLeft = sb.scrollLeft;
36454         lv.scrollTop = bs.scrollTop = sb.scrollTop;
36455     },
36456
36457     handleScroll : function(e){
36458         this.syncScroll();
36459         var sb = this.scroller.dom;
36460         this.grid.fireEvent("bodyscroll", sb.scrollLeft, sb.scrollTop);
36461         e.stopEvent();
36462     },
36463
36464     handleWheel : function(e){
36465         var d = e.getWheelDelta();
36466         this.scroller.dom.scrollTop -= d*22;
36467         // set this here to prevent jumpy scrolling on large tables
36468         this.lockedBody.dom.scrollTop = this.mainBody.dom.scrollTop = this.scroller.dom.scrollTop;
36469         e.stopEvent();
36470     },
36471
36472     renderRows : function(startRow, endRow){
36473         // pull in all the crap needed to render rows
36474         var g = this.grid, cm = g.colModel, ds = g.dataSource, stripe = g.stripeRows;
36475         var colCount = cm.getColumnCount();
36476
36477         if(ds.getCount() < 1){
36478             return ["", ""];
36479         }
36480
36481         // build a map for all the columns
36482         var cs = [];
36483         for(var i = 0; i < colCount; i++){
36484             var name = cm.getDataIndex(i);
36485             cs[i] = {
36486                 name : typeof name == 'undefined' ? ds.fields.get(i).name : name,
36487                 renderer : cm.getRenderer(i),
36488                 id : cm.getColumnId(i),
36489                 locked : cm.isLocked(i)
36490             };
36491         }
36492
36493         startRow = startRow || 0;
36494         endRow = typeof endRow == "undefined"? ds.getCount()-1 : endRow;
36495
36496         // records to render
36497         var rs = ds.getRange(startRow, endRow);
36498
36499         return this.doRender(cs, rs, ds, startRow, colCount, stripe);
36500     },
36501
36502     // As much as I hate to duplicate code, this was branched because FireFox really hates
36503     // [].join("") on strings. The performance difference was substantial enough to
36504     // branch this function
36505     doRender : Roo.isGecko ?
36506             function(cs, rs, ds, startRow, colCount, stripe){
36507                 var ts = this.templates, ct = ts.cell, rt = ts.row;
36508                 // buffers
36509                 var buf = "", lbuf = "", cb, lcb, c, p = {}, rp = {}, r, rowIndex;
36510                 
36511                 var hasListener = this.grid.hasListener('rowclass');
36512                 var rowcfg = {};
36513                 for(var j = 0, len = rs.length; j < len; j++){
36514                     r = rs[j]; cb = ""; lcb = ""; rowIndex = (j+startRow);
36515                     for(var i = 0; i < colCount; i++){
36516                         c = cs[i];
36517                         p.cellId = "x-grid-cell-" + rowIndex + "-" + i;
36518                         p.id = c.id;
36519                         p.css = p.attr = "";
36520                         p.value = c.renderer(r.data[c.name], p, r, rowIndex, i, ds);
36521                         if(p.value == undefined || p.value === "") p.value = "&#160;";
36522                         if(r.dirty && typeof r.modified[c.name] !== 'undefined'){
36523                             p.css += p.css ? ' x-grid-dirty-cell' : 'x-grid-dirty-cell';
36524                         }
36525                         var markup = ct.apply(p);
36526                         if(!c.locked){
36527                             cb+= markup;
36528                         }else{
36529                             lcb+= markup;
36530                         }
36531                     }
36532                     var alt = [];
36533                     if(stripe && ((rowIndex+1) % 2 == 0)){
36534                         alt.push("x-grid-row-alt")
36535                     }
36536                     if(r.dirty){
36537                         alt.push(  " x-grid-dirty-row");
36538                     }
36539                     rp.cells = lcb;
36540                     if(this.getRowClass){
36541                         alt.push(this.getRowClass(r, rowIndex));
36542                     }
36543                     if (hasListener) {
36544                         rowcfg = {
36545                              
36546                             record: r,
36547                             rowIndex : rowIndex,
36548                             rowClass : ''
36549                         }
36550                         this.grid.fireEvent('rowclass', this, rowcfg);
36551                         alt.push(rowcfg.rowClass);
36552                     }
36553                     rp.alt = alt.join(" ");
36554                     lbuf+= rt.apply(rp);
36555                     rp.cells = cb;
36556                     buf+=  rt.apply(rp);
36557                 }
36558                 return [lbuf, buf];
36559             } :
36560             function(cs, rs, ds, startRow, colCount, stripe){
36561                 var ts = this.templates, ct = ts.cell, rt = ts.row;
36562                 // buffers
36563                 var buf = [], lbuf = [], cb, lcb, c, p = {}, rp = {}, r, rowIndex;
36564                 var hasListener = this.grid.hasListener('rowclass');
36565  
36566                 var rowcfg = {};
36567                 for(var j = 0, len = rs.length; j < len; j++){
36568                     r = rs[j]; cb = []; lcb = []; rowIndex = (j+startRow);
36569                     for(var i = 0; i < colCount; i++){
36570                         c = cs[i];
36571                         p.cellId = "x-grid-cell-" + rowIndex + "-" + i;
36572                         p.id = c.id;
36573                         p.css = p.attr = "";
36574                         p.value = c.renderer(r.data[c.name], p, r, rowIndex, i, ds);
36575                         if(p.value == undefined || p.value === "") p.value = "&#160;";
36576                         if(r.dirty && typeof r.modified[c.name] !== 'undefined'){
36577                             p.css += p.css ? ' x-grid-dirty-cell' : 'x-grid-dirty-cell';
36578                         }
36579                         
36580                         var markup = ct.apply(p);
36581                         if(!c.locked){
36582                             cb[cb.length] = markup;
36583                         }else{
36584                             lcb[lcb.length] = markup;
36585                         }
36586                     }
36587                     var alt = [];
36588                     if(stripe && ((rowIndex+1) % 2 == 0)){
36589                         alt.push( "x-grid-row-alt");
36590                     }
36591                     if(r.dirty){
36592                         alt.push(" x-grid-dirty-row");
36593                     }
36594                     rp.cells = lcb;
36595                     if(this.getRowClass){
36596                         alt.push( this.getRowClass(r, rowIndex));
36597                     }
36598                     if (hasListener) {
36599                         rowcfg = {
36600                              
36601                             record: r,
36602                             rowIndex : rowIndex,
36603                             rowClass : ''
36604                         }
36605                         this.grid.fireEvent('rowclass', this, rowcfg);
36606                         alt.push(rowcfg.rowClass);
36607                     }
36608                     rp.alt = alt.join(" ");
36609                     rp.cells = lcb.join("");
36610                     lbuf[lbuf.length] = rt.apply(rp);
36611                     rp.cells = cb.join("");
36612                     buf[buf.length] =  rt.apply(rp);
36613                 }
36614                 return [lbuf.join(""), buf.join("")];
36615             },
36616
36617     renderBody : function(){
36618         var markup = this.renderRows();
36619         var bt = this.templates.body;
36620         return [bt.apply({rows: markup[0]}), bt.apply({rows: markup[1]})];
36621     },
36622
36623     /**
36624      * Refreshes the grid
36625      * @param {Boolean} headersToo
36626      */
36627     refresh : function(headersToo){
36628         this.fireEvent("beforerefresh", this);
36629         this.grid.stopEditing();
36630         var result = this.renderBody();
36631         this.lockedBody.update(result[0]);
36632         this.mainBody.update(result[1]);
36633         if(headersToo === true){
36634             this.updateHeaders();
36635             this.updateColumns();
36636             this.updateSplitters();
36637             this.updateHeaderSortState();
36638         }
36639         this.syncRowHeights();
36640         this.layout();
36641         this.fireEvent("refresh", this);
36642     },
36643
36644     handleColumnMove : function(cm, oldIndex, newIndex){
36645         this.indexMap = null;
36646         var s = this.getScrollState();
36647         this.refresh(true);
36648         this.restoreScroll(s);
36649         this.afterMove(newIndex);
36650     },
36651
36652     afterMove : function(colIndex){
36653         if(this.enableMoveAnim && Roo.enableFx){
36654             this.fly(this.getHeaderCell(colIndex).firstChild).highlight(this.hlColor);
36655         }
36656         // if multisort - fix sortOrder, and reload..
36657         if (this.grid.dataSource.multiSort) {
36658             // the we can call sort again..
36659             var dm = this.grid.dataSource;
36660             var cm = this.grid.colModel;
36661             var so = [];
36662             for(var i = 0; i < cm.config.length; i++ ) {
36663                 
36664                 if ((typeof(dm.sortToggle[cm.config[i].dataIndex]) == 'undefined')) {
36665                     continue; // dont' bother, it's not in sort list or being set.
36666                 }
36667                 
36668                 so.push(cm.config[i].dataIndex);
36669             };
36670             dm.sortOrder = so;
36671             dm.load(dm.lastOptions);
36672             
36673             
36674         }
36675         
36676     },
36677
36678     updateCell : function(dm, rowIndex, dataIndex){
36679         var colIndex = this.getColumnIndexByDataIndex(dataIndex);
36680         if(typeof colIndex == "undefined"){ // not present in grid
36681             return;
36682         }
36683         var cm = this.grid.colModel;
36684         var cell = this.getCell(rowIndex, colIndex);
36685         var cellText = this.getCellText(rowIndex, colIndex);
36686
36687         var p = {
36688             cellId : "x-grid-cell-" + rowIndex + "-" + colIndex,
36689             id : cm.getColumnId(colIndex),
36690             css: colIndex == cm.getColumnCount()-1 ? "x-grid-col-last" : ""
36691         };
36692         var renderer = cm.getRenderer(colIndex);
36693         var val = renderer(dm.getValueAt(rowIndex, dataIndex), p, rowIndex, colIndex, dm);
36694         if(typeof val == "undefined" || val === "") val = "&#160;";
36695         cellText.innerHTML = val;
36696         cell.className = this.cellClass + " " + this.idToCssName(p.cellId) + " " + p.css;
36697         this.syncRowHeights(rowIndex, rowIndex);
36698     },
36699
36700     calcColumnWidth : function(colIndex, maxRowsToMeasure){
36701         var maxWidth = 0;
36702         if(this.grid.autoSizeHeaders){
36703             var h = this.getHeaderCellMeasure(colIndex);
36704             maxWidth = Math.max(maxWidth, h.scrollWidth);
36705         }
36706         var tb, index;
36707         if(this.cm.isLocked(colIndex)){
36708             tb = this.getLockedTable();
36709             index = colIndex;
36710         }else{
36711             tb = this.getBodyTable();
36712             index = colIndex - this.cm.getLockedCount();
36713         }
36714         if(tb && tb.rows){
36715             var rows = tb.rows;
36716             var stopIndex = Math.min(maxRowsToMeasure || rows.length, rows.length);
36717             for(var i = 0; i < stopIndex; i++){
36718                 var cell = rows[i].childNodes[index].firstChild;
36719                 maxWidth = Math.max(maxWidth, cell.scrollWidth);
36720             }
36721         }
36722         return maxWidth + /*margin for error in IE*/ 5;
36723     },
36724     /**
36725      * Autofit a column to its content.
36726      * @param {Number} colIndex
36727      * @param {Boolean} forceMinSize true to force the column to go smaller if possible
36728      */
36729      autoSizeColumn : function(colIndex, forceMinSize, suppressEvent){
36730          if(this.cm.isHidden(colIndex)){
36731              return; // can't calc a hidden column
36732          }
36733         if(forceMinSize){
36734             var cid = this.cm.getColumnId(colIndex);
36735             this.css.updateRule(this.colSelector +this.idToCssName( cid), "width", this.grid.minColumnWidth + "px");
36736            if(this.grid.autoSizeHeaders){
36737                this.css.updateRule(this.hdSelector + this.idToCssName(cid), "width", this.grid.minColumnWidth + "px");
36738            }
36739         }
36740         var newWidth = this.calcColumnWidth(colIndex);
36741         this.cm.setColumnWidth(colIndex,
36742             Math.max(this.grid.minColumnWidth, newWidth), suppressEvent);
36743         if(!suppressEvent){
36744             this.grid.fireEvent("columnresize", colIndex, newWidth);
36745         }
36746     },
36747
36748     /**
36749      * Autofits all columns to their content and then expands to fit any extra space in the grid
36750      */
36751      autoSizeColumns : function(){
36752         var cm = this.grid.colModel;
36753         var colCount = cm.getColumnCount();
36754         for(var i = 0; i < colCount; i++){
36755             this.autoSizeColumn(i, true, true);
36756         }
36757         if(cm.getTotalWidth() < this.scroller.dom.clientWidth){
36758             this.fitColumns();
36759         }else{
36760             this.updateColumns();
36761             this.layout();
36762         }
36763     },
36764
36765     /**
36766      * Autofits all columns to the grid's width proportionate with their current size
36767      * @param {Boolean} reserveScrollSpace Reserve space for a scrollbar
36768      */
36769     fitColumns : function(reserveScrollSpace){
36770         var cm = this.grid.colModel;
36771         var colCount = cm.getColumnCount();
36772         var cols = [];
36773         var width = 0;
36774         var i, w;
36775         for (i = 0; i < colCount; i++){
36776             if(!cm.isHidden(i) && !cm.isFixed(i)){
36777                 w = cm.getColumnWidth(i);
36778                 cols.push(i);
36779                 cols.push(w);
36780                 width += w;
36781             }
36782         }
36783         var avail = Math.min(this.scroller.dom.clientWidth, this.el.getWidth());
36784         if(reserveScrollSpace){
36785             avail -= 17;
36786         }
36787         var frac = (avail - cm.getTotalWidth())/width;
36788         while (cols.length){
36789             w = cols.pop();
36790             i = cols.pop();
36791             cm.setColumnWidth(i, Math.floor(w + w*frac), true);
36792         }
36793         this.updateColumns();
36794         this.layout();
36795     },
36796
36797     onRowSelect : function(rowIndex){
36798         var row = this.getRowComposite(rowIndex);
36799         row.addClass("x-grid-row-selected");
36800     },
36801
36802     onRowDeselect : function(rowIndex){
36803         var row = this.getRowComposite(rowIndex);
36804         row.removeClass("x-grid-row-selected");
36805     },
36806
36807     onCellSelect : function(row, col){
36808         var cell = this.getCell(row, col);
36809         if(cell){
36810             Roo.fly(cell).addClass("x-grid-cell-selected");
36811         }
36812     },
36813
36814     onCellDeselect : function(row, col){
36815         var cell = this.getCell(row, col);
36816         if(cell){
36817             Roo.fly(cell).removeClass("x-grid-cell-selected");
36818         }
36819     },
36820
36821     updateHeaderSortState : function(){
36822         
36823         // sort state can be single { field: xxx, direction : yyy}
36824         // or   { xxx=>ASC , yyy : DESC ..... }
36825         
36826         var mstate = {};
36827         if (!this.ds.multiSort) { 
36828             var state = this.ds.getSortState();
36829             if(!state){
36830                 return;
36831             }
36832             mstate[state.field] = state.direction;
36833             // FIXME... - this is not used here.. but might be elsewhere..
36834             this.sortState = state;
36835             
36836         } else {
36837             mstate = this.ds.sortToggle;
36838         }
36839         //remove existing sort classes..
36840         
36841         var sc = this.sortClasses;
36842         var hds = this.el.select(this.headerSelector).removeClass(sc);
36843         
36844         for(var f in mstate) {
36845         
36846             var sortColumn = this.cm.findColumnIndex(f);
36847             
36848             if(sortColumn != -1){
36849                 var sortDir = mstate[f];        
36850                 hds.item(sortColumn).addClass(sc[sortDir == "DESC" ? 1 : 0]);
36851             }
36852         }
36853         
36854          
36855         
36856     },
36857
36858
36859     handleHeaderClick : function(g, index){
36860         if(this.headersDisabled){
36861             return;
36862         }
36863         var dm = g.dataSource, cm = g.colModel;
36864         if(!cm.isSortable(index)){
36865             return;
36866         }
36867         g.stopEditing();
36868         
36869         if (dm.multiSort) {
36870             // update the sortOrder
36871             var so = [];
36872             for(var i = 0; i < cm.config.length; i++ ) {
36873                 
36874                 if ((typeof(dm.sortToggle[cm.config[i].dataIndex]) == 'undefined') && (index != i)) {
36875                     continue; // dont' bother, it's not in sort list or being set.
36876                 }
36877                 
36878                 so.push(cm.config[i].dataIndex);
36879             };
36880             dm.sortOrder = so;
36881         }
36882         
36883         
36884         dm.sort(cm.getDataIndex(index));
36885     },
36886
36887
36888     destroy : function(){
36889         if(this.colMenu){
36890             this.colMenu.removeAll();
36891             Roo.menu.MenuMgr.unregister(this.colMenu);
36892             this.colMenu.getEl().remove();
36893             delete this.colMenu;
36894         }
36895         if(this.hmenu){
36896             this.hmenu.removeAll();
36897             Roo.menu.MenuMgr.unregister(this.hmenu);
36898             this.hmenu.getEl().remove();
36899             delete this.hmenu;
36900         }
36901         if(this.grid.enableColumnMove){
36902             var dds = Roo.dd.DDM.ids['gridHeader' + this.grid.getGridEl().id];
36903             if(dds){
36904                 for(var dd in dds){
36905                     if(!dds[dd].config.isTarget && dds[dd].dragElId){
36906                         var elid = dds[dd].dragElId;
36907                         dds[dd].unreg();
36908                         Roo.get(elid).remove();
36909                     } else if(dds[dd].config.isTarget){
36910                         dds[dd].proxyTop.remove();
36911                         dds[dd].proxyBottom.remove();
36912                         dds[dd].unreg();
36913                     }
36914                     if(Roo.dd.DDM.locationCache[dd]){
36915                         delete Roo.dd.DDM.locationCache[dd];
36916                     }
36917                 }
36918                 delete Roo.dd.DDM.ids['gridHeader' + this.grid.getGridEl().id];
36919             }
36920         }
36921         Roo.util.CSS.removeStyleSheet(this.idToCssName(this.grid.id) + '-cssrules');
36922         this.bind(null, null);
36923         Roo.EventManager.removeResizeListener(this.onWindowResize, this);
36924     },
36925
36926     handleLockChange : function(){
36927         this.refresh(true);
36928     },
36929
36930     onDenyColumnLock : function(){
36931
36932     },
36933
36934     onDenyColumnHide : function(){
36935
36936     },
36937
36938     handleHdMenuClick : function(item){
36939         var index = this.hdCtxIndex;
36940         var cm = this.cm, ds = this.ds;
36941         switch(item.id){
36942             case "asc":
36943                 ds.sort(cm.getDataIndex(index), "ASC");
36944                 break;
36945             case "desc":
36946                 ds.sort(cm.getDataIndex(index), "DESC");
36947                 break;
36948             case "lock":
36949                 var lc = cm.getLockedCount();
36950                 if(cm.getColumnCount(true) <= lc+1){
36951                     this.onDenyColumnLock();
36952                     return;
36953                 }
36954                 if(lc != index){
36955                     cm.setLocked(index, true, true);
36956                     cm.moveColumn(index, lc);
36957                     this.grid.fireEvent("columnmove", index, lc);
36958                 }else{
36959                     cm.setLocked(index, true);
36960                 }
36961             break;
36962             case "unlock":
36963                 var lc = cm.getLockedCount();
36964                 if((lc-1) != index){
36965                     cm.setLocked(index, false, true);
36966                     cm.moveColumn(index, lc-1);
36967                     this.grid.fireEvent("columnmove", index, lc-1);
36968                 }else{
36969                     cm.setLocked(index, false);
36970                 }
36971             break;
36972             default:
36973                 index = cm.getIndexById(item.id.substr(4));
36974                 if(index != -1){
36975                     if(item.checked && cm.getColumnCount(true) <= 1){
36976                         this.onDenyColumnHide();
36977                         return false;
36978                     }
36979                     cm.setHidden(index, item.checked);
36980                 }
36981         }
36982         return true;
36983     },
36984
36985     beforeColMenuShow : function(){
36986         var cm = this.cm,  colCount = cm.getColumnCount();
36987         this.colMenu.removeAll();
36988         for(var i = 0; i < colCount; i++){
36989             this.colMenu.add(new Roo.menu.CheckItem({
36990                 id: "col-"+cm.getColumnId(i),
36991                 text: cm.getColumnHeader(i),
36992                 checked: !cm.isHidden(i),
36993                 hideOnClick:false
36994             }));
36995         }
36996     },
36997
36998     handleHdCtx : function(g, index, e){
36999         e.stopEvent();
37000         var hd = this.getHeaderCell(index);
37001         this.hdCtxIndex = index;
37002         var ms = this.hmenu.items, cm = this.cm;
37003         ms.get("asc").setDisabled(!cm.isSortable(index));
37004         ms.get("desc").setDisabled(!cm.isSortable(index));
37005         if(this.grid.enableColLock !== false){
37006             ms.get("lock").setDisabled(cm.isLocked(index));
37007             ms.get("unlock").setDisabled(!cm.isLocked(index));
37008         }
37009         this.hmenu.show(hd, "tl-bl");
37010     },
37011
37012     handleHdOver : function(e){
37013         var hd = this.findHeaderCell(e.getTarget());
37014         if(hd && !this.headersDisabled){
37015             if(this.grid.colModel.isSortable(this.getCellIndex(hd))){
37016                this.fly(hd).addClass("x-grid-hd-over");
37017             }
37018         }
37019     },
37020
37021     handleHdOut : function(e){
37022         var hd = this.findHeaderCell(e.getTarget());
37023         if(hd){
37024             this.fly(hd).removeClass("x-grid-hd-over");
37025         }
37026     },
37027
37028     handleSplitDblClick : function(e, t){
37029         var i = this.getCellIndex(t);
37030         if(this.grid.enableColumnResize !== false && this.cm.isResizable(i) && !this.cm.isFixed(i)){
37031             this.autoSizeColumn(i, true);
37032             this.layout();
37033         }
37034     },
37035
37036     render : function(){
37037
37038         var cm = this.cm;
37039         var colCount = cm.getColumnCount();
37040
37041         if(this.grid.monitorWindowResize === true){
37042             Roo.EventManager.onWindowResize(this.onWindowResize, this, true);
37043         }
37044         var header = this.renderHeaders();
37045         var body = this.templates.body.apply({rows:""});
37046         var html = this.templates.master.apply({
37047             lockedBody: body,
37048             body: body,
37049             lockedHeader: header[0],
37050             header: header[1]
37051         });
37052
37053         //this.updateColumns();
37054
37055         this.grid.getGridEl().dom.innerHTML = html;
37056
37057         this.initElements();
37058         
37059         // a kludge to fix the random scolling effect in webkit
37060         this.el.on("scroll", function() {
37061             this.el.dom.scrollTop=0; // hopefully not recursive..
37062         },this);
37063
37064         this.scroller.on("scroll", this.handleScroll, this);
37065         this.lockedBody.on("mousewheel", this.handleWheel, this);
37066         this.mainBody.on("mousewheel", this.handleWheel, this);
37067
37068         this.mainHd.on("mouseover", this.handleHdOver, this);
37069         this.mainHd.on("mouseout", this.handleHdOut, this);
37070         this.mainHd.on("dblclick", this.handleSplitDblClick, this,
37071                 {delegate: "."+this.splitClass});
37072
37073         this.lockedHd.on("mouseover", this.handleHdOver, this);
37074         this.lockedHd.on("mouseout", this.handleHdOut, this);
37075         this.lockedHd.on("dblclick", this.handleSplitDblClick, this,
37076                 {delegate: "."+this.splitClass});
37077
37078         if(this.grid.enableColumnResize !== false && Roo.grid.SplitDragZone){
37079             new Roo.grid.SplitDragZone(this.grid, this.lockedHd.dom, this.mainHd.dom);
37080         }
37081
37082         this.updateSplitters();
37083
37084         if(this.grid.enableColumnMove && Roo.grid.HeaderDragZone){
37085             new Roo.grid.HeaderDragZone(this.grid, this.lockedHd.dom, this.mainHd.dom);
37086             new Roo.grid.HeaderDropZone(this.grid, this.lockedHd.dom, this.mainHd.dom);
37087         }
37088
37089         if(this.grid.enableCtxMenu !== false && Roo.menu.Menu){
37090             this.hmenu = new Roo.menu.Menu({id: this.grid.id + "-hctx"});
37091             this.hmenu.add(
37092                 {id:"asc", text: this.sortAscText, cls: "xg-hmenu-sort-asc"},
37093                 {id:"desc", text: this.sortDescText, cls: "xg-hmenu-sort-desc"}
37094             );
37095             if(this.grid.enableColLock !== false){
37096                 this.hmenu.add('-',
37097                     {id:"lock", text: this.lockText, cls: "xg-hmenu-lock"},
37098                     {id:"unlock", text: this.unlockText, cls: "xg-hmenu-unlock"}
37099                 );
37100             }
37101             if(this.grid.enableColumnHide !== false){
37102
37103                 this.colMenu = new Roo.menu.Menu({id:this.grid.id + "-hcols-menu"});
37104                 this.colMenu.on("beforeshow", this.beforeColMenuShow, this);
37105                 this.colMenu.on("itemclick", this.handleHdMenuClick, this);
37106
37107                 this.hmenu.add('-',
37108                     {id:"columns", text: this.columnsText, menu: this.colMenu}
37109                 );
37110             }
37111             this.hmenu.on("itemclick", this.handleHdMenuClick, this);
37112
37113             this.grid.on("headercontextmenu", this.handleHdCtx, this);
37114         }
37115
37116         if((this.grid.enableDragDrop || this.grid.enableDrag) && Roo.grid.GridDragZone){
37117             this.dd = new Roo.grid.GridDragZone(this.grid, {
37118                 ddGroup : this.grid.ddGroup || 'GridDD'
37119             });
37120             
37121         }
37122
37123         /*
37124         for(var i = 0; i < colCount; i++){
37125             if(cm.isHidden(i)){
37126                 this.hideColumn(i);
37127             }
37128             if(cm.config[i].align){
37129                 this.css.updateRule(this.colSelector + i, "textAlign", cm.config[i].align);
37130                 this.css.updateRule(this.hdSelector + i, "textAlign", cm.config[i].align);
37131             }
37132         }*/
37133         
37134         this.updateHeaderSortState();
37135
37136         this.beforeInitialResize();
37137         this.layout(true);
37138
37139         // two part rendering gives faster view to the user
37140         this.renderPhase2.defer(1, this);
37141     },
37142
37143     renderPhase2 : function(){
37144         // render the rows now
37145         this.refresh();
37146         if(this.grid.autoSizeColumns){
37147             this.autoSizeColumns();
37148         }
37149     },
37150
37151     beforeInitialResize : function(){
37152
37153     },
37154
37155     onColumnSplitterMoved : function(i, w){
37156         this.userResized = true;
37157         var cm = this.grid.colModel;
37158         cm.setColumnWidth(i, w, true);
37159         var cid = cm.getColumnId(i);
37160         this.css.updateRule(this.colSelector + this.idToCssName(cid), "width", (w-this.borderWidth) + "px");
37161         this.css.updateRule(this.hdSelector + this.idToCssName(cid), "width", (w-this.borderWidth) + "px");
37162         this.updateSplitters();
37163         this.layout();
37164         this.grid.fireEvent("columnresize", i, w);
37165     },
37166
37167     syncRowHeights : function(startIndex, endIndex){
37168         if(this.grid.enableRowHeightSync === true && this.cm.getLockedCount() > 0){
37169             startIndex = startIndex || 0;
37170             var mrows = this.getBodyTable().rows;
37171             var lrows = this.getLockedTable().rows;
37172             var len = mrows.length-1;
37173             endIndex = Math.min(endIndex || len, len);
37174             for(var i = startIndex; i <= endIndex; i++){
37175                 var m = mrows[i], l = lrows[i];
37176                 var h = Math.max(m.offsetHeight, l.offsetHeight);
37177                 m.style.height = l.style.height = h + "px";
37178             }
37179         }
37180     },
37181
37182     layout : function(initialRender, is2ndPass){
37183         var g = this.grid;
37184         var auto = g.autoHeight;
37185         var scrollOffset = 16;
37186         var c = g.getGridEl(), cm = this.cm,
37187                 expandCol = g.autoExpandColumn,
37188                 gv = this;
37189         //c.beginMeasure();
37190
37191         if(!c.dom.offsetWidth){ // display:none?
37192             if(initialRender){
37193                 this.lockedWrap.show();
37194                 this.mainWrap.show();
37195             }
37196             return;
37197         }
37198
37199         var hasLock = this.cm.isLocked(0);
37200
37201         var tbh = this.headerPanel.getHeight();
37202         var bbh = this.footerPanel.getHeight();
37203
37204         if(auto){
37205             var ch = this.getBodyTable().offsetHeight + tbh + bbh + this.mainHd.getHeight();
37206             var newHeight = ch + c.getBorderWidth("tb");
37207             if(g.maxHeight){
37208                 newHeight = Math.min(g.maxHeight, newHeight);
37209             }
37210             c.setHeight(newHeight);
37211         }
37212
37213         if(g.autoWidth){
37214             c.setWidth(cm.getTotalWidth()+c.getBorderWidth('lr'));
37215         }
37216
37217         var s = this.scroller;
37218
37219         var csize = c.getSize(true);
37220
37221         this.el.setSize(csize.width, csize.height);
37222
37223         this.headerPanel.setWidth(csize.width);
37224         this.footerPanel.setWidth(csize.width);
37225
37226         var hdHeight = this.mainHd.getHeight();
37227         var vw = csize.width;
37228         var vh = csize.height - (tbh + bbh);
37229
37230         s.setSize(vw, vh);
37231
37232         var bt = this.getBodyTable();
37233         var ltWidth = hasLock ?
37234                       Math.max(this.getLockedTable().offsetWidth, this.lockedHd.dom.firstChild.offsetWidth) : 0;
37235
37236         var scrollHeight = bt.offsetHeight;
37237         var scrollWidth = ltWidth + bt.offsetWidth;
37238         var vscroll = false, hscroll = false;
37239
37240         this.scrollSizer.setSize(scrollWidth, scrollHeight+hdHeight);
37241
37242         var lw = this.lockedWrap, mw = this.mainWrap;
37243         var lb = this.lockedBody, mb = this.mainBody;
37244
37245         setTimeout(function(){
37246             var t = s.dom.offsetTop;
37247             var w = s.dom.clientWidth,
37248                 h = s.dom.clientHeight;
37249
37250             lw.setTop(t);
37251             lw.setSize(ltWidth, h);
37252
37253             mw.setLeftTop(ltWidth, t);
37254             mw.setSize(w-ltWidth, h);
37255
37256             lb.setHeight(h-hdHeight);
37257             mb.setHeight(h-hdHeight);
37258
37259             if(is2ndPass !== true && !gv.userResized && expandCol){
37260                 // high speed resize without full column calculation
37261                 
37262                 var ci = cm.getIndexById(expandCol);
37263                 if (ci < 0) {
37264                     ci = cm.findColumnIndex(expandCol);
37265                 }
37266                 ci = Math.max(0, ci); // make sure it's got at least the first col.
37267                 var expandId = cm.getColumnId(ci);
37268                 var  tw = cm.getTotalWidth(false);
37269                 var currentWidth = cm.getColumnWidth(ci);
37270                 var cw = Math.min(Math.max(((w-tw)+currentWidth-2)-/*scrollbar*/(w <= s.dom.offsetWidth ? 0 : 18), g.autoExpandMin), g.autoExpandMax);
37271                 if(currentWidth != cw){
37272                     cm.setColumnWidth(ci, cw, true);
37273                     gv.css.updateRule(gv.colSelector+gv.idToCssName(expandId), "width", (cw - gv.borderWidth) + "px");
37274                     gv.css.updateRule(gv.hdSelector+gv.idToCssName(expandId), "width", (cw - gv.borderWidth) + "px");
37275                     gv.updateSplitters();
37276                     gv.layout(false, true);
37277                 }
37278             }
37279
37280             if(initialRender){
37281                 lw.show();
37282                 mw.show();
37283             }
37284             //c.endMeasure();
37285         }, 10);
37286     },
37287
37288     onWindowResize : function(){
37289         if(!this.grid.monitorWindowResize || this.grid.autoHeight){
37290             return;
37291         }
37292         this.layout();
37293     },
37294
37295     appendFooter : function(parentEl){
37296         return null;
37297     },
37298
37299     sortAscText : "Sort Ascending",
37300     sortDescText : "Sort Descending",
37301     lockText : "Lock Column",
37302     unlockText : "Unlock Column",
37303     columnsText : "Columns"
37304 });
37305
37306
37307 Roo.grid.GridView.ColumnDragZone = function(grid, hd){
37308     Roo.grid.GridView.ColumnDragZone.superclass.constructor.call(this, grid, hd, null);
37309     this.proxy.el.addClass('x-grid3-col-dd');
37310 };
37311
37312 Roo.extend(Roo.grid.GridView.ColumnDragZone, Roo.grid.HeaderDragZone, {
37313     handleMouseDown : function(e){
37314
37315     },
37316
37317     callHandleMouseDown : function(e){
37318         Roo.grid.GridView.ColumnDragZone.superclass.handleMouseDown.call(this, e);
37319     }
37320 });
37321 /*
37322  * Based on:
37323  * Ext JS Library 1.1.1
37324  * Copyright(c) 2006-2007, Ext JS, LLC.
37325  *
37326  * Originally Released Under LGPL - original licence link has changed is not relivant.
37327  *
37328  * Fork - LGPL
37329  * <script type="text/javascript">
37330  */
37331  
37332 // private
37333 // This is a support class used internally by the Grid components
37334 Roo.grid.SplitDragZone = function(grid, hd, hd2){
37335     this.grid = grid;
37336     this.view = grid.getView();
37337     this.proxy = this.view.resizeProxy;
37338     Roo.grid.SplitDragZone.superclass.constructor.call(this, hd,
37339         "gridSplitters" + this.grid.getGridEl().id, {
37340         dragElId : Roo.id(this.proxy.dom), resizeFrame:false
37341     });
37342     this.setHandleElId(Roo.id(hd));
37343     this.setOuterHandleElId(Roo.id(hd2));
37344     this.scroll = false;
37345 };
37346 Roo.extend(Roo.grid.SplitDragZone, Roo.dd.DDProxy, {
37347     fly: Roo.Element.fly,
37348
37349     b4StartDrag : function(x, y){
37350         this.view.headersDisabled = true;
37351         this.proxy.setHeight(this.view.mainWrap.getHeight());
37352         var w = this.cm.getColumnWidth(this.cellIndex);
37353         var minw = Math.max(w-this.grid.minColumnWidth, 0);
37354         this.resetConstraints();
37355         this.setXConstraint(minw, 1000);
37356         this.setYConstraint(0, 0);
37357         this.minX = x - minw;
37358         this.maxX = x + 1000;
37359         this.startPos = x;
37360         Roo.dd.DDProxy.prototype.b4StartDrag.call(this, x, y);
37361     },
37362
37363
37364     handleMouseDown : function(e){
37365         ev = Roo.EventObject.setEvent(e);
37366         var t = this.fly(ev.getTarget());
37367         if(t.hasClass("x-grid-split")){
37368             this.cellIndex = this.view.getCellIndex(t.dom);
37369             this.split = t.dom;
37370             this.cm = this.grid.colModel;
37371             if(this.cm.isResizable(this.cellIndex) && !this.cm.isFixed(this.cellIndex)){
37372                 Roo.grid.SplitDragZone.superclass.handleMouseDown.apply(this, arguments);
37373             }
37374         }
37375     },
37376
37377     endDrag : function(e){
37378         this.view.headersDisabled = false;
37379         var endX = Math.max(this.minX, Roo.lib.Event.getPageX(e));
37380         var diff = endX - this.startPos;
37381         this.view.onColumnSplitterMoved(this.cellIndex, this.cm.getColumnWidth(this.cellIndex)+diff);
37382     },
37383
37384     autoOffset : function(){
37385         this.setDelta(0,0);
37386     }
37387 });/*
37388  * Based on:
37389  * Ext JS Library 1.1.1
37390  * Copyright(c) 2006-2007, Ext JS, LLC.
37391  *
37392  * Originally Released Under LGPL - original licence link has changed is not relivant.
37393  *
37394  * Fork - LGPL
37395  * <script type="text/javascript">
37396  */
37397  
37398 // private
37399 // This is a support class used internally by the Grid components
37400 Roo.grid.GridDragZone = function(grid, config){
37401     this.view = grid.getView();
37402     Roo.grid.GridDragZone.superclass.constructor.call(this, this.view.mainBody.dom, config);
37403     if(this.view.lockedBody){
37404         this.setHandleElId(Roo.id(this.view.mainBody.dom));
37405         this.setOuterHandleElId(Roo.id(this.view.lockedBody.dom));
37406     }
37407     this.scroll = false;
37408     this.grid = grid;
37409     this.ddel = document.createElement('div');
37410     this.ddel.className = 'x-grid-dd-wrap';
37411 };
37412
37413 Roo.extend(Roo.grid.GridDragZone, Roo.dd.DragZone, {
37414     ddGroup : "GridDD",
37415
37416     getDragData : function(e){
37417         var t = Roo.lib.Event.getTarget(e);
37418         var rowIndex = this.view.findRowIndex(t);
37419         var sm = this.grid.selModel;
37420             
37421         //Roo.log(rowIndex);
37422         
37423         if (sm.getSelectedCell) {
37424             // cell selection..
37425             if (!sm.getSelectedCell()) {
37426                 return false;
37427             }
37428             if (rowIndex != sm.getSelectedCell()[0]) {
37429                 return false;
37430             }
37431         
37432         }
37433         
37434         if(rowIndex !== false){
37435             
37436             // if editorgrid.. 
37437             
37438             
37439             //Roo.log([ sm.getSelectedCell() ? sm.getSelectedCell()[0] : 'NO' , rowIndex ]);
37440                
37441             //if(!sm.isSelected(rowIndex) || e.hasModifier()){
37442               //  
37443             //}
37444             if (e.hasModifier()){
37445                 sm.handleMouseDown(e, t); // non modifier buttons are handled by row select.
37446             }
37447             
37448             Roo.log("getDragData");
37449             
37450             return {
37451                 grid: this.grid,
37452                 ddel: this.ddel,
37453                 rowIndex: rowIndex,
37454                 selections:sm.getSelections ? sm.getSelections() : (
37455                     sm.getSelectedCell() ? [ this.grid.ds.getAt(sm.getSelectedCell()[0]) ] : []
37456                 )
37457             };
37458         }
37459         return false;
37460     },
37461
37462     onInitDrag : function(e){
37463         var data = this.dragData;
37464         this.ddel.innerHTML = this.grid.getDragDropText();
37465         this.proxy.update(this.ddel);
37466         // fire start drag?
37467     },
37468
37469     afterRepair : function(){
37470         this.dragging = false;
37471     },
37472
37473     getRepairXY : function(e, data){
37474         return false;
37475     },
37476
37477     onEndDrag : function(data, e){
37478         // fire end drag?
37479     },
37480
37481     onValidDrop : function(dd, e, id){
37482         // fire drag drop?
37483         this.hideProxy();
37484     },
37485
37486     beforeInvalidDrop : function(e, id){
37487
37488     }
37489 });/*
37490  * Based on:
37491  * Ext JS Library 1.1.1
37492  * Copyright(c) 2006-2007, Ext JS, LLC.
37493  *
37494  * Originally Released Under LGPL - original licence link has changed is not relivant.
37495  *
37496  * Fork - LGPL
37497  * <script type="text/javascript">
37498  */
37499  
37500
37501 /**
37502  * @class Roo.grid.ColumnModel
37503  * @extends Roo.util.Observable
37504  * This is the default implementation of a ColumnModel used by the Grid. It defines
37505  * the columns in the grid.
37506  * <br>Usage:<br>
37507  <pre><code>
37508  var colModel = new Roo.grid.ColumnModel([
37509         {header: "Ticker", width: 60, sortable: true, locked: true},
37510         {header: "Company Name", width: 150, sortable: true},
37511         {header: "Market Cap.", width: 100, sortable: true},
37512         {header: "$ Sales", width: 100, sortable: true, renderer: money},
37513         {header: "Employees", width: 100, sortable: true, resizable: false}
37514  ]);
37515  </code></pre>
37516  * <p>
37517  
37518  * The config options listed for this class are options which may appear in each
37519  * individual column definition.
37520  * <br/>RooJS Fix - column id's are not sequential but use Roo.id() - fixes bugs with layouts.
37521  * @constructor
37522  * @param {Object} config An Array of column config objects. See this class's
37523  * config objects for details.
37524 */
37525 Roo.grid.ColumnModel = function(config){
37526         /**
37527      * The config passed into the constructor
37528      */
37529     this.config = config;
37530     this.lookup = {};
37531
37532     // if no id, create one
37533     // if the column does not have a dataIndex mapping,
37534     // map it to the order it is in the config
37535     for(var i = 0, len = config.length; i < len; i++){
37536         var c = config[i];
37537         if(typeof c.dataIndex == "undefined"){
37538             c.dataIndex = i;
37539         }
37540         if(typeof c.renderer == "string"){
37541             c.renderer = Roo.util.Format[c.renderer];
37542         }
37543         if(typeof c.id == "undefined"){
37544             c.id = Roo.id();
37545         }
37546         if(c.editor && c.editor.xtype){
37547             c.editor  = Roo.factory(c.editor, Roo.grid);
37548         }
37549         if(c.editor && c.editor.isFormField){
37550             c.editor = new Roo.grid.GridEditor(c.editor);
37551         }
37552         this.lookup[c.id] = c;
37553     }
37554
37555     /**
37556      * The width of columns which have no width specified (defaults to 100)
37557      * @type Number
37558      */
37559     this.defaultWidth = 100;
37560
37561     /**
37562      * Default sortable of columns which have no sortable specified (defaults to false)
37563      * @type Boolean
37564      */
37565     this.defaultSortable = false;
37566
37567     this.addEvents({
37568         /**
37569              * @event widthchange
37570              * Fires when the width of a column changes.
37571              * @param {ColumnModel} this
37572              * @param {Number} columnIndex The column index
37573              * @param {Number} newWidth The new width
37574              */
37575             "widthchange": true,
37576         /**
37577              * @event headerchange
37578              * Fires when the text of a header changes.
37579              * @param {ColumnModel} this
37580              * @param {Number} columnIndex The column index
37581              * @param {Number} newText The new header text
37582              */
37583             "headerchange": true,
37584         /**
37585              * @event hiddenchange
37586              * Fires when a column is hidden or "unhidden".
37587              * @param {ColumnModel} this
37588              * @param {Number} columnIndex The column index
37589              * @param {Boolean} hidden true if hidden, false otherwise
37590              */
37591             "hiddenchange": true,
37592             /**
37593          * @event columnmoved
37594          * Fires when a column is moved.
37595          * @param {ColumnModel} this
37596          * @param {Number} oldIndex
37597          * @param {Number} newIndex
37598          */
37599         "columnmoved" : true,
37600         /**
37601          * @event columlockchange
37602          * Fires when a column's locked state is changed
37603          * @param {ColumnModel} this
37604          * @param {Number} colIndex
37605          * @param {Boolean} locked true if locked
37606          */
37607         "columnlockchange" : true
37608     });
37609     Roo.grid.ColumnModel.superclass.constructor.call(this);
37610 };
37611 Roo.extend(Roo.grid.ColumnModel, Roo.util.Observable, {
37612     /**
37613      * @cfg {String} header The header text to display in the Grid view.
37614      */
37615     /**
37616      * @cfg {String} dataIndex (Optional) The name of the field in the grid's {@link Roo.data.Store}'s
37617      * {@link Roo.data.Record} definition from which to draw the column's value. If not
37618      * specified, the column's index is used as an index into the Record's data Array.
37619      */
37620     /**
37621      * @cfg {Number} width (Optional) The initial width in pixels of the column. Using this
37622      * instead of {@link Roo.grid.Grid#autoSizeColumns} is more efficient.
37623      */
37624     /**
37625      * @cfg {Boolean} sortable (Optional) True if sorting is to be allowed on this column.
37626      * Defaults to the value of the {@link #defaultSortable} property.
37627      * Whether local/remote sorting is used is specified in {@link Roo.data.Store#remoteSort}.
37628      */
37629     /**
37630      * @cfg {Boolean} locked (Optional) True to lock the column in place while scrolling the Grid.  Defaults to false.
37631      */
37632     /**
37633      * @cfg {Boolean} fixed (Optional) True if the column width cannot be changed.  Defaults to false.
37634      */
37635     /**
37636      * @cfg {Boolean} resizable (Optional) False to disable column resizing. Defaults to true.
37637      */
37638     /**
37639      * @cfg {Boolean} hidden (Optional) True to hide the column. Defaults to false.
37640      */
37641     /**
37642      * @cfg {Function} renderer (Optional) A function used to generate HTML markup for a cell
37643      * given the cell's data value. See {@link #setRenderer}. If not specified, the
37644      * default renderer uses the raw data value.
37645      */
37646        /**
37647      * @cfg {Roo.grid.GridEditor} editor (Optional) For grid editors - returns the grid editor 
37648      */
37649     /**
37650      * @cfg {String} align (Optional) Set the CSS text-align property of the column.  Defaults to undefined.
37651      */
37652
37653     /**
37654      * Returns the id of the column at the specified index.
37655      * @param {Number} index The column index
37656      * @return {String} the id
37657      */
37658     getColumnId : function(index){
37659         return this.config[index].id;
37660     },
37661
37662     /**
37663      * Returns the column for a specified id.
37664      * @param {String} id The column id
37665      * @return {Object} the column
37666      */
37667     getColumnById : function(id){
37668         return this.lookup[id];
37669     },
37670
37671     
37672     /**
37673      * Returns the column for a specified dataIndex.
37674      * @param {String} dataIndex The column dataIndex
37675      * @return {Object|Boolean} the column or false if not found
37676      */
37677     getColumnByDataIndex: function(dataIndex){
37678         var index = this.findColumnIndex(dataIndex);
37679         return index > -1 ? this.config[index] : false;
37680     },
37681     
37682     /**
37683      * Returns the index for a specified column id.
37684      * @param {String} id The column id
37685      * @return {Number} the index, or -1 if not found
37686      */
37687     getIndexById : function(id){
37688         for(var i = 0, len = this.config.length; i < len; i++){
37689             if(this.config[i].id == id){
37690                 return i;
37691             }
37692         }
37693         return -1;
37694     },
37695     
37696     /**
37697      * Returns the index for a specified column dataIndex.
37698      * @param {String} dataIndex The column dataIndex
37699      * @return {Number} the index, or -1 if not found
37700      */
37701     
37702     findColumnIndex : function(dataIndex){
37703         for(var i = 0, len = this.config.length; i < len; i++){
37704             if(this.config[i].dataIndex == dataIndex){
37705                 return i;
37706             }
37707         }
37708         return -1;
37709     },
37710     
37711     
37712     moveColumn : function(oldIndex, newIndex){
37713         var c = this.config[oldIndex];
37714         this.config.splice(oldIndex, 1);
37715         this.config.splice(newIndex, 0, c);
37716         this.dataMap = null;
37717         this.fireEvent("columnmoved", this, oldIndex, newIndex);
37718     },
37719
37720     isLocked : function(colIndex){
37721         return this.config[colIndex].locked === true;
37722     },
37723
37724     setLocked : function(colIndex, value, suppressEvent){
37725         if(this.isLocked(colIndex) == value){
37726             return;
37727         }
37728         this.config[colIndex].locked = value;
37729         if(!suppressEvent){
37730             this.fireEvent("columnlockchange", this, colIndex, value);
37731         }
37732     },
37733
37734     getTotalLockedWidth : function(){
37735         var totalWidth = 0;
37736         for(var i = 0; i < this.config.length; i++){
37737             if(this.isLocked(i) && !this.isHidden(i)){
37738                 this.totalWidth += this.getColumnWidth(i);
37739             }
37740         }
37741         return totalWidth;
37742     },
37743
37744     getLockedCount : function(){
37745         for(var i = 0, len = this.config.length; i < len; i++){
37746             if(!this.isLocked(i)){
37747                 return i;
37748             }
37749         }
37750     },
37751
37752     /**
37753      * Returns the number of columns.
37754      * @return {Number}
37755      */
37756     getColumnCount : function(visibleOnly){
37757         if(visibleOnly === true){
37758             var c = 0;
37759             for(var i = 0, len = this.config.length; i < len; i++){
37760                 if(!this.isHidden(i)){
37761                     c++;
37762                 }
37763             }
37764             return c;
37765         }
37766         return this.config.length;
37767     },
37768
37769     /**
37770      * Returns the column configs that return true by the passed function that is called with (columnConfig, index)
37771      * @param {Function} fn
37772      * @param {Object} scope (optional)
37773      * @return {Array} result
37774      */
37775     getColumnsBy : function(fn, scope){
37776         var r = [];
37777         for(var i = 0, len = this.config.length; i < len; i++){
37778             var c = this.config[i];
37779             if(fn.call(scope||this, c, i) === true){
37780                 r[r.length] = c;
37781             }
37782         }
37783         return r;
37784     },
37785
37786     /**
37787      * Returns true if the specified column is sortable.
37788      * @param {Number} col The column index
37789      * @return {Boolean}
37790      */
37791     isSortable : function(col){
37792         if(typeof this.config[col].sortable == "undefined"){
37793             return this.defaultSortable;
37794         }
37795         return this.config[col].sortable;
37796     },
37797
37798     /**
37799      * Returns the rendering (formatting) function defined for the column.
37800      * @param {Number} col The column index.
37801      * @return {Function} The function used to render the cell. See {@link #setRenderer}.
37802      */
37803     getRenderer : function(col){
37804         if(!this.config[col].renderer){
37805             return Roo.grid.ColumnModel.defaultRenderer;
37806         }
37807         return this.config[col].renderer;
37808     },
37809
37810     /**
37811      * Sets the rendering (formatting) function for a column.
37812      * @param {Number} col The column index
37813      * @param {Function} fn The function to use to process the cell's raw data
37814      * to return HTML markup for the grid view. The render function is called with
37815      * the following parameters:<ul>
37816      * <li>Data value.</li>
37817      * <li>Cell metadata. An object in which you may set the following attributes:<ul>
37818      * <li>css A CSS style string to apply to the table cell.</li>
37819      * <li>attr An HTML attribute definition string to apply to the data container element <i>within</i> the table cell.</li></ul>
37820      * <li>The {@link Roo.data.Record} from which the data was extracted.</li>
37821      * <li>Row index</li>
37822      * <li>Column index</li>
37823      * <li>The {@link Roo.data.Store} object from which the Record was extracted</li></ul>
37824      */
37825     setRenderer : function(col, fn){
37826         this.config[col].renderer = fn;
37827     },
37828
37829     /**
37830      * Returns the width for the specified column.
37831      * @param {Number} col The column index
37832      * @return {Number}
37833      */
37834     getColumnWidth : function(col){
37835         return this.config[col].width * 1 || this.defaultWidth;
37836     },
37837
37838     /**
37839      * Sets the width for a column.
37840      * @param {Number} col The column index
37841      * @param {Number} width The new width
37842      */
37843     setColumnWidth : function(col, width, suppressEvent){
37844         this.config[col].width = width;
37845         this.totalWidth = null;
37846         if(!suppressEvent){
37847              this.fireEvent("widthchange", this, col, width);
37848         }
37849     },
37850
37851     /**
37852      * Returns the total width of all columns.
37853      * @param {Boolean} includeHidden True to include hidden column widths
37854      * @return {Number}
37855      */
37856     getTotalWidth : function(includeHidden){
37857         if(!this.totalWidth){
37858             this.totalWidth = 0;
37859             for(var i = 0, len = this.config.length; i < len; i++){
37860                 if(includeHidden || !this.isHidden(i)){
37861                     this.totalWidth += this.getColumnWidth(i);
37862                 }
37863             }
37864         }
37865         return this.totalWidth;
37866     },
37867
37868     /**
37869      * Returns the header for the specified column.
37870      * @param {Number} col The column index
37871      * @return {String}
37872      */
37873     getColumnHeader : function(col){
37874         return this.config[col].header;
37875     },
37876
37877     /**
37878      * Sets the header for a column.
37879      * @param {Number} col The column index
37880      * @param {String} header The new header
37881      */
37882     setColumnHeader : function(col, header){
37883         this.config[col].header = header;
37884         this.fireEvent("headerchange", this, col, header);
37885     },
37886
37887     /**
37888      * Returns the tooltip for the specified column.
37889      * @param {Number} col The column index
37890      * @return {String}
37891      */
37892     getColumnTooltip : function(col){
37893             return this.config[col].tooltip;
37894     },
37895     /**
37896      * Sets the tooltip for a column.
37897      * @param {Number} col The column index
37898      * @param {String} tooltip The new tooltip
37899      */
37900     setColumnTooltip : function(col, tooltip){
37901             this.config[col].tooltip = tooltip;
37902     },
37903
37904     /**
37905      * Returns the dataIndex for the specified column.
37906      * @param {Number} col The column index
37907      * @return {Number}
37908      */
37909     getDataIndex : function(col){
37910         return this.config[col].dataIndex;
37911     },
37912
37913     /**
37914      * Sets the dataIndex for a column.
37915      * @param {Number} col The column index
37916      * @param {Number} dataIndex The new dataIndex
37917      */
37918     setDataIndex : function(col, dataIndex){
37919         this.config[col].dataIndex = dataIndex;
37920     },
37921
37922     
37923     
37924     /**
37925      * Returns true if the cell is editable.
37926      * @param {Number} colIndex The column index
37927      * @param {Number} rowIndex The row index
37928      * @return {Boolean}
37929      */
37930     isCellEditable : function(colIndex, rowIndex){
37931         return (this.config[colIndex].editable || (typeof this.config[colIndex].editable == "undefined" && this.config[colIndex].editor)) ? true : false;
37932     },
37933
37934     /**
37935      * Returns the editor defined for the cell/column.
37936      * return false or null to disable editing.
37937      * @param {Number} colIndex The column index
37938      * @param {Number} rowIndex The row index
37939      * @return {Object}
37940      */
37941     getCellEditor : function(colIndex, rowIndex){
37942         return this.config[colIndex].editor;
37943     },
37944
37945     /**
37946      * Sets if a column is editable.
37947      * @param {Number} col The column index
37948      * @param {Boolean} editable True if the column is editable
37949      */
37950     setEditable : function(col, editable){
37951         this.config[col].editable = editable;
37952     },
37953
37954
37955     /**
37956      * Returns true if the column is hidden.
37957      * @param {Number} colIndex The column index
37958      * @return {Boolean}
37959      */
37960     isHidden : function(colIndex){
37961         return this.config[colIndex].hidden;
37962     },
37963
37964
37965     /**
37966      * Returns true if the column width cannot be changed
37967      */
37968     isFixed : function(colIndex){
37969         return this.config[colIndex].fixed;
37970     },
37971
37972     /**
37973      * Returns true if the column can be resized
37974      * @return {Boolean}
37975      */
37976     isResizable : function(colIndex){
37977         return colIndex >= 0 && this.config[colIndex].resizable !== false && this.config[colIndex].fixed !== true;
37978     },
37979     /**
37980      * Sets if a column is hidden.
37981      * @param {Number} colIndex The column index
37982      * @param {Boolean} hidden True if the column is hidden
37983      */
37984     setHidden : function(colIndex, hidden){
37985         this.config[colIndex].hidden = hidden;
37986         this.totalWidth = null;
37987         this.fireEvent("hiddenchange", this, colIndex, hidden);
37988     },
37989
37990     /**
37991      * Sets the editor for a column.
37992      * @param {Number} col The column index
37993      * @param {Object} editor The editor object
37994      */
37995     setEditor : function(col, editor){
37996         this.config[col].editor = editor;
37997     }
37998 });
37999
38000 Roo.grid.ColumnModel.defaultRenderer = function(value){
38001         if(typeof value == "string" && value.length < 1){
38002             return "&#160;";
38003         }
38004         return value;
38005 };
38006
38007 // Alias for backwards compatibility
38008 Roo.grid.DefaultColumnModel = Roo.grid.ColumnModel;
38009 /*
38010  * Based on:
38011  * Ext JS Library 1.1.1
38012  * Copyright(c) 2006-2007, Ext JS, LLC.
38013  *
38014  * Originally Released Under LGPL - original licence link has changed is not relivant.
38015  *
38016  * Fork - LGPL
38017  * <script type="text/javascript">
38018  */
38019
38020 /**
38021  * @class Roo.grid.AbstractSelectionModel
38022  * @extends Roo.util.Observable
38023  * Abstract base class for grid SelectionModels.  It provides the interface that should be
38024  * implemented by descendant classes.  This class should not be directly instantiated.
38025  * @constructor
38026  */
38027 Roo.grid.AbstractSelectionModel = function(){
38028     this.locked = false;
38029     Roo.grid.AbstractSelectionModel.superclass.constructor.call(this);
38030 };
38031
38032 Roo.extend(Roo.grid.AbstractSelectionModel, Roo.util.Observable,  {
38033     /** @ignore Called by the grid automatically. Do not call directly. */
38034     init : function(grid){
38035         this.grid = grid;
38036         this.initEvents();
38037     },
38038
38039     /**
38040      * Locks the selections.
38041      */
38042     lock : function(){
38043         this.locked = true;
38044     },
38045
38046     /**
38047      * Unlocks the selections.
38048      */
38049     unlock : function(){
38050         this.locked = false;
38051     },
38052
38053     /**
38054      * Returns true if the selections are locked.
38055      * @return {Boolean}
38056      */
38057     isLocked : function(){
38058         return this.locked;
38059     }
38060 });/*
38061  * Based on:
38062  * Ext JS Library 1.1.1
38063  * Copyright(c) 2006-2007, Ext JS, LLC.
38064  *
38065  * Originally Released Under LGPL - original licence link has changed is not relivant.
38066  *
38067  * Fork - LGPL
38068  * <script type="text/javascript">
38069  */
38070 /**
38071  * @extends Roo.grid.AbstractSelectionModel
38072  * @class Roo.grid.RowSelectionModel
38073  * The default SelectionModel used by {@link Roo.grid.Grid}.
38074  * It supports multiple selections and keyboard selection/navigation. 
38075  * @constructor
38076  * @param {Object} config
38077  */
38078 Roo.grid.RowSelectionModel = function(config){
38079     Roo.apply(this, config);
38080     this.selections = new Roo.util.MixedCollection(false, function(o){
38081         return o.id;
38082     });
38083
38084     this.last = false;
38085     this.lastActive = false;
38086
38087     this.addEvents({
38088         /**
38089              * @event selectionchange
38090              * Fires when the selection changes
38091              * @param {SelectionModel} this
38092              */
38093             "selectionchange" : true,
38094         /**
38095              * @event afterselectionchange
38096              * Fires after the selection changes (eg. by key press or clicking)
38097              * @param {SelectionModel} this
38098              */
38099             "afterselectionchange" : true,
38100         /**
38101              * @event beforerowselect
38102              * Fires when a row is selected being selected, return false to cancel.
38103              * @param {SelectionModel} this
38104              * @param {Number} rowIndex The selected index
38105              * @param {Boolean} keepExisting False if other selections will be cleared
38106              */
38107             "beforerowselect" : true,
38108         /**
38109              * @event rowselect
38110              * Fires when a row is selected.
38111              * @param {SelectionModel} this
38112              * @param {Number} rowIndex The selected index
38113              * @param {Roo.data.Record} r The record
38114              */
38115             "rowselect" : true,
38116         /**
38117              * @event rowdeselect
38118              * Fires when a row is deselected.
38119              * @param {SelectionModel} this
38120              * @param {Number} rowIndex The selected index
38121              */
38122         "rowdeselect" : true
38123     });
38124     Roo.grid.RowSelectionModel.superclass.constructor.call(this);
38125     this.locked = false;
38126 };
38127
38128 Roo.extend(Roo.grid.RowSelectionModel, Roo.grid.AbstractSelectionModel,  {
38129     /**
38130      * @cfg {Boolean} singleSelect
38131      * True to allow selection of only one row at a time (defaults to false)
38132      */
38133     singleSelect : false,
38134
38135     // private
38136     initEvents : function(){
38137
38138         if(!this.grid.enableDragDrop && !this.grid.enableDrag){
38139             this.grid.on("mousedown", this.handleMouseDown, this);
38140         }else{ // allow click to work like normal
38141             this.grid.on("rowclick", this.handleDragableRowClick, this);
38142         }
38143
38144         this.rowNav = new Roo.KeyNav(this.grid.getGridEl(), {
38145             "up" : function(e){
38146                 if(!e.shiftKey){
38147                     this.selectPrevious(e.shiftKey);
38148                 }else if(this.last !== false && this.lastActive !== false){
38149                     var last = this.last;
38150                     this.selectRange(this.last,  this.lastActive-1);
38151                     this.grid.getView().focusRow(this.lastActive);
38152                     if(last !== false){
38153                         this.last = last;
38154                     }
38155                 }else{
38156                     this.selectFirstRow();
38157                 }
38158                 this.fireEvent("afterselectionchange", this);
38159             },
38160             "down" : function(e){
38161                 if(!e.shiftKey){
38162                     this.selectNext(e.shiftKey);
38163                 }else if(this.last !== false && this.lastActive !== false){
38164                     var last = this.last;
38165                     this.selectRange(this.last,  this.lastActive+1);
38166                     this.grid.getView().focusRow(this.lastActive);
38167                     if(last !== false){
38168                         this.last = last;
38169                     }
38170                 }else{
38171                     this.selectFirstRow();
38172                 }
38173                 this.fireEvent("afterselectionchange", this);
38174             },
38175             scope: this
38176         });
38177
38178         var view = this.grid.view;
38179         view.on("refresh", this.onRefresh, this);
38180         view.on("rowupdated", this.onRowUpdated, this);
38181         view.on("rowremoved", this.onRemove, this);
38182     },
38183
38184     // private
38185     onRefresh : function(){
38186         var ds = this.grid.dataSource, i, v = this.grid.view;
38187         var s = this.selections;
38188         s.each(function(r){
38189             if((i = ds.indexOfId(r.id)) != -1){
38190                 v.onRowSelect(i);
38191             }else{
38192                 s.remove(r);
38193             }
38194         });
38195     },
38196
38197     // private
38198     onRemove : function(v, index, r){
38199         this.selections.remove(r);
38200     },
38201
38202     // private
38203     onRowUpdated : function(v, index, r){
38204         if(this.isSelected(r)){
38205             v.onRowSelect(index);
38206         }
38207     },
38208
38209     /**
38210      * Select records.
38211      * @param {Array} records The records to select
38212      * @param {Boolean} keepExisting (optional) True to keep existing selections
38213      */
38214     selectRecords : function(records, keepExisting){
38215         if(!keepExisting){
38216             this.clearSelections();
38217         }
38218         var ds = this.grid.dataSource;
38219         for(var i = 0, len = records.length; i < len; i++){
38220             this.selectRow(ds.indexOf(records[i]), true);
38221         }
38222     },
38223
38224     /**
38225      * Gets the number of selected rows.
38226      * @return {Number}
38227      */
38228     getCount : function(){
38229         return this.selections.length;
38230     },
38231
38232     /**
38233      * Selects the first row in the grid.
38234      */
38235     selectFirstRow : function(){
38236         this.selectRow(0);
38237     },
38238
38239     /**
38240      * Select the last row.
38241      * @param {Boolean} keepExisting (optional) True to keep existing selections
38242      */
38243     selectLastRow : function(keepExisting){
38244         this.selectRow(this.grid.dataSource.getCount() - 1, keepExisting);
38245     },
38246
38247     /**
38248      * Selects the row immediately following the last selected row.
38249      * @param {Boolean} keepExisting (optional) True to keep existing selections
38250      */
38251     selectNext : function(keepExisting){
38252         if(this.last !== false && (this.last+1) < this.grid.dataSource.getCount()){
38253             this.selectRow(this.last+1, keepExisting);
38254             this.grid.getView().focusRow(this.last);
38255         }
38256     },
38257
38258     /**
38259      * Selects the row that precedes the last selected row.
38260      * @param {Boolean} keepExisting (optional) True to keep existing selections
38261      */
38262     selectPrevious : function(keepExisting){
38263         if(this.last){
38264             this.selectRow(this.last-1, keepExisting);
38265             this.grid.getView().focusRow(this.last);
38266         }
38267     },
38268
38269     /**
38270      * Returns the selected records
38271      * @return {Array} Array of selected records
38272      */
38273     getSelections : function(){
38274         return [].concat(this.selections.items);
38275     },
38276
38277     /**
38278      * Returns the first selected record.
38279      * @return {Record}
38280      */
38281     getSelected : function(){
38282         return this.selections.itemAt(0);
38283     },
38284
38285
38286     /**
38287      * Clears all selections.
38288      */
38289     clearSelections : function(fast){
38290         if(this.locked) return;
38291         if(fast !== true){
38292             var ds = this.grid.dataSource;
38293             var s = this.selections;
38294             s.each(function(r){
38295                 this.deselectRow(ds.indexOfId(r.id));
38296             }, this);
38297             s.clear();
38298         }else{
38299             this.selections.clear();
38300         }
38301         this.last = false;
38302     },
38303
38304
38305     /**
38306      * Selects all rows.
38307      */
38308     selectAll : function(){
38309         if(this.locked) return;
38310         this.selections.clear();
38311         for(var i = 0, len = this.grid.dataSource.getCount(); i < len; i++){
38312             this.selectRow(i, true);
38313         }
38314     },
38315
38316     /**
38317      * Returns True if there is a selection.
38318      * @return {Boolean}
38319      */
38320     hasSelection : function(){
38321         return this.selections.length > 0;
38322     },
38323
38324     /**
38325      * Returns True if the specified row is selected.
38326      * @param {Number/Record} record The record or index of the record to check
38327      * @return {Boolean}
38328      */
38329     isSelected : function(index){
38330         var r = typeof index == "number" ? this.grid.dataSource.getAt(index) : index;
38331         return (r && this.selections.key(r.id) ? true : false);
38332     },
38333
38334     /**
38335      * Returns True if the specified record id is selected.
38336      * @param {String} id The id of record to check
38337      * @return {Boolean}
38338      */
38339     isIdSelected : function(id){
38340         return (this.selections.key(id) ? true : false);
38341     },
38342
38343     // private
38344     handleMouseDown : function(e, t){
38345         var view = this.grid.getView(), rowIndex;
38346         if(this.isLocked() || (rowIndex = view.findRowIndex(t)) === false){
38347             return;
38348         };
38349         if(e.shiftKey && this.last !== false){
38350             var last = this.last;
38351             this.selectRange(last, rowIndex, e.ctrlKey);
38352             this.last = last; // reset the last
38353             view.focusRow(rowIndex);
38354         }else{
38355             var isSelected = this.isSelected(rowIndex);
38356             if(e.button !== 0 && isSelected){
38357                 view.focusRow(rowIndex);
38358             }else if(e.ctrlKey && isSelected){
38359                 this.deselectRow(rowIndex);
38360             }else if(!isSelected){
38361                 this.selectRow(rowIndex, e.button === 0 && (e.ctrlKey || e.shiftKey));
38362                 view.focusRow(rowIndex);
38363             }
38364         }
38365         this.fireEvent("afterselectionchange", this);
38366     },
38367     // private
38368     handleDragableRowClick :  function(grid, rowIndex, e) 
38369     {
38370         if(e.button === 0 && !e.shiftKey && !e.ctrlKey) {
38371             this.selectRow(rowIndex, false);
38372             grid.view.focusRow(rowIndex);
38373              this.fireEvent("afterselectionchange", this);
38374         }
38375     },
38376     
38377     /**
38378      * Selects multiple rows.
38379      * @param {Array} rows Array of the indexes of the row to select
38380      * @param {Boolean} keepExisting (optional) True to keep existing selections
38381      */
38382     selectRows : function(rows, keepExisting){
38383         if(!keepExisting){
38384             this.clearSelections();
38385         }
38386         for(var i = 0, len = rows.length; i < len; i++){
38387             this.selectRow(rows[i], true);
38388         }
38389     },
38390
38391     /**
38392      * Selects a range of rows. All rows in between startRow and endRow are also selected.
38393      * @param {Number} startRow The index of the first row in the range
38394      * @param {Number} endRow The index of the last row in the range
38395      * @param {Boolean} keepExisting (optional) True to retain existing selections
38396      */
38397     selectRange : function(startRow, endRow, keepExisting){
38398         if(this.locked) return;
38399         if(!keepExisting){
38400             this.clearSelections();
38401         }
38402         if(startRow <= endRow){
38403             for(var i = startRow; i <= endRow; i++){
38404                 this.selectRow(i, true);
38405             }
38406         }else{
38407             for(var i = startRow; i >= endRow; i--){
38408                 this.selectRow(i, true);
38409             }
38410         }
38411     },
38412
38413     /**
38414      * Deselects a range of rows. All rows in between startRow and endRow are also deselected.
38415      * @param {Number} startRow The index of the first row in the range
38416      * @param {Number} endRow The index of the last row in the range
38417      */
38418     deselectRange : function(startRow, endRow, preventViewNotify){
38419         if(this.locked) return;
38420         for(var i = startRow; i <= endRow; i++){
38421             this.deselectRow(i, preventViewNotify);
38422         }
38423     },
38424
38425     /**
38426      * Selects a row.
38427      * @param {Number} row The index of the row to select
38428      * @param {Boolean} keepExisting (optional) True to keep existing selections
38429      */
38430     selectRow : function(index, keepExisting, preventViewNotify){
38431         if(this.locked || (index < 0 || index >= this.grid.dataSource.getCount())) return;
38432         if(this.fireEvent("beforerowselect", this, index, keepExisting) !== false){
38433             if(!keepExisting || this.singleSelect){
38434                 this.clearSelections();
38435             }
38436             var r = this.grid.dataSource.getAt(index);
38437             this.selections.add(r);
38438             this.last = this.lastActive = index;
38439             if(!preventViewNotify){
38440                 this.grid.getView().onRowSelect(index);
38441             }
38442             this.fireEvent("rowselect", this, index, r);
38443             this.fireEvent("selectionchange", this);
38444         }
38445     },
38446
38447     /**
38448      * Deselects a row.
38449      * @param {Number} row The index of the row to deselect
38450      */
38451     deselectRow : function(index, preventViewNotify){
38452         if(this.locked) return;
38453         if(this.last == index){
38454             this.last = false;
38455         }
38456         if(this.lastActive == index){
38457             this.lastActive = false;
38458         }
38459         var r = this.grid.dataSource.getAt(index);
38460         this.selections.remove(r);
38461         if(!preventViewNotify){
38462             this.grid.getView().onRowDeselect(index);
38463         }
38464         this.fireEvent("rowdeselect", this, index);
38465         this.fireEvent("selectionchange", this);
38466     },
38467
38468     // private
38469     restoreLast : function(){
38470         if(this._last){
38471             this.last = this._last;
38472         }
38473     },
38474
38475     // private
38476     acceptsNav : function(row, col, cm){
38477         return !cm.isHidden(col) && cm.isCellEditable(col, row);
38478     },
38479
38480     // private
38481     onEditorKey : function(field, e){
38482         var k = e.getKey(), newCell, g = this.grid, ed = g.activeEditor;
38483         if(k == e.TAB){
38484             e.stopEvent();
38485             ed.completeEdit();
38486             if(e.shiftKey){
38487                 newCell = g.walkCells(ed.row, ed.col-1, -1, this.acceptsNav, this);
38488             }else{
38489                 newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
38490             }
38491         }else if(k == e.ENTER && !e.ctrlKey){
38492             e.stopEvent();
38493             ed.completeEdit();
38494             if(e.shiftKey){
38495                 newCell = g.walkCells(ed.row-1, ed.col, -1, this.acceptsNav, this);
38496             }else{
38497                 newCell = g.walkCells(ed.row+1, ed.col, 1, this.acceptsNav, this);
38498             }
38499         }else if(k == e.ESC){
38500             ed.cancelEdit();
38501         }
38502         if(newCell){
38503             g.startEditing(newCell[0], newCell[1]);
38504         }
38505     }
38506 });/*
38507  * Based on:
38508  * Ext JS Library 1.1.1
38509  * Copyright(c) 2006-2007, Ext JS, LLC.
38510  *
38511  * Originally Released Under LGPL - original licence link has changed is not relivant.
38512  *
38513  * Fork - LGPL
38514  * <script type="text/javascript">
38515  */
38516 /**
38517  * @class Roo.grid.CellSelectionModel
38518  * @extends Roo.grid.AbstractSelectionModel
38519  * This class provides the basic implementation for cell selection in a grid.
38520  * @constructor
38521  * @param {Object} config The object containing the configuration of this model.
38522  * @cfg {Boolean} enter_is_tab Enter behaves the same as tab. (eg. goes to next cell) default: false
38523  */
38524 Roo.grid.CellSelectionModel = function(config){
38525     Roo.apply(this, config);
38526
38527     this.selection = null;
38528
38529     this.addEvents({
38530         /**
38531              * @event beforerowselect
38532              * Fires before a cell is selected.
38533              * @param {SelectionModel} this
38534              * @param {Number} rowIndex The selected row index
38535              * @param {Number} colIndex The selected cell index
38536              */
38537             "beforecellselect" : true,
38538         /**
38539              * @event cellselect
38540              * Fires when a cell is selected.
38541              * @param {SelectionModel} this
38542              * @param {Number} rowIndex The selected row index
38543              * @param {Number} colIndex The selected cell index
38544              */
38545             "cellselect" : true,
38546         /**
38547              * @event selectionchange
38548              * Fires when the active selection changes.
38549              * @param {SelectionModel} this
38550              * @param {Object} selection null for no selection or an object (o) with two properties
38551                 <ul>
38552                 <li>o.record: the record object for the row the selection is in</li>
38553                 <li>o.cell: An array of [rowIndex, columnIndex]</li>
38554                 </ul>
38555              */
38556             "selectionchange" : true,
38557         /**
38558              * @event tabend
38559              * Fires when the tab (or enter) was pressed on the last editable cell
38560              * You can use this to trigger add new row.
38561              * @param {SelectionModel} this
38562              */
38563             "tabend" : true,
38564          /**
38565              * @event beforeeditnext
38566              * Fires before the next editable sell is made active
38567              * You can use this to skip to another cell or fire the tabend
38568              *    if you set cell to false
38569              * @param {Object} eventdata object : { cell : [ row, col ] } 
38570              */
38571             "beforeeditnext" : true
38572     });
38573     Roo.grid.CellSelectionModel.superclass.constructor.call(this);
38574 };
38575
38576 Roo.extend(Roo.grid.CellSelectionModel, Roo.grid.AbstractSelectionModel,  {
38577     
38578     enter_is_tab: false,
38579
38580     /** @ignore */
38581     initEvents : function(){
38582         this.grid.on("mousedown", this.handleMouseDown, this);
38583         this.grid.getGridEl().on(Roo.isIE ? "keydown" : "keypress", this.handleKeyDown, this);
38584         var view = this.grid.view;
38585         view.on("refresh", this.onViewChange, this);
38586         view.on("rowupdated", this.onRowUpdated, this);
38587         view.on("beforerowremoved", this.clearSelections, this);
38588         view.on("beforerowsinserted", this.clearSelections, this);
38589         if(this.grid.isEditor){
38590             this.grid.on("beforeedit", this.beforeEdit,  this);
38591         }
38592     },
38593
38594         //private
38595     beforeEdit : function(e){
38596         this.select(e.row, e.column, false, true, e.record);
38597     },
38598
38599         //private
38600     onRowUpdated : function(v, index, r){
38601         if(this.selection && this.selection.record == r){
38602             v.onCellSelect(index, this.selection.cell[1]);
38603         }
38604     },
38605
38606         //private
38607     onViewChange : function(){
38608         this.clearSelections(true);
38609     },
38610
38611         /**
38612          * Returns the currently selected cell,.
38613          * @return {Array} The selected cell (row, column) or null if none selected.
38614          */
38615     getSelectedCell : function(){
38616         return this.selection ? this.selection.cell : null;
38617     },
38618
38619     /**
38620      * Clears all selections.
38621      * @param {Boolean} true to prevent the gridview from being notified about the change.
38622      */
38623     clearSelections : function(preventNotify){
38624         var s = this.selection;
38625         if(s){
38626             if(preventNotify !== true){
38627                 this.grid.view.onCellDeselect(s.cell[0], s.cell[1]);
38628             }
38629             this.selection = null;
38630             this.fireEvent("selectionchange", this, null);
38631         }
38632     },
38633
38634     /**
38635      * Returns true if there is a selection.
38636      * @return {Boolean}
38637      */
38638     hasSelection : function(){
38639         return this.selection ? true : false;
38640     },
38641
38642     /** @ignore */
38643     handleMouseDown : function(e, t){
38644         var v = this.grid.getView();
38645         if(this.isLocked()){
38646             return;
38647         };
38648         var row = v.findRowIndex(t);
38649         var cell = v.findCellIndex(t);
38650         if(row !== false && cell !== false){
38651             this.select(row, cell);
38652         }
38653     },
38654
38655     /**
38656      * Selects a cell.
38657      * @param {Number} rowIndex
38658      * @param {Number} collIndex
38659      */
38660     select : function(rowIndex, colIndex, preventViewNotify, preventFocus, /*internal*/ r){
38661         if(this.fireEvent("beforecellselect", this, rowIndex, colIndex) !== false){
38662             this.clearSelections();
38663             r = r || this.grid.dataSource.getAt(rowIndex);
38664             this.selection = {
38665                 record : r,
38666                 cell : [rowIndex, colIndex]
38667             };
38668             if(!preventViewNotify){
38669                 var v = this.grid.getView();
38670                 v.onCellSelect(rowIndex, colIndex);
38671                 if(preventFocus !== true){
38672                     v.focusCell(rowIndex, colIndex);
38673                 }
38674             }
38675             this.fireEvent("cellselect", this, rowIndex, colIndex);
38676             this.fireEvent("selectionchange", this, this.selection);
38677         }
38678     },
38679
38680         //private
38681     isSelectable : function(rowIndex, colIndex, cm){
38682         return !cm.isHidden(colIndex);
38683     },
38684
38685     /** @ignore */
38686     handleKeyDown : function(e){
38687         //Roo.log('Cell Sel Model handleKeyDown');
38688         if(!e.isNavKeyPress()){
38689             return;
38690         }
38691         var g = this.grid, s = this.selection;
38692         if(!s){
38693             e.stopEvent();
38694             var cell = g.walkCells(0, 0, 1, this.isSelectable,  this);
38695             if(cell){
38696                 this.select(cell[0], cell[1]);
38697             }
38698             return;
38699         }
38700         var sm = this;
38701         var walk = function(row, col, step){
38702             return g.walkCells(row, col, step, sm.isSelectable,  sm);
38703         };
38704         var k = e.getKey(), r = s.cell[0], c = s.cell[1];
38705         var newCell;
38706
38707       
38708
38709         switch(k){
38710             case e.TAB:
38711                 // handled by onEditorKey
38712                 if (g.isEditor && g.editing) {
38713                     return;
38714                 }
38715                 if(e.shiftKey) {
38716                     newCell = walk(r, c-1, -1);
38717                 } else {
38718                     newCell = walk(r, c+1, 1);
38719                 }
38720                 break;
38721             
38722             case e.DOWN:
38723                newCell = walk(r+1, c, 1);
38724                 break;
38725             
38726             case e.UP:
38727                 newCell = walk(r-1, c, -1);
38728                 break;
38729             
38730             case e.RIGHT:
38731                 newCell = walk(r, c+1, 1);
38732                 break;
38733             
38734             case e.LEFT:
38735                 newCell = walk(r, c-1, -1);
38736                 break;
38737             
38738             case e.ENTER:
38739                 
38740                 if(g.isEditor && !g.editing){
38741                    g.startEditing(r, c);
38742                    e.stopEvent();
38743                    return;
38744                 }
38745                 
38746                 
38747              break;
38748         };
38749         if(newCell){
38750             this.select(newCell[0], newCell[1]);
38751             e.stopEvent();
38752             
38753         }
38754     },
38755
38756     acceptsNav : function(row, col, cm){
38757         return !cm.isHidden(col) && cm.isCellEditable(col, row);
38758     },
38759     /**
38760      * Selects a cell.
38761      * @param {Number} field (not used) - as it's normally used as a listener
38762      * @param {Number} e - event - fake it by using
38763      *
38764      * var e = Roo.EventObjectImpl.prototype;
38765      * e.keyCode = e.TAB
38766      *
38767      * 
38768      */
38769     onEditorKey : function(field, e){
38770         
38771         var k = e.getKey(),
38772             newCell,
38773             g = this.grid,
38774             ed = g.activeEditor,
38775             forward = false;
38776         ///Roo.log('onEditorKey' + k);
38777         
38778         
38779         if (this.enter_is_tab && k == e.ENTER) {
38780             k = e.TAB;
38781         }
38782         
38783         if(k == e.TAB){
38784             if(e.shiftKey){
38785                 newCell = g.walkCells(ed.row, ed.col-1, -1, this.acceptsNav, this);
38786             }else{
38787                 newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
38788                 forward = true;
38789             }
38790             
38791             e.stopEvent();
38792             
38793         } else if(k == e.ENTER &&  !e.ctrlKey){
38794             ed.completeEdit();
38795             e.stopEvent();
38796             newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
38797         
38798                 } else if(k == e.ESC){
38799             ed.cancelEdit();
38800         }
38801                 
38802         if (newCell) {
38803             var ecall = { cell : newCell, forward : forward };
38804             this.fireEvent('beforeeditnext', ecall );
38805             newCell = ecall.cell;
38806                         forward = ecall.forward;
38807         }
38808                 
38809         if(newCell){
38810             //Roo.log('next cell after edit');
38811             g.startEditing.defer(100, g, [newCell[0], newCell[1]]);
38812         } else if (forward) {
38813             // tabbed past last
38814             this.fireEvent.defer(100, this, ['tabend',this]);
38815         }
38816     }
38817 });/*
38818  * Based on:
38819  * Ext JS Library 1.1.1
38820  * Copyright(c) 2006-2007, Ext JS, LLC.
38821  *
38822  * Originally Released Under LGPL - original licence link has changed is not relivant.
38823  *
38824  * Fork - LGPL
38825  * <script type="text/javascript">
38826  */
38827  
38828 /**
38829  * @class Roo.grid.EditorGrid
38830  * @extends Roo.grid.Grid
38831  * Class for creating and editable grid.
38832  * @param {String/HTMLElement/Roo.Element} container The element into which this grid will be rendered - 
38833  * The container MUST have some type of size defined for the grid to fill. The container will be 
38834  * automatically set to position relative if it isn't already.
38835  * @param {Object} dataSource The data model to bind to
38836  * @param {Object} colModel The column model with info about this grid's columns
38837  */
38838 Roo.grid.EditorGrid = function(container, config){
38839     Roo.grid.EditorGrid.superclass.constructor.call(this, container, config);
38840     this.getGridEl().addClass("xedit-grid");
38841
38842     if(!this.selModel){
38843         this.selModel = new Roo.grid.CellSelectionModel();
38844     }
38845
38846     this.activeEditor = null;
38847
38848         this.addEvents({
38849             /**
38850              * @event beforeedit
38851              * Fires before cell editing is triggered. The edit event object has the following properties <br />
38852              * <ul style="padding:5px;padding-left:16px;">
38853              * <li>grid - This grid</li>
38854              * <li>record - The record being edited</li>
38855              * <li>field - The field name being edited</li>
38856              * <li>value - The value for the field being edited.</li>
38857              * <li>row - The grid row index</li>
38858              * <li>column - The grid column index</li>
38859              * <li>cancel - Set this to true to cancel the edit or return false from your handler.</li>
38860              * </ul>
38861              * @param {Object} e An edit event (see above for description)
38862              */
38863             "beforeedit" : true,
38864             /**
38865              * @event afteredit
38866              * Fires after a cell is edited. <br />
38867              * <ul style="padding:5px;padding-left:16px;">
38868              * <li>grid - This grid</li>
38869              * <li>record - The record being edited</li>
38870              * <li>field - The field name being edited</li>
38871              * <li>value - The value being set</li>
38872              * <li>originalValue - The original value for the field, before the edit.</li>
38873              * <li>row - The grid row index</li>
38874              * <li>column - The grid column index</li>
38875              * </ul>
38876              * @param {Object} e An edit event (see above for description)
38877              */
38878             "afteredit" : true,
38879             /**
38880              * @event validateedit
38881              * Fires after a cell is edited, but before the value is set in the record. 
38882          * You can use this to modify the value being set in the field, Return false
38883              * to cancel the change. The edit event object has the following properties <br />
38884              * <ul style="padding:5px;padding-left:16px;">
38885          * <li>editor - This editor</li>
38886              * <li>grid - This grid</li>
38887              * <li>record - The record being edited</li>
38888              * <li>field - The field name being edited</li>
38889              * <li>value - The value being set</li>
38890              * <li>originalValue - The original value for the field, before the edit.</li>
38891              * <li>row - The grid row index</li>
38892              * <li>column - The grid column index</li>
38893              * <li>cancel - Set this to true to cancel the edit or return false from your handler.</li>
38894              * </ul>
38895              * @param {Object} e An edit event (see above for description)
38896              */
38897             "validateedit" : true
38898         });
38899     this.on("bodyscroll", this.stopEditing,  this);
38900     this.on(this.clicksToEdit == 1 ? "cellclick" : "celldblclick", this.onCellDblClick,  this);
38901 };
38902
38903 Roo.extend(Roo.grid.EditorGrid, Roo.grid.Grid, {
38904     /**
38905      * @cfg {Number} clicksToEdit
38906      * The number of clicks on a cell required to display the cell's editor (defaults to 2)
38907      */
38908     clicksToEdit: 2,
38909
38910     // private
38911     isEditor : true,
38912     // private
38913     trackMouseOver: false, // causes very odd FF errors
38914
38915     onCellDblClick : function(g, row, col){
38916         this.startEditing(row, col);
38917     },
38918
38919     onEditComplete : function(ed, value, startValue){
38920         this.editing = false;
38921         this.activeEditor = null;
38922         ed.un("specialkey", this.selModel.onEditorKey, this.selModel);
38923         var r = ed.record;
38924         var field = this.colModel.getDataIndex(ed.col);
38925         var e = {
38926             grid: this,
38927             record: r,
38928             field: field,
38929             originalValue: startValue,
38930             value: value,
38931             row: ed.row,
38932             column: ed.col,
38933             cancel:false,
38934             editor: ed
38935         };
38936         var cell = Roo.get(this.view.getCell(ed.row,ed.col))
38937         cell.show();
38938           
38939         if(String(value) !== String(startValue)){
38940             
38941             if(this.fireEvent("validateedit", e) !== false && !e.cancel){
38942                 r.set(field, e.value);
38943                 // if we are dealing with a combo box..
38944                 // then we also set the 'name' colum to be the displayField
38945                 if (ed.field.displayField && ed.field.name) {
38946                     r.set(ed.field.name, ed.field.el.dom.value);
38947                 }
38948                 
38949                 delete e.cancel; //?? why!!!
38950                 this.fireEvent("afteredit", e);
38951             }
38952         } else {
38953             this.fireEvent("afteredit", e); // always fire it!
38954         }
38955         this.view.focusCell(ed.row, ed.col);
38956     },
38957
38958     /**
38959      * Starts editing the specified for the specified row/column
38960      * @param {Number} rowIndex
38961      * @param {Number} colIndex
38962      */
38963     startEditing : function(row, col){
38964         this.stopEditing();
38965         if(this.colModel.isCellEditable(col, row)){
38966             this.view.ensureVisible(row, col, true);
38967           
38968             var r = this.dataSource.getAt(row);
38969             var field = this.colModel.getDataIndex(col);
38970             var cell = Roo.get(this.view.getCell(row,col));
38971             var e = {
38972                 grid: this,
38973                 record: r,
38974                 field: field,
38975                 value: r.data[field],
38976                 row: row,
38977                 column: col,
38978                 cancel:false 
38979             };
38980             if(this.fireEvent("beforeedit", e) !== false && !e.cancel){
38981                 this.editing = true;
38982                 var ed = this.colModel.getCellEditor(col, row);
38983                 
38984                 if (!ed) {
38985                     return;
38986                 }
38987                 if(!ed.rendered){
38988                     ed.render(ed.parentEl || document.body);
38989                 }
38990                 ed.field.reset();
38991                
38992                 cell.hide();
38993                 
38994                 (function(){ // complex but required for focus issues in safari, ie and opera
38995                     ed.row = row;
38996                     ed.col = col;
38997                     ed.record = r;
38998                     ed.on("complete",   this.onEditComplete,        this,       {single: true});
38999                     ed.on("specialkey", this.selModel.onEditorKey,  this.selModel);
39000                     this.activeEditor = ed;
39001                     var v = r.data[field];
39002                     ed.startEdit(this.view.getCell(row, col), v);
39003                     // combo's with 'displayField and name set
39004                     if (ed.field.displayField && ed.field.name) {
39005                         ed.field.el.dom.value = r.data[ed.field.name];
39006                     }
39007                     
39008                     
39009                 }).defer(50, this);
39010             }
39011         }
39012     },
39013         
39014     /**
39015      * Stops any active editing
39016      */
39017     stopEditing : function(){
39018         if(this.activeEditor){
39019             this.activeEditor.completeEdit();
39020         }
39021         this.activeEditor = null;
39022     },
39023         
39024          /**
39025      * Called to get grid's drag proxy text, by default returns this.ddText.
39026      * @return {String}
39027      */
39028     getDragDropText : function(){
39029         var count = this.selModel.getSelectedCell() ? 1 : 0;
39030         return String.format(this.ddText, count, count == 1 ? '' : 's');
39031     }
39032         
39033 });