Roo/dd/DragDrop.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         if (!Roo.isTouch) {
569             Event.on(this.id, "mousedown", this.handleMouseDown, this);
570         }
571         Event.on(this.id, "touchstart", this.handleMouseDown, this);
572         // Event.on(this.id, "selectstart", Event.preventDefault);
573     },
574
575     /**
576      * Initializes Targeting functionality only... the object does not
577      * get a mousedown handler.
578      * @method initTarget
579      * @param id the id of the linked element
580      * @param {String} sGroup the group of related items
581      * @param {object} config configuration attributes
582      */
583     initTarget: function(id, sGroup, config) {
584
585         // configuration attributes
586         this.config = config || {};
587
588         // create a local reference to the drag and drop manager
589         this.DDM = Roo.dd.DDM;
590         // initialize the groups array
591         this.groups = {};
592
593         // assume that we have an element reference instead of an id if the
594         // parameter is not a string
595         if (typeof id !== "string") {
596             id = Roo.id(id);
597         }
598
599         // set the id
600         this.id = id;
601
602         // add to an interaction group
603         this.addToGroup((sGroup) ? sGroup : "default");
604
605         // We don't want to register this as the handle with the manager
606         // so we just set the id rather than calling the setter.
607         this.handleElId = id;
608
609         // the linked element is the element that gets dragged by default
610         this.setDragElId(id);
611
612         // by default, clicked anchors will not start drag operations.
613         this.invalidHandleTypes = { A: "A" };
614         this.invalidHandleIds = {};
615         this.invalidHandleClasses = [];
616
617         this.applyConfig();
618
619         this.handleOnAvailable();
620     },
621
622     /**
623      * Applies the configuration parameters that were passed into the constructor.
624      * This is supposed to happen at each level through the inheritance chain.  So
625      * a DDProxy implentation will execute apply config on DDProxy, DD, and
626      * DragDrop in order to get all of the parameters that are available in
627      * each object.
628      * @method applyConfig
629      */
630     applyConfig: function() {
631
632         // configurable properties:
633         //    padding, isTarget, maintainOffset, primaryButtonOnly
634         this.padding           = this.config.padding || [0, 0, 0, 0];
635         this.isTarget          = (this.config.isTarget !== false);
636         this.maintainOffset    = (this.config.maintainOffset);
637         this.primaryButtonOnly = (this.config.primaryButtonOnly !== false);
638
639     },
640
641     /**
642      * Executed when the linked element is available
643      * @method handleOnAvailable
644      * @private
645      */
646     handleOnAvailable: function() {
647         this.available = true;
648         this.resetConstraints();
649         this.onAvailable();
650     },
651
652      /**
653      * Configures the padding for the target zone in px.  Effectively expands
654      * (or reduces) the virtual object size for targeting calculations.
655      * Supports css-style shorthand; if only one parameter is passed, all sides
656      * will have that padding, and if only two are passed, the top and bottom
657      * will have the first param, the left and right the second.
658      * @method setPadding
659      * @param {int} iTop    Top pad
660      * @param {int} iRight  Right pad
661      * @param {int} iBot    Bot pad
662      * @param {int} iLeft   Left pad
663      */
664     setPadding: function(iTop, iRight, iBot, iLeft) {
665         // this.padding = [iLeft, iRight, iTop, iBot];
666         if (!iRight && 0 !== iRight) {
667             this.padding = [iTop, iTop, iTop, iTop];
668         } else if (!iBot && 0 !== iBot) {
669             this.padding = [iTop, iRight, iTop, iRight];
670         } else {
671             this.padding = [iTop, iRight, iBot, iLeft];
672         }
673     },
674
675     /**
676      * Stores the initial placement of the linked element.
677      * @method setInitialPosition
678      * @param {int} diffX   the X offset, default 0
679      * @param {int} diffY   the Y offset, default 0
680      */
681     setInitPosition: function(diffX, diffY) {
682         var el = this.getEl();
683
684         if (!this.DDM.verifyEl(el)) {
685             return;
686         }
687
688         var dx = diffX || 0;
689         var dy = diffY || 0;
690
691         var p = Dom.getXY( el );
692
693         this.initPageX = p[0] - dx;
694         this.initPageY = p[1] - dy;
695
696         this.lastPageX = p[0];
697         this.lastPageY = p[1];
698
699
700         this.setStartPosition(p);
701     },
702
703     /**
704      * Sets the start position of the element.  This is set when the obj
705      * is initialized, the reset when a drag is started.
706      * @method setStartPosition
707      * @param pos current position (from previous lookup)
708      * @private
709      */
710     setStartPosition: function(pos) {
711         var p = pos || Dom.getXY( this.getEl() );
712         this.deltaSetXY = null;
713
714         this.startPageX = p[0];
715         this.startPageY = p[1];
716     },
717
718     /**
719      * Add this instance to a group of related drag/drop objects.  All
720      * instances belong to at least one group, and can belong to as many
721      * groups as needed.
722      * @method addToGroup
723      * @param sGroup {string} the name of the group
724      */
725     addToGroup: function(sGroup) {
726         this.groups[sGroup] = true;
727         this.DDM.regDragDrop(this, sGroup);
728     },
729
730     /**
731      * Remove's this instance from the supplied interaction group
732      * @method removeFromGroup
733      * @param {string}  sGroup  The group to drop
734      */
735     removeFromGroup: function(sGroup) {
736         if (this.groups[sGroup]) {
737             delete this.groups[sGroup];
738         }
739
740         this.DDM.removeDDFromGroup(this, sGroup);
741     },
742
743     /**
744      * Allows you to specify that an element other than the linked element
745      * will be moved with the cursor during a drag
746      * @method setDragElId
747      * @param id {string} the id of the element that will be used to initiate the drag
748      */
749     setDragElId: function(id) {
750         this.dragElId = id;
751     },
752
753     /**
754      * Allows you to specify a child of the linked element that should be
755      * used to initiate the drag operation.  An example of this would be if
756      * you have a content div with text and links.  Clicking anywhere in the
757      * content area would normally start the drag operation.  Use this method
758      * to specify that an element inside of the content div is the element
759      * that starts the drag operation.
760      * @method setHandleElId
761      * @param id {string} the id of the element that will be used to
762      * initiate the drag.
763      */
764     setHandleElId: function(id) {
765         if (typeof id !== "string") {
766             id = Roo.id(id);
767         }
768         this.handleElId = id;
769         this.DDM.regHandle(this.id, id);
770     },
771
772     /**
773      * Allows you to set an element outside of the linked element as a drag
774      * handle
775      * @method setOuterHandleElId
776      * @param id the id of the element that will be used to initiate the drag
777      */
778     setOuterHandleElId: function(id) {
779         if (typeof id !== "string") {
780             id = Roo.id(id);
781         }
782         Event.on(id, "mousedown",
783                 this.handleMouseDown, this);
784         this.setHandleElId(id);
785
786         this.hasOuterHandles = true;
787     },
788
789     /**
790      * Remove all drag and drop hooks for this element
791      * @method unreg
792      */
793     unreg: function() {
794         Event.un(this.id, "mousedown",
795                 this.handleMouseDown);
796         Event.un(this.id, "touchstart",
797                 this.handleMouseDown);
798         this._domRef = null;
799         this.DDM._remove(this);
800     },
801
802     destroy : function(){
803         this.unreg();
804     },
805
806     /**
807      * Returns true if this instance is locked, or the drag drop mgr is locked
808      * (meaning that all drag/drop is disabled on the page.)
809      * @method isLocked
810      * @return {boolean} true if this obj or all drag/drop is locked, else
811      * false
812      */
813     isLocked: function() {
814         return (this.DDM.isLocked() || this.locked);
815     },
816
817     /**
818      * Fired when this object is clicked
819      * @method handleMouseDown
820      * @param {Event} e
821      * @param {Roo.dd.DragDrop} oDD the clicked dd object (this dd obj)
822      * @private
823      */
824     handleMouseDown: function(e, oDD){
825         Roo.log(this);
826         Roo.log(e);
827         if (this.primaryButtonOnly && e.button != 0) {
828             return;
829         }
830
831         if (this.isLocked()) {
832             return;
833         }
834
835         this.DDM.refreshCache(this.groups);
836
837         var pt = new Roo.lib.Point(Roo.lib.Event.getPageX(e), Roo.lib.Event.getPageY(e));
838         if (!this.hasOuterHandles && !this.DDM.isOverTarget(pt, this) )  {
839         } else {
840             if (this.clickValidator(e)) {
841
842                 // set the initial element position
843                 this.setStartPosition();
844
845
846                 this.b4MouseDown(e);
847                 this.onMouseDown(e);
848
849                 this.DDM.handleMouseDown(e, this);
850
851                 this.DDM.stopEvent(e);
852             } else {
853
854
855             }
856         }
857     },
858
859     clickValidator: function(e) {
860         var target = e.getTarget();
861         return ( this.isValidHandleChild(target) &&
862                     (this.id == this.handleElId ||
863                         this.DDM.handleWasClicked(target, this.id)) );
864     },
865
866     /**
867      * Allows you to specify a tag name that should not start a drag operation
868      * when clicked.  This is designed to facilitate embedding links within a
869      * drag handle that do something other than start the drag.
870      * @method addInvalidHandleType
871      * @param {string} tagName the type of element to exclude
872      */
873     addInvalidHandleType: function(tagName) {
874         var type = tagName.toUpperCase();
875         this.invalidHandleTypes[type] = type;
876     },
877
878     /**
879      * Lets you to specify an element id for a child of a drag handle
880      * that should not initiate a drag
881      * @method addInvalidHandleId
882      * @param {string} id the element id of the element you wish to ignore
883      */
884     addInvalidHandleId: function(id) {
885         if (typeof id !== "string") {
886             id = Roo.id(id);
887         }
888         this.invalidHandleIds[id] = id;
889     },
890
891     /**
892      * Lets you specify a css class of elements that will not initiate a drag
893      * @method addInvalidHandleClass
894      * @param {string} cssClass the class of the elements you wish to ignore
895      */
896     addInvalidHandleClass: function(cssClass) {
897         this.invalidHandleClasses.push(cssClass);
898     },
899
900     /**
901      * Unsets an excluded tag name set by addInvalidHandleType
902      * @method removeInvalidHandleType
903      * @param {string} tagName the type of element to unexclude
904      */
905     removeInvalidHandleType: function(tagName) {
906         var type = tagName.toUpperCase();
907         // this.invalidHandleTypes[type] = null;
908         delete this.invalidHandleTypes[type];
909     },
910
911     /**
912      * Unsets an invalid handle id
913      * @method removeInvalidHandleId
914      * @param {string} id the id of the element to re-enable
915      */
916     removeInvalidHandleId: function(id) {
917         if (typeof id !== "string") {
918             id = Roo.id(id);
919         }
920         delete this.invalidHandleIds[id];
921     },
922
923     /**
924      * Unsets an invalid css class
925      * @method removeInvalidHandleClass
926      * @param {string} cssClass the class of the element(s) you wish to
927      * re-enable
928      */
929     removeInvalidHandleClass: function(cssClass) {
930         for (var i=0, len=this.invalidHandleClasses.length; i<len; ++i) {
931             if (this.invalidHandleClasses[i] == cssClass) {
932                 delete this.invalidHandleClasses[i];
933             }
934         }
935     },
936
937     /**
938      * Checks the tag exclusion list to see if this click should be ignored
939      * @method isValidHandleChild
940      * @param {HTMLElement} node the HTMLElement to evaluate
941      * @return {boolean} true if this is a valid tag type, false if not
942      */
943     isValidHandleChild: function(node) {
944
945         var valid = true;
946         // var n = (node.nodeName == "#text") ? node.parentNode : node;
947         var nodeName;
948         try {
949             nodeName = node.nodeName.toUpperCase();
950         } catch(e) {
951             nodeName = node.nodeName;
952         }
953         valid = valid && !this.invalidHandleTypes[nodeName];
954         valid = valid && !this.invalidHandleIds[node.id];
955
956         for (var i=0, len=this.invalidHandleClasses.length; valid && i<len; ++i) {
957             valid = !Dom.hasClass(node, this.invalidHandleClasses[i]);
958         }
959
960
961         return valid;
962
963     },
964
965     /**
966      * Create the array of horizontal tick marks if an interval was specified
967      * in setXConstraint().
968      * @method setXTicks
969      * @private
970      */
971     setXTicks: function(iStartX, iTickSize) {
972         this.xTicks = [];
973         this.xTickSize = iTickSize;
974
975         var tickMap = {};
976
977         for (var i = this.initPageX; i >= this.minX; i = i - iTickSize) {
978             if (!tickMap[i]) {
979                 this.xTicks[this.xTicks.length] = i;
980                 tickMap[i] = true;
981             }
982         }
983
984         for (i = this.initPageX; i <= this.maxX; i = i + iTickSize) {
985             if (!tickMap[i]) {
986                 this.xTicks[this.xTicks.length] = i;
987                 tickMap[i] = true;
988             }
989         }
990
991         this.xTicks.sort(this.DDM.numericSort) ;
992     },
993
994     /**
995      * Create the array of vertical tick marks if an interval was specified in
996      * setYConstraint().
997      * @method setYTicks
998      * @private
999      */
1000     setYTicks: function(iStartY, iTickSize) {
1001         this.yTicks = [];
1002         this.yTickSize = iTickSize;
1003
1004         var tickMap = {};
1005
1006         for (var i = this.initPageY; i >= this.minY; i = i - iTickSize) {
1007             if (!tickMap[i]) {
1008                 this.yTicks[this.yTicks.length] = i;
1009                 tickMap[i] = true;
1010             }
1011         }
1012
1013         for (i = this.initPageY; i <= this.maxY; i = i + iTickSize) {
1014             if (!tickMap[i]) {
1015                 this.yTicks[this.yTicks.length] = i;
1016                 tickMap[i] = true;
1017             }
1018         }
1019
1020         this.yTicks.sort(this.DDM.numericSort) ;
1021     },
1022
1023     /**
1024      * By default, the element can be dragged any place on the screen.  Use
1025      * this method to limit the horizontal travel of the element.  Pass in
1026      * 0,0 for the parameters if you want to lock the drag to the y axis.
1027      * @method setXConstraint
1028      * @param {int} iLeft the number of pixels the element can move to the left
1029      * @param {int} iRight the number of pixels the element can move to the
1030      * right
1031      * @param {int} iTickSize optional parameter for specifying that the
1032      * element
1033      * should move iTickSize pixels at a time.
1034      */
1035     setXConstraint: function(iLeft, iRight, iTickSize) {
1036         this.leftConstraint = iLeft;
1037         this.rightConstraint = iRight;
1038
1039         this.minX = this.initPageX - iLeft;
1040         this.maxX = this.initPageX + iRight;
1041         if (iTickSize) { this.setXTicks(this.initPageX, iTickSize); }
1042
1043         this.constrainX = true;
1044     },
1045
1046     /**
1047      * Clears any constraints applied to this instance.  Also clears ticks
1048      * since they can't exist independent of a constraint at this time.
1049      * @method clearConstraints
1050      */
1051     clearConstraints: function() {
1052         this.constrainX = false;
1053         this.constrainY = false;
1054         this.clearTicks();
1055     },
1056
1057     /**
1058      * Clears any tick interval defined for this instance
1059      * @method clearTicks
1060      */
1061     clearTicks: function() {
1062         this.xTicks = null;
1063         this.yTicks = null;
1064         this.xTickSize = 0;
1065         this.yTickSize = 0;
1066     },
1067
1068     /**
1069      * By default, the element can be dragged any place on the screen.  Set
1070      * this to limit the vertical travel of the element.  Pass in 0,0 for the
1071      * parameters if you want to lock the drag to the x axis.
1072      * @method setYConstraint
1073      * @param {int} iUp the number of pixels the element can move up
1074      * @param {int} iDown the number of pixels the element can move down
1075      * @param {int} iTickSize optional parameter for specifying that the
1076      * element should move iTickSize pixels at a time.
1077      */
1078     setYConstraint: function(iUp, iDown, iTickSize) {
1079         this.topConstraint = iUp;
1080         this.bottomConstraint = iDown;
1081
1082         this.minY = this.initPageY - iUp;
1083         this.maxY = this.initPageY + iDown;
1084         if (iTickSize) { this.setYTicks(this.initPageY, iTickSize); }
1085
1086         this.constrainY = true;
1087
1088     },
1089
1090     /**
1091      * resetConstraints must be called if you manually reposition a dd element.
1092      * @method resetConstraints
1093      * @param {boolean} maintainOffset
1094      */
1095     resetConstraints: function() {
1096
1097
1098         // Maintain offsets if necessary
1099         if (this.initPageX || this.initPageX === 0) {
1100             // figure out how much this thing has moved
1101             var dx = (this.maintainOffset) ? this.lastPageX - this.initPageX : 0;
1102             var dy = (this.maintainOffset) ? this.lastPageY - this.initPageY : 0;
1103
1104             this.setInitPosition(dx, dy);
1105
1106         // This is the first time we have detected the element's position
1107         } else {
1108             this.setInitPosition();
1109         }
1110
1111         if (this.constrainX) {
1112             this.setXConstraint( this.leftConstraint,
1113                                  this.rightConstraint,
1114                                  this.xTickSize        );
1115         }
1116
1117         if (this.constrainY) {
1118             this.setYConstraint( this.topConstraint,
1119                                  this.bottomConstraint,
1120                                  this.yTickSize         );
1121         }
1122     },
1123
1124     /**
1125      * Normally the drag element is moved pixel by pixel, but we can specify
1126      * that it move a number of pixels at a time.  This method resolves the
1127      * location when we have it set up like this.
1128      * @method getTick
1129      * @param {int} val where we want to place the object
1130      * @param {int[]} tickArray sorted array of valid points
1131      * @return {int} the closest tick
1132      * @private
1133      */
1134     getTick: function(val, tickArray) {
1135
1136         if (!tickArray) {
1137             // If tick interval is not defined, it is effectively 1 pixel,
1138             // so we return the value passed to us.
1139             return val;
1140         } else if (tickArray[0] >= val) {
1141             // The value is lower than the first tick, so we return the first
1142             // tick.
1143             return tickArray[0];
1144         } else {
1145             for (var i=0, len=tickArray.length; i<len; ++i) {
1146                 var next = i + 1;
1147                 if (tickArray[next] && tickArray[next] >= val) {
1148                     var diff1 = val - tickArray[i];
1149                     var diff2 = tickArray[next] - val;
1150                     return (diff2 > diff1) ? tickArray[i] : tickArray[next];
1151                 }
1152             }
1153
1154             // The value is larger than the last tick, so we return the last
1155             // tick.
1156             return tickArray[tickArray.length - 1];
1157         }
1158     },
1159
1160     /**
1161      * toString method
1162      * @method toString
1163      * @return {string} string representation of the dd obj
1164      */
1165     toString: function() {
1166         return ("DragDrop " + this.id);
1167     }
1168
1169 });
1170
1171 })();
1172 /*
1173  * Based on:
1174  * Ext JS Library 1.1.1
1175  * Copyright(c) 2006-2007, Ext JS, LLC.
1176  *
1177  * Originally Released Under LGPL - original licence link has changed is not relivant.
1178  *
1179  * Fork - LGPL
1180  * <script type="text/javascript">
1181  */
1182
1183
1184 /**
1185  * The drag and drop utility provides a framework for building drag and drop
1186  * applications.  In addition to enabling drag and drop for specific elements,
1187  * the drag and drop elements are tracked by the manager class, and the
1188  * interactions between the various elements are tracked during the drag and
1189  * the implementing code is notified about these important moments.
1190  */
1191
1192 // Only load the library once.  Rewriting the manager class would orphan
1193 // existing drag and drop instances.
1194 if (!Roo.dd.DragDropMgr) {
1195
1196 /**
1197  * @class Roo.dd.DragDropMgr
1198  * DragDropMgr is a singleton that tracks the element interaction for
1199  * all DragDrop items in the window.  Generally, you will not call
1200  * this class directly, but it does have helper methods that could
1201  * be useful in your DragDrop implementations.
1202  * @singleton
1203  */
1204 Roo.dd.DragDropMgr = function() {
1205
1206     var Event = Roo.EventManager;
1207
1208     return {
1209
1210         /**
1211          * Two dimensional Array of registered DragDrop objects.  The first
1212          * dimension is the DragDrop item group, the second the DragDrop
1213          * object.
1214          * @property ids
1215          * @type {string: string}
1216          * @private
1217          * @static
1218          */
1219         ids: {},
1220
1221         /**
1222          * Array of element ids defined as drag handles.  Used to determine
1223          * if the element that generated the mousedown event is actually the
1224          * handle and not the html element itself.
1225          * @property handleIds
1226          * @type {string: string}
1227          * @private
1228          * @static
1229          */
1230         handleIds: {},
1231
1232         /**
1233          * the DragDrop object that is currently being dragged
1234          * @property dragCurrent
1235          * @type DragDrop
1236          * @private
1237          * @static
1238          **/
1239         dragCurrent: null,
1240
1241         /**
1242          * the DragDrop object(s) that are being hovered over
1243          * @property dragOvers
1244          * @type Array
1245          * @private
1246          * @static
1247          */
1248         dragOvers: {},
1249
1250         /**
1251          * the X distance between the cursor and the object being dragged
1252          * @property deltaX
1253          * @type int
1254          * @private
1255          * @static
1256          */
1257         deltaX: 0,
1258
1259         /**
1260          * the Y distance between the cursor and the object being dragged
1261          * @property deltaY
1262          * @type int
1263          * @private
1264          * @static
1265          */
1266         deltaY: 0,
1267
1268         /**
1269          * Flag to determine if we should prevent the default behavior of the
1270          * events we define. By default this is true, but this can be set to
1271          * false if you need the default behavior (not recommended)
1272          * @property preventDefault
1273          * @type boolean
1274          * @static
1275          */
1276         preventDefault: true,
1277
1278         /**
1279          * Flag to determine if we should stop the propagation of the events
1280          * we generate. This is true by default but you may want to set it to
1281          * false if the html element contains other features that require the
1282          * mouse click.
1283          * @property stopPropagation
1284          * @type boolean
1285          * @static
1286          */
1287         stopPropagation: true,
1288
1289         /**
1290          * Internal flag that is set to true when drag and drop has been
1291          * intialized
1292          * @property initialized
1293          * @private
1294          * @static
1295          */
1296         initalized: false,
1297
1298         /**
1299          * All drag and drop can be disabled.
1300          * @property locked
1301          * @private
1302          * @static
1303          */
1304         locked: false,
1305
1306         /**
1307          * Called the first time an element is registered.
1308          * @method init
1309          * @private
1310          * @static
1311          */
1312         init: function() {
1313             this.initialized = true;
1314         },
1315
1316         /**
1317          * In point mode, drag and drop interaction is defined by the
1318          * location of the cursor during the drag/drop
1319          * @property POINT
1320          * @type int
1321          * @static
1322          */
1323         POINT: 0,
1324
1325         /**
1326          * In intersect mode, drag and drop interactio nis defined by the
1327          * overlap of two or more drag and drop objects.
1328          * @property INTERSECT
1329          * @type int
1330          * @static
1331          */
1332         INTERSECT: 1,
1333
1334         /**
1335          * The current drag and drop mode.  Default: POINT
1336          * @property mode
1337          * @type int
1338          * @static
1339          */
1340         mode: 0,
1341
1342         /**
1343          * Runs method on all drag and drop objects
1344          * @method _execOnAll
1345          * @private
1346          * @static
1347          */
1348         _execOnAll: function(sMethod, args) {
1349             for (var i in this.ids) {
1350                 for (var j in this.ids[i]) {
1351                     var oDD = this.ids[i][j];
1352                     if (! this.isTypeOfDD(oDD)) {
1353                         continue;
1354                     }
1355                     oDD[sMethod].apply(oDD, args);
1356                 }
1357             }
1358         },
1359
1360         /**
1361          * Drag and drop initialization.  Sets up the global event handlers
1362          * @method _onLoad
1363          * @private
1364          * @static
1365          */
1366         _onLoad: function() {
1367
1368             this.init();
1369
1370             if (!Roo.isTouch) {
1371                 Event.on(document, "mouseup",   this.handleMouseUp, this, true);
1372                 Event.on(document, "mousemove", this.handleMouseMove, this, true);
1373             }
1374             Event.on(document, "touchend",   this.handleMouseUp, this, true);
1375             Event.on(document, "touchmove", this.handleMouseMove, this, true);
1376             
1377             Event.on(window,   "unload",    this._onUnload, this, true);
1378             Event.on(window,   "resize",    this._onResize, this, true);
1379             // Event.on(window,   "mouseout",    this._test);
1380
1381         },
1382
1383         /**
1384          * Reset constraints on all drag and drop objs
1385          * @method _onResize
1386          * @private
1387          * @static
1388          */
1389         _onResize: function(e) {
1390             this._execOnAll("resetConstraints", []);
1391         },
1392
1393         /**
1394          * Lock all drag and drop functionality
1395          * @method lock
1396          * @static
1397          */
1398         lock: function() { this.locked = true; },
1399
1400         /**
1401          * Unlock all drag and drop functionality
1402          * @method unlock
1403          * @static
1404          */
1405         unlock: function() { this.locked = false; },
1406
1407         /**
1408          * Is drag and drop locked?
1409          * @method isLocked
1410          * @return {boolean} True if drag and drop is locked, false otherwise.
1411          * @static
1412          */
1413         isLocked: function() { return this.locked; },
1414
1415         /**
1416          * Location cache that is set for all drag drop objects when a drag is
1417          * initiated, cleared when the drag is finished.
1418          * @property locationCache
1419          * @private
1420          * @static
1421          */
1422         locationCache: {},
1423
1424         /**
1425          * Set useCache to false if you want to force object the lookup of each
1426          * drag and drop linked element constantly during a drag.
1427          * @property useCache
1428          * @type boolean
1429          * @static
1430          */
1431         useCache: true,
1432
1433         /**
1434          * The number of pixels that the mouse needs to move after the
1435          * mousedown before the drag is initiated.  Default=3;
1436          * @property clickPixelThresh
1437          * @type int
1438          * @static
1439          */
1440         clickPixelThresh: 3,
1441
1442         /**
1443          * The number of milliseconds after the mousedown event to initiate the
1444          * drag if we don't get a mouseup event. Default=1000
1445          * @property clickTimeThresh
1446          * @type int
1447          * @static
1448          */
1449         clickTimeThresh: 350,
1450
1451         /**
1452          * Flag that indicates that either the drag pixel threshold or the
1453          * mousdown time threshold has been met
1454          * @property dragThreshMet
1455          * @type boolean
1456          * @private
1457          * @static
1458          */
1459         dragThreshMet: false,
1460
1461         /**
1462          * Timeout used for the click time threshold
1463          * @property clickTimeout
1464          * @type Object
1465          * @private
1466          * @static
1467          */
1468         clickTimeout: null,
1469
1470         /**
1471          * The X position of the mousedown event stored for later use when a
1472          * drag threshold is met.
1473          * @property startX
1474          * @type int
1475          * @private
1476          * @static
1477          */
1478         startX: 0,
1479
1480         /**
1481          * The Y position of the mousedown event stored for later use when a
1482          * drag threshold is met.
1483          * @property startY
1484          * @type int
1485          * @private
1486          * @static
1487          */
1488         startY: 0,
1489
1490         /**
1491          * Each DragDrop instance must be registered with the DragDropMgr.
1492          * This is executed in DragDrop.init()
1493          * @method regDragDrop
1494          * @param {DragDrop} oDD the DragDrop object to register
1495          * @param {String} sGroup the name of the group this element belongs to
1496          * @static
1497          */
1498         regDragDrop: function(oDD, sGroup) {
1499             if (!this.initialized) { this.init(); }
1500
1501             if (!this.ids[sGroup]) {
1502                 this.ids[sGroup] = {};
1503             }
1504             this.ids[sGroup][oDD.id] = oDD;
1505         },
1506
1507         /**
1508          * Removes the supplied dd instance from the supplied group. Executed
1509          * by DragDrop.removeFromGroup, so don't call this function directly.
1510          * @method removeDDFromGroup
1511          * @private
1512          * @static
1513          */
1514         removeDDFromGroup: function(oDD, sGroup) {
1515             if (!this.ids[sGroup]) {
1516                 this.ids[sGroup] = {};
1517             }
1518
1519             var obj = this.ids[sGroup];
1520             if (obj && obj[oDD.id]) {
1521                 delete obj[oDD.id];
1522             }
1523         },
1524
1525         /**
1526          * Unregisters a drag and drop item.  This is executed in
1527          * DragDrop.unreg, use that method instead of calling this directly.
1528          * @method _remove
1529          * @private
1530          * @static
1531          */
1532         _remove: function(oDD) {
1533             for (var g in oDD.groups) {
1534                 if (g && this.ids[g][oDD.id]) {
1535                     delete this.ids[g][oDD.id];
1536                 }
1537             }
1538             delete this.handleIds[oDD.id];
1539         },
1540
1541         /**
1542          * Each DragDrop handle element must be registered.  This is done
1543          * automatically when executing DragDrop.setHandleElId()
1544          * @method regHandle
1545          * @param {String} sDDId the DragDrop id this element is a handle for
1546          * @param {String} sHandleId the id of the element that is the drag
1547          * handle
1548          * @static
1549          */
1550         regHandle: function(sDDId, sHandleId) {
1551             if (!this.handleIds[sDDId]) {
1552                 this.handleIds[sDDId] = {};
1553             }
1554             this.handleIds[sDDId][sHandleId] = sHandleId;
1555         },
1556
1557         /**
1558          * Utility function to determine if a given element has been
1559          * registered as a drag drop item.
1560          * @method isDragDrop
1561          * @param {String} id the element id to check
1562          * @return {boolean} true if this element is a DragDrop item,
1563          * false otherwise
1564          * @static
1565          */
1566         isDragDrop: function(id) {
1567             return ( this.getDDById(id) ) ? true : false;
1568         },
1569
1570         /**
1571          * Returns the drag and drop instances that are in all groups the
1572          * passed in instance belongs to.
1573          * @method getRelated
1574          * @param {DragDrop} p_oDD the obj to get related data for
1575          * @param {boolean} bTargetsOnly if true, only return targetable objs
1576          * @return {DragDrop[]} the related instances
1577          * @static
1578          */
1579         getRelated: function(p_oDD, bTargetsOnly) {
1580             var oDDs = [];
1581             for (var i in p_oDD.groups) {
1582                 for (j in this.ids[i]) {
1583                     var dd = this.ids[i][j];
1584                     if (! this.isTypeOfDD(dd)) {
1585                         continue;
1586                     }
1587                     if (!bTargetsOnly || dd.isTarget) {
1588                         oDDs[oDDs.length] = dd;
1589                     }
1590                 }
1591             }
1592
1593             return oDDs;
1594         },
1595
1596         /**
1597          * Returns true if the specified dd target is a legal target for
1598          * the specifice drag obj
1599          * @method isLegalTarget
1600          * @param {DragDrop} the drag obj
1601          * @param {DragDrop} the target
1602          * @return {boolean} true if the target is a legal target for the
1603          * dd obj
1604          * @static
1605          */
1606         isLegalTarget: function (oDD, oTargetDD) {
1607             var targets = this.getRelated(oDD, true);
1608             for (var i=0, len=targets.length;i<len;++i) {
1609                 if (targets[i].id == oTargetDD.id) {
1610                     return true;
1611                 }
1612             }
1613
1614             return false;
1615         },
1616
1617         /**
1618          * My goal is to be able to transparently determine if an object is
1619          * typeof DragDrop, and the exact subclass of DragDrop.  typeof
1620          * returns "object", oDD.constructor.toString() always returns
1621          * "DragDrop" and not the name of the subclass.  So for now it just
1622          * evaluates a well-known variable in DragDrop.
1623          * @method isTypeOfDD
1624          * @param {Object} the object to evaluate
1625          * @return {boolean} true if typeof oDD = DragDrop
1626          * @static
1627          */
1628         isTypeOfDD: function (oDD) {
1629             return (oDD && oDD.__ygDragDrop);
1630         },
1631
1632         /**
1633          * Utility function to determine if a given element has been
1634          * registered as a drag drop handle for the given Drag Drop object.
1635          * @method isHandle
1636          * @param {String} id the element id to check
1637          * @return {boolean} true if this element is a DragDrop handle, false
1638          * otherwise
1639          * @static
1640          */
1641         isHandle: function(sDDId, sHandleId) {
1642             return ( this.handleIds[sDDId] &&
1643                             this.handleIds[sDDId][sHandleId] );
1644         },
1645
1646         /**
1647          * Returns the DragDrop instance for a given id
1648          * @method getDDById
1649          * @param {String} id the id of the DragDrop object
1650          * @return {DragDrop} the drag drop object, null if it is not found
1651          * @static
1652          */
1653         getDDById: function(id) {
1654             for (var i in this.ids) {
1655                 if (this.ids[i][id]) {
1656                     return this.ids[i][id];
1657                 }
1658             }
1659             return null;
1660         },
1661
1662         /**
1663          * Fired after a registered DragDrop object gets the mousedown event.
1664          * Sets up the events required to track the object being dragged
1665          * @method handleMouseDown
1666          * @param {Event} e the event
1667          * @param oDD the DragDrop object being dragged
1668          * @private
1669          * @static
1670          */
1671         handleMouseDown: function(e, oDD) {
1672             if(Roo.QuickTips){
1673                 Roo.QuickTips.disable();
1674             }
1675             this.currentTarget = e.getTarget();
1676
1677             this.dragCurrent = oDD;
1678
1679             var el = oDD.getEl();
1680
1681             // track start position
1682             this.startX = e.getPageX();
1683             this.startY = e.getPageY();
1684
1685             this.deltaX = this.startX - el.offsetLeft;
1686             this.deltaY = this.startY - el.offsetTop;
1687
1688             this.dragThreshMet = false;
1689
1690             this.clickTimeout = setTimeout(
1691                     function() {
1692                         var DDM = Roo.dd.DDM;
1693                         DDM.startDrag(DDM.startX, DDM.startY);
1694                     },
1695                     this.clickTimeThresh );
1696         },
1697
1698         /**
1699          * Fired when either the drag pixel threshol or the mousedown hold
1700          * time threshold has been met.
1701          * @method startDrag
1702          * @param x {int} the X position of the original mousedown
1703          * @param y {int} the Y position of the original mousedown
1704          * @static
1705          */
1706         startDrag: function(x, y) {
1707             clearTimeout(this.clickTimeout);
1708             if (this.dragCurrent) {
1709                 this.dragCurrent.b4StartDrag(x, y);
1710                 this.dragCurrent.startDrag(x, y);
1711             }
1712             this.dragThreshMet = true;
1713         },
1714
1715         /**
1716          * Internal function to handle the mouseup event.  Will be invoked
1717          * from the context of the document.
1718          * @method handleMouseUp
1719          * @param {Event} e the event
1720          * @private
1721          * @static
1722          */
1723         handleMouseUp: function(e) {
1724
1725             if(Roo.QuickTips){
1726                 Roo.QuickTips.enable();
1727             }
1728             if (! this.dragCurrent) {
1729                 return;
1730             }
1731
1732             clearTimeout(this.clickTimeout);
1733
1734             if (this.dragThreshMet) {
1735                 this.fireEvents(e, true);
1736             } else {
1737             }
1738
1739             this.stopDrag(e);
1740
1741             this.stopEvent(e);
1742         },
1743
1744         /**
1745          * Utility to stop event propagation and event default, if these
1746          * features are turned on.
1747          * @method stopEvent
1748          * @param {Event} e the event as returned by this.getEvent()
1749          * @static
1750          */
1751         stopEvent: function(e){
1752             if(this.stopPropagation) {
1753                 e.stopPropagation();
1754             }
1755
1756             if (this.preventDefault) {
1757                 e.preventDefault();
1758             }
1759         },
1760
1761         /**
1762          * Internal function to clean up event handlers after the drag
1763          * operation is complete
1764          * @method stopDrag
1765          * @param {Event} e the event
1766          * @private
1767          * @static
1768          */
1769         stopDrag: function(e) {
1770             // Fire the drag end event for the item that was dragged
1771             if (this.dragCurrent) {
1772                 if (this.dragThreshMet) {
1773                     this.dragCurrent.b4EndDrag(e);
1774                     this.dragCurrent.endDrag(e);
1775                 }
1776
1777                 this.dragCurrent.onMouseUp(e);
1778             }
1779
1780             this.dragCurrent = null;
1781             this.dragOvers = {};
1782         },
1783
1784         /**
1785          * Internal function to handle the mousemove event.  Will be invoked
1786          * from the context of the html element.
1787          *
1788          * @TODO figure out what we can do about mouse events lost when the
1789          * user drags objects beyond the window boundary.  Currently we can
1790          * detect this in internet explorer by verifying that the mouse is
1791          * down during the mousemove event.  Firefox doesn't give us the
1792          * button state on the mousemove event.
1793          * @method handleMouseMove
1794          * @param {Event} e the event
1795          * @private
1796          * @static
1797          */
1798         handleMouseMove: function(e) {
1799             if (! this.dragCurrent) {
1800                 return true;
1801             }
1802
1803             // var button = e.which || e.button;
1804
1805             // check for IE mouseup outside of page boundary
1806             if (Roo.isIE && (e.button !== 0 && e.button !== 1 && e.button !== 2)) {
1807                 this.stopEvent(e);
1808                 return this.handleMouseUp(e);
1809             }
1810
1811             if (!this.dragThreshMet) {
1812                 var diffX = Math.abs(this.startX - e.getPageX());
1813                 var diffY = Math.abs(this.startY - e.getPageY());
1814                 if (diffX > this.clickPixelThresh ||
1815                             diffY > this.clickPixelThresh) {
1816                     this.startDrag(this.startX, this.startY);
1817                 }
1818             }
1819
1820             if (this.dragThreshMet) {
1821                 this.dragCurrent.b4Drag(e);
1822                 this.dragCurrent.onDrag(e);
1823                 if(!this.dragCurrent.moveOnly){
1824                     this.fireEvents(e, false);
1825                 }
1826             }
1827
1828             this.stopEvent(e);
1829
1830             return true;
1831         },
1832
1833         /**
1834          * Iterates over all of the DragDrop elements to find ones we are
1835          * hovering over or dropping on
1836          * @method fireEvents
1837          * @param {Event} e the event
1838          * @param {boolean} isDrop is this a drop op or a mouseover op?
1839          * @private
1840          * @static
1841          */
1842         fireEvents: function(e, isDrop) {
1843             var dc = this.dragCurrent;
1844
1845             // If the user did the mouse up outside of the window, we could
1846             // get here even though we have ended the drag.
1847             if (!dc || dc.isLocked()) {
1848                 return;
1849             }
1850
1851             var pt = e.getPoint();
1852
1853             // cache the previous dragOver array
1854             var oldOvers = [];
1855
1856             var outEvts   = [];
1857             var overEvts  = [];
1858             var dropEvts  = [];
1859             var enterEvts = [];
1860
1861             // Check to see if the object(s) we were hovering over is no longer
1862             // being hovered over so we can fire the onDragOut event
1863             for (var i in this.dragOvers) {
1864
1865                 var ddo = this.dragOvers[i];
1866
1867                 if (! this.isTypeOfDD(ddo)) {
1868                     continue;
1869                 }
1870
1871                 if (! this.isOverTarget(pt, ddo, this.mode)) {
1872                     outEvts.push( ddo );
1873                 }
1874
1875                 oldOvers[i] = true;
1876                 delete this.dragOvers[i];
1877             }
1878
1879             for (var sGroup in dc.groups) {
1880
1881                 if ("string" != typeof sGroup) {
1882                     continue;
1883                 }
1884
1885                 for (i in this.ids[sGroup]) {
1886                     var oDD = this.ids[sGroup][i];
1887                     if (! this.isTypeOfDD(oDD)) {
1888                         continue;
1889                     }
1890
1891                     if (oDD.isTarget && !oDD.isLocked() && oDD != dc) {
1892                         if (this.isOverTarget(pt, oDD, this.mode)) {
1893                             // look for drop interactions
1894                             if (isDrop) {
1895                                 dropEvts.push( oDD );
1896                             // look for drag enter and drag over interactions
1897                             } else {
1898
1899                                 // initial drag over: dragEnter fires
1900                                 if (!oldOvers[oDD.id]) {
1901                                     enterEvts.push( oDD );
1902                                 // subsequent drag overs: dragOver fires
1903                                 } else {
1904                                     overEvts.push( oDD );
1905                                 }
1906
1907                                 this.dragOvers[oDD.id] = oDD;
1908                             }
1909                         }
1910                     }
1911                 }
1912             }
1913
1914             if (this.mode) {
1915                 if (outEvts.length) {
1916                     dc.b4DragOut(e, outEvts);
1917                     dc.onDragOut(e, outEvts);
1918                 }
1919
1920                 if (enterEvts.length) {
1921                     dc.onDragEnter(e, enterEvts);
1922                 }
1923
1924                 if (overEvts.length) {
1925                     dc.b4DragOver(e, overEvts);
1926                     dc.onDragOver(e, overEvts);
1927                 }
1928
1929                 if (dropEvts.length) {
1930                     dc.b4DragDrop(e, dropEvts);
1931                     dc.onDragDrop(e, dropEvts);
1932                 }
1933
1934             } else {
1935                 // fire dragout events
1936                 var len = 0;
1937                 for (i=0, len=outEvts.length; i<len; ++i) {
1938                     dc.b4DragOut(e, outEvts[i].id);
1939                     dc.onDragOut(e, outEvts[i].id);
1940                 }
1941
1942                 // fire enter events
1943                 for (i=0,len=enterEvts.length; i<len; ++i) {
1944                     // dc.b4DragEnter(e, oDD.id);
1945                     dc.onDragEnter(e, enterEvts[i].id);
1946                 }
1947
1948                 // fire over events
1949                 for (i=0,len=overEvts.length; i<len; ++i) {
1950                     dc.b4DragOver(e, overEvts[i].id);
1951                     dc.onDragOver(e, overEvts[i].id);
1952                 }
1953
1954                 // fire drop events
1955                 for (i=0, len=dropEvts.length; i<len; ++i) {
1956                     dc.b4DragDrop(e, dropEvts[i].id);
1957                     dc.onDragDrop(e, dropEvts[i].id);
1958                 }
1959
1960             }
1961
1962             // notify about a drop that did not find a target
1963             if (isDrop && !dropEvts.length) {
1964                 dc.onInvalidDrop(e);
1965             }
1966
1967         },
1968
1969         /**
1970          * Helper function for getting the best match from the list of drag
1971          * and drop objects returned by the drag and drop events when we are
1972          * in INTERSECT mode.  It returns either the first object that the
1973          * cursor is over, or the object that has the greatest overlap with
1974          * the dragged element.
1975          * @method getBestMatch
1976          * @param  {DragDrop[]} dds The array of drag and drop objects
1977          * targeted
1978          * @return {DragDrop}       The best single match
1979          * @static
1980          */
1981         getBestMatch: function(dds) {
1982             var winner = null;
1983             // Return null if the input is not what we expect
1984             //if (!dds || !dds.length || dds.length == 0) {
1985                // winner = null;
1986             // If there is only one item, it wins
1987             //} else if (dds.length == 1) {
1988
1989             var len = dds.length;
1990
1991             if (len == 1) {
1992                 winner = dds[0];
1993             } else {
1994                 // Loop through the targeted items
1995                 for (var i=0; i<len; ++i) {
1996                     var dd = dds[i];
1997                     // If the cursor is over the object, it wins.  If the
1998                     // cursor is over multiple matches, the first one we come
1999                     // to wins.
2000                     if (dd.cursorIsOver) {
2001                         winner = dd;
2002                         break;
2003                     // Otherwise the object with the most overlap wins
2004                     } else {
2005                         if (!winner ||
2006                             winner.overlap.getArea() < dd.overlap.getArea()) {
2007                             winner = dd;
2008                         }
2009                     }
2010                 }
2011             }
2012
2013             return winner;
2014         },
2015
2016         /**
2017          * Refreshes the cache of the top-left and bottom-right points of the
2018          * drag and drop objects in the specified group(s).  This is in the
2019          * format that is stored in the drag and drop instance, so typical
2020          * usage is:
2021          * <code>
2022          * Roo.dd.DragDropMgr.refreshCache(ddinstance.groups);
2023          * </code>
2024          * Alternatively:
2025          * <code>
2026          * Roo.dd.DragDropMgr.refreshCache({group1:true, group2:true});
2027          * </code>
2028          * @TODO this really should be an indexed array.  Alternatively this
2029          * method could accept both.
2030          * @method refreshCache
2031          * @param {Object} groups an associative array of groups to refresh
2032          * @static
2033          */
2034         refreshCache: function(groups) {
2035             for (var sGroup in groups) {
2036                 if ("string" != typeof sGroup) {
2037                     continue;
2038                 }
2039                 for (var i in this.ids[sGroup]) {
2040                     var oDD = this.ids[sGroup][i];
2041
2042                     if (this.isTypeOfDD(oDD)) {
2043                     // if (this.isTypeOfDD(oDD) && oDD.isTarget) {
2044                         var loc = this.getLocation(oDD);
2045                         if (loc) {
2046                             this.locationCache[oDD.id] = loc;
2047                         } else {
2048                             delete this.locationCache[oDD.id];
2049                             // this will unregister the drag and drop object if
2050                             // the element is not in a usable state
2051                             // oDD.unreg();
2052                         }
2053                     }
2054                 }
2055             }
2056         },
2057
2058         /**
2059          * This checks to make sure an element exists and is in the DOM.  The
2060          * main purpose is to handle cases where innerHTML is used to remove
2061          * drag and drop objects from the DOM.  IE provides an 'unspecified
2062          * error' when trying to access the offsetParent of such an element
2063          * @method verifyEl
2064          * @param {HTMLElement} el the element to check
2065          * @return {boolean} true if the element looks usable
2066          * @static
2067          */
2068         verifyEl: function(el) {
2069             if (el) {
2070                 var parent;
2071                 if(Roo.isIE){
2072                     try{
2073                         parent = el.offsetParent;
2074                     }catch(e){}
2075                 }else{
2076                     parent = el.offsetParent;
2077                 }
2078                 if (parent) {
2079                     return true;
2080                 }
2081             }
2082
2083             return false;
2084         },
2085
2086         /**
2087          * Returns a Region object containing the drag and drop element's position
2088          * and size, including the padding configured for it
2089          * @method getLocation
2090          * @param {DragDrop} oDD the drag and drop object to get the
2091          *                       location for
2092          * @return {Roo.lib.Region} a Region object representing the total area
2093          *                             the element occupies, including any padding
2094          *                             the instance is configured for.
2095          * @static
2096          */
2097         getLocation: function(oDD) {
2098             if (! this.isTypeOfDD(oDD)) {
2099                 return null;
2100             }
2101
2102             var el = oDD.getEl(), pos, x1, x2, y1, y2, t, r, b, l;
2103
2104             try {
2105                 pos= Roo.lib.Dom.getXY(el);
2106             } catch (e) { }
2107
2108             if (!pos) {
2109                 return null;
2110             }
2111
2112             x1 = pos[0];
2113             x2 = x1 + el.offsetWidth;
2114             y1 = pos[1];
2115             y2 = y1 + el.offsetHeight;
2116
2117             t = y1 - oDD.padding[0];
2118             r = x2 + oDD.padding[1];
2119             b = y2 + oDD.padding[2];
2120             l = x1 - oDD.padding[3];
2121
2122             return new Roo.lib.Region( t, r, b, l );
2123         },
2124
2125         /**
2126          * Checks the cursor location to see if it over the target
2127          * @method isOverTarget
2128          * @param {Roo.lib.Point} pt The point to evaluate
2129          * @param {DragDrop} oTarget the DragDrop object we are inspecting
2130          * @return {boolean} true if the mouse is over the target
2131          * @private
2132          * @static
2133          */
2134         isOverTarget: function(pt, oTarget, intersect) {
2135             // use cache if available
2136             var loc = this.locationCache[oTarget.id];
2137             if (!loc || !this.useCache) {
2138                 loc = this.getLocation(oTarget);
2139                 this.locationCache[oTarget.id] = loc;
2140
2141             }
2142
2143             if (!loc) {
2144                 return false;
2145             }
2146
2147             oTarget.cursorIsOver = loc.contains( pt );
2148
2149             // DragDrop is using this as a sanity check for the initial mousedown
2150             // in this case we are done.  In POINT mode, if the drag obj has no
2151             // contraints, we are also done. Otherwise we need to evaluate the
2152             // location of the target as related to the actual location of the
2153             // dragged element.
2154             var dc = this.dragCurrent;
2155             if (!dc || !dc.getTargetCoord ||
2156                     (!intersect && !dc.constrainX && !dc.constrainY)) {
2157                 return oTarget.cursorIsOver;
2158             }
2159
2160             oTarget.overlap = null;
2161
2162             // Get the current location of the drag element, this is the
2163             // location of the mouse event less the delta that represents
2164             // where the original mousedown happened on the element.  We
2165             // need to consider constraints and ticks as well.
2166             var pos = dc.getTargetCoord(pt.x, pt.y);
2167
2168             var el = dc.getDragEl();
2169             var curRegion = new Roo.lib.Region( pos.y,
2170                                                    pos.x + el.offsetWidth,
2171                                                    pos.y + el.offsetHeight,
2172                                                    pos.x );
2173
2174             var overlap = curRegion.intersect(loc);
2175
2176             if (overlap) {
2177                 oTarget.overlap = overlap;
2178                 return (intersect) ? true : oTarget.cursorIsOver;
2179             } else {
2180                 return false;
2181             }
2182         },
2183
2184         /**
2185          * unload event handler
2186          * @method _onUnload
2187          * @private
2188          * @static
2189          */
2190         _onUnload: function(e, me) {
2191             Roo.dd.DragDropMgr.unregAll();
2192         },
2193
2194         /**
2195          * Cleans up the drag and drop events and objects.
2196          * @method unregAll
2197          * @private
2198          * @static
2199          */
2200         unregAll: function() {
2201
2202             if (this.dragCurrent) {
2203                 this.stopDrag();
2204                 this.dragCurrent = null;
2205             }
2206
2207             this._execOnAll("unreg", []);
2208
2209             for (i in this.elementCache) {
2210                 delete this.elementCache[i];
2211             }
2212
2213             this.elementCache = {};
2214             this.ids = {};
2215         },
2216
2217         /**
2218          * A cache of DOM elements
2219          * @property elementCache
2220          * @private
2221          * @static
2222          */
2223         elementCache: {},
2224
2225         /**
2226          * Get the wrapper for the DOM element specified
2227          * @method getElWrapper
2228          * @param {String} id the id of the element to get
2229          * @return {Roo.dd.DDM.ElementWrapper} the wrapped element
2230          * @private
2231          * @deprecated This wrapper isn't that useful
2232          * @static
2233          */
2234         getElWrapper: function(id) {
2235             var oWrapper = this.elementCache[id];
2236             if (!oWrapper || !oWrapper.el) {
2237                 oWrapper = this.elementCache[id] =
2238                     new this.ElementWrapper(Roo.getDom(id));
2239             }
2240             return oWrapper;
2241         },
2242
2243         /**
2244          * Returns the actual DOM element
2245          * @method getElement
2246          * @param {String} id the id of the elment to get
2247          * @return {Object} The element
2248          * @deprecated use Roo.getDom instead
2249          * @static
2250          */
2251         getElement: function(id) {
2252             return Roo.getDom(id);
2253         },
2254
2255         /**
2256          * Returns the style property for the DOM element (i.e.,
2257          * document.getElById(id).style)
2258          * @method getCss
2259          * @param {String} id the id of the elment to get
2260          * @return {Object} The style property of the element
2261          * @deprecated use Roo.getDom instead
2262          * @static
2263          */
2264         getCss: function(id) {
2265             var el = Roo.getDom(id);
2266             return (el) ? el.style : null;
2267         },
2268
2269         /**
2270          * Inner class for cached elements
2271          * @class DragDropMgr.ElementWrapper
2272          * @for DragDropMgr
2273          * @private
2274          * @deprecated
2275          */
2276         ElementWrapper: function(el) {
2277                 /**
2278                  * The element
2279                  * @property el
2280                  */
2281                 this.el = el || null;
2282                 /**
2283                  * The element id
2284                  * @property id
2285                  */
2286                 this.id = this.el && el.id;
2287                 /**
2288                  * A reference to the style property
2289                  * @property css
2290                  */
2291                 this.css = this.el && el.style;
2292             },
2293
2294         /**
2295          * Returns the X position of an html element
2296          * @method getPosX
2297          * @param el the element for which to get the position
2298          * @return {int} the X coordinate
2299          * @for DragDropMgr
2300          * @deprecated use Roo.lib.Dom.getX instead
2301          * @static
2302          */
2303         getPosX: function(el) {
2304             return Roo.lib.Dom.getX(el);
2305         },
2306
2307         /**
2308          * Returns the Y position of an html element
2309          * @method getPosY
2310          * @param el the element for which to get the position
2311          * @return {int} the Y coordinate
2312          * @deprecated use Roo.lib.Dom.getY instead
2313          * @static
2314          */
2315         getPosY: function(el) {
2316             return Roo.lib.Dom.getY(el);
2317         },
2318
2319         /**
2320          * Swap two nodes.  In IE, we use the native method, for others we
2321          * emulate the IE behavior
2322          * @method swapNode
2323          * @param n1 the first node to swap
2324          * @param n2 the other node to swap
2325          * @static
2326          */
2327         swapNode: function(n1, n2) {
2328             if (n1.swapNode) {
2329                 n1.swapNode(n2);
2330             } else {
2331                 var p = n2.parentNode;
2332                 var s = n2.nextSibling;
2333
2334                 if (s == n1) {
2335                     p.insertBefore(n1, n2);
2336                 } else if (n2 == n1.nextSibling) {
2337                     p.insertBefore(n2, n1);
2338                 } else {
2339                     n1.parentNode.replaceChild(n2, n1);
2340                     p.insertBefore(n1, s);
2341                 }
2342             }
2343         },
2344
2345         /**
2346          * Returns the current scroll position
2347          * @method getScroll
2348          * @private
2349          * @static
2350          */
2351         getScroll: function () {
2352             var t, l, dde=document.documentElement, db=document.body;
2353             if (dde && (dde.scrollTop || dde.scrollLeft)) {
2354                 t = dde.scrollTop;
2355                 l = dde.scrollLeft;
2356             } else if (db) {
2357                 t = db.scrollTop;
2358                 l = db.scrollLeft;
2359             } else {
2360
2361             }
2362             return { top: t, left: l };
2363         },
2364
2365         /**
2366          * Returns the specified element style property
2367          * @method getStyle
2368          * @param {HTMLElement} el          the element
2369          * @param {string}      styleProp   the style property
2370          * @return {string} The value of the style property
2371          * @deprecated use Roo.lib.Dom.getStyle
2372          * @static
2373          */
2374         getStyle: function(el, styleProp) {
2375             return Roo.fly(el).getStyle(styleProp);
2376         },
2377
2378         /**
2379          * Gets the scrollTop
2380          * @method getScrollTop
2381          * @return {int} the document's scrollTop
2382          * @static
2383          */
2384         getScrollTop: function () { return this.getScroll().top; },
2385
2386         /**
2387          * Gets the scrollLeft
2388          * @method getScrollLeft
2389          * @return {int} the document's scrollTop
2390          * @static
2391          */
2392         getScrollLeft: function () { return this.getScroll().left; },
2393
2394         /**
2395          * Sets the x/y position of an element to the location of the
2396          * target element.
2397          * @method moveToEl
2398          * @param {HTMLElement} moveEl      The element to move
2399          * @param {HTMLElement} targetEl    The position reference element
2400          * @static
2401          */
2402         moveToEl: function (moveEl, targetEl) {
2403             var aCoord = Roo.lib.Dom.getXY(targetEl);
2404             Roo.lib.Dom.setXY(moveEl, aCoord);
2405         },
2406
2407         /**
2408          * Numeric array sort function
2409          * @method numericSort
2410          * @static
2411          */
2412         numericSort: function(a, b) { return (a - b); },
2413
2414         /**
2415          * Internal counter
2416          * @property _timeoutCount
2417          * @private
2418          * @static
2419          */
2420         _timeoutCount: 0,
2421
2422         /**
2423          * Trying to make the load order less important.  Without this we get
2424          * an error if this file is loaded before the Event Utility.
2425          * @method _addListeners
2426          * @private
2427          * @static
2428          */
2429         _addListeners: function() {
2430             var DDM = Roo.dd.DDM;
2431             if ( Roo.lib.Event && document ) {
2432                 DDM._onLoad();
2433             } else {
2434                 if (DDM._timeoutCount > 2000) {
2435                 } else {
2436                     setTimeout(DDM._addListeners, 10);
2437                     if (document && document.body) {
2438                         DDM._timeoutCount += 1;
2439                     }
2440                 }
2441             }
2442         },
2443
2444         /**
2445          * Recursively searches the immediate parent and all child nodes for
2446          * the handle element in order to determine wheter or not it was
2447          * clicked.
2448          * @method handleWasClicked
2449          * @param node the html element to inspect
2450          * @static
2451          */
2452         handleWasClicked: function(node, id) {
2453             if (this.isHandle(id, node.id)) {
2454                 return true;
2455             } else {
2456                 // check to see if this is a text node child of the one we want
2457                 var p = node.parentNode;
2458
2459                 while (p) {
2460                     if (this.isHandle(id, p.id)) {
2461                         return true;
2462                     } else {
2463                         p = p.parentNode;
2464                     }
2465                 }
2466             }
2467
2468             return false;
2469         }
2470
2471     };
2472
2473 }();
2474
2475 // shorter alias, save a few bytes
2476 Roo.dd.DDM = Roo.dd.DragDropMgr;
2477 Roo.dd.DDM._addListeners();
2478
2479 }/*
2480  * Based on:
2481  * Ext JS Library 1.1.1
2482  * Copyright(c) 2006-2007, Ext JS, LLC.
2483  *
2484  * Originally Released Under LGPL - original licence link has changed is not relivant.
2485  *
2486  * Fork - LGPL
2487  * <script type="text/javascript">
2488  */
2489
2490 /**
2491  * @class Roo.dd.DD
2492  * A DragDrop implementation where the linked element follows the
2493  * mouse cursor during a drag.
2494  * @extends Roo.dd.DragDrop
2495  * @constructor
2496  * @param {String} id the id of the linked element
2497  * @param {String} sGroup the group of related DragDrop items
2498  * @param {object} config an object containing configurable attributes
2499  *                Valid properties for DD:
2500  *                    scroll
2501  */
2502 Roo.dd.DD = function(id, sGroup, config) {
2503     if (id) {
2504         this.init(id, sGroup, config);
2505     }
2506 };
2507
2508 Roo.extend(Roo.dd.DD, Roo.dd.DragDrop, {
2509
2510     /**
2511      * When set to true, the utility automatically tries to scroll the browser
2512      * window wehn a drag and drop element is dragged near the viewport boundary.
2513      * Defaults to true.
2514      * @property scroll
2515      * @type boolean
2516      */
2517     scroll: true,
2518
2519     /**
2520      * Sets the pointer offset to the distance between the linked element's top
2521      * left corner and the location the element was clicked
2522      * @method autoOffset
2523      * @param {int} iPageX the X coordinate of the click
2524      * @param {int} iPageY the Y coordinate of the click
2525      */
2526     autoOffset: function(iPageX, iPageY) {
2527         var x = iPageX - this.startPageX;
2528         var y = iPageY - this.startPageY;
2529         this.setDelta(x, y);
2530     },
2531
2532     /**
2533      * Sets the pointer offset.  You can call this directly to force the
2534      * offset to be in a particular location (e.g., pass in 0,0 to set it
2535      * to the center of the object)
2536      * @method setDelta
2537      * @param {int} iDeltaX the distance from the left
2538      * @param {int} iDeltaY the distance from the top
2539      */
2540     setDelta: function(iDeltaX, iDeltaY) {
2541         this.deltaX = iDeltaX;
2542         this.deltaY = iDeltaY;
2543     },
2544
2545     /**
2546      * Sets the drag element to the location of the mousedown or click event,
2547      * maintaining the cursor location relative to the location on the element
2548      * that was clicked.  Override this if you want to place the element in a
2549      * location other than where the cursor is.
2550      * @method setDragElPos
2551      * @param {int} iPageX the X coordinate of the mousedown or drag event
2552      * @param {int} iPageY the Y coordinate of the mousedown or drag event
2553      */
2554     setDragElPos: function(iPageX, iPageY) {
2555         // the first time we do this, we are going to check to make sure
2556         // the element has css positioning
2557
2558         var el = this.getDragEl();
2559         this.alignElWithMouse(el, iPageX, iPageY);
2560     },
2561
2562     /**
2563      * Sets the element to the location of the mousedown or click event,
2564      * maintaining the cursor location relative to the location on the element
2565      * that was clicked.  Override this if you want to place the element in a
2566      * location other than where the cursor is.
2567      * @method alignElWithMouse
2568      * @param {HTMLElement} el the element to move
2569      * @param {int} iPageX the X coordinate of the mousedown or drag event
2570      * @param {int} iPageY the Y coordinate of the mousedown or drag event
2571      */
2572     alignElWithMouse: function(el, iPageX, iPageY) {
2573         var oCoord = this.getTargetCoord(iPageX, iPageY);
2574         var fly = el.dom ? el : Roo.fly(el);
2575         if (!this.deltaSetXY) {
2576             var aCoord = [oCoord.x, oCoord.y];
2577             fly.setXY(aCoord);
2578             var newLeft = fly.getLeft(true);
2579             var newTop  = fly.getTop(true);
2580             this.deltaSetXY = [ newLeft - oCoord.x, newTop - oCoord.y ];
2581         } else {
2582             fly.setLeftTop(oCoord.x + this.deltaSetXY[0], oCoord.y + this.deltaSetXY[1]);
2583         }
2584
2585         this.cachePosition(oCoord.x, oCoord.y);
2586         this.autoScroll(oCoord.x, oCoord.y, el.offsetHeight, el.offsetWidth);
2587         return oCoord;
2588     },
2589
2590     /**
2591      * Saves the most recent position so that we can reset the constraints and
2592      * tick marks on-demand.  We need to know this so that we can calculate the
2593      * number of pixels the element is offset from its original position.
2594      * @method cachePosition
2595      * @param iPageX the current x position (optional, this just makes it so we
2596      * don't have to look it up again)
2597      * @param iPageY the current y position (optional, this just makes it so we
2598      * don't have to look it up again)
2599      */
2600     cachePosition: function(iPageX, iPageY) {
2601         if (iPageX) {
2602             this.lastPageX = iPageX;
2603             this.lastPageY = iPageY;
2604         } else {
2605             var aCoord = Roo.lib.Dom.getXY(this.getEl());
2606             this.lastPageX = aCoord[0];
2607             this.lastPageY = aCoord[1];
2608         }
2609     },
2610
2611     /**
2612      * Auto-scroll the window if the dragged object has been moved beyond the
2613      * visible window boundary.
2614      * @method autoScroll
2615      * @param {int} x the drag element's x position
2616      * @param {int} y the drag element's y position
2617      * @param {int} h the height of the drag element
2618      * @param {int} w the width of the drag element
2619      * @private
2620      */
2621     autoScroll: function(x, y, h, w) {
2622
2623         if (this.scroll) {
2624             // The client height
2625             var clientH = Roo.lib.Dom.getViewWidth();
2626
2627             // The client width
2628             var clientW = Roo.lib.Dom.getViewHeight();
2629
2630             // The amt scrolled down
2631             var st = this.DDM.getScrollTop();
2632
2633             // The amt scrolled right
2634             var sl = this.DDM.getScrollLeft();
2635
2636             // Location of the bottom of the element
2637             var bot = h + y;
2638
2639             // Location of the right of the element
2640             var right = w + x;
2641
2642             // The distance from the cursor to the bottom of the visible area,
2643             // adjusted so that we don't scroll if the cursor is beyond the
2644             // element drag constraints
2645             var toBot = (clientH + st - y - this.deltaY);
2646
2647             // The distance from the cursor to the right of the visible area
2648             var toRight = (clientW + sl - x - this.deltaX);
2649
2650
2651             // How close to the edge the cursor must be before we scroll
2652             // var thresh = (document.all) ? 100 : 40;
2653             var thresh = 40;
2654
2655             // How many pixels to scroll per autoscroll op.  This helps to reduce
2656             // clunky scrolling. IE is more sensitive about this ... it needs this
2657             // value to be higher.
2658             var scrAmt = (document.all) ? 80 : 30;
2659
2660             // Scroll down if we are near the bottom of the visible page and the
2661             // obj extends below the crease
2662             if ( bot > clientH && toBot < thresh ) {
2663                 window.scrollTo(sl, st + scrAmt);
2664             }
2665
2666             // Scroll up if the window is scrolled down and the top of the object
2667             // goes above the top border
2668             if ( y < st && st > 0 && y - st < thresh ) {
2669                 window.scrollTo(sl, st - scrAmt);
2670             }
2671
2672             // Scroll right if the obj is beyond the right border and the cursor is
2673             // near the border.
2674             if ( right > clientW && toRight < thresh ) {
2675                 window.scrollTo(sl + scrAmt, st);
2676             }
2677
2678             // Scroll left if the window has been scrolled to the right and the obj
2679             // extends past the left border
2680             if ( x < sl && sl > 0 && x - sl < thresh ) {
2681                 window.scrollTo(sl - scrAmt, st);
2682             }
2683         }
2684     },
2685
2686     /**
2687      * Finds the location the element should be placed if we want to move
2688      * it to where the mouse location less the click offset would place us.
2689      * @method getTargetCoord
2690      * @param {int} iPageX the X coordinate of the click
2691      * @param {int} iPageY the Y coordinate of the click
2692      * @return an object that contains the coordinates (Object.x and Object.y)
2693      * @private
2694      */
2695     getTargetCoord: function(iPageX, iPageY) {
2696
2697
2698         var x = iPageX - this.deltaX;
2699         var y = iPageY - this.deltaY;
2700
2701         if (this.constrainX) {
2702             if (x < this.minX) { x = this.minX; }
2703             if (x > this.maxX) { x = this.maxX; }
2704         }
2705
2706         if (this.constrainY) {
2707             if (y < this.minY) { y = this.minY; }
2708             if (y > this.maxY) { y = this.maxY; }
2709         }
2710
2711         x = this.getTick(x, this.xTicks);
2712         y = this.getTick(y, this.yTicks);
2713
2714
2715         return {x:x, y:y};
2716     },
2717
2718     /*
2719      * Sets up config options specific to this class. Overrides
2720      * Roo.dd.DragDrop, but all versions of this method through the
2721      * inheritance chain are called
2722      */
2723     applyConfig: function() {
2724         Roo.dd.DD.superclass.applyConfig.call(this);
2725         this.scroll = (this.config.scroll !== false);
2726     },
2727
2728     /*
2729      * Event that fires prior to the onMouseDown event.  Overrides
2730      * Roo.dd.DragDrop.
2731      */
2732     b4MouseDown: function(e) {
2733         // this.resetConstraints();
2734         this.autoOffset(e.getPageX(),
2735                             e.getPageY());
2736     },
2737
2738     /*
2739      * Event that fires prior to the onDrag event.  Overrides
2740      * Roo.dd.DragDrop.
2741      */
2742     b4Drag: function(e) {
2743         this.setDragElPos(e.getPageX(),
2744                             e.getPageY());
2745     },
2746
2747     toString: function() {
2748         return ("DD " + this.id);
2749     }
2750
2751     //////////////////////////////////////////////////////////////////////////
2752     // Debugging ygDragDrop events that can be overridden
2753     //////////////////////////////////////////////////////////////////////////
2754     /*
2755     startDrag: function(x, y) {
2756     },
2757
2758     onDrag: function(e) {
2759     },
2760
2761     onDragEnter: function(e, id) {
2762     },
2763
2764     onDragOver: function(e, id) {
2765     },
2766
2767     onDragOut: function(e, id) {
2768     },
2769
2770     onDragDrop: function(e, id) {
2771     },
2772
2773     endDrag: function(e) {
2774     }
2775
2776     */
2777
2778 });/*
2779  * Based on:
2780  * Ext JS Library 1.1.1
2781  * Copyright(c) 2006-2007, Ext JS, LLC.
2782  *
2783  * Originally Released Under LGPL - original licence link has changed is not relivant.
2784  *
2785  * Fork - LGPL
2786  * <script type="text/javascript">
2787  */
2788
2789 /**
2790  * @class Roo.dd.DDProxy
2791  * A DragDrop implementation that inserts an empty, bordered div into
2792  * the document that follows the cursor during drag operations.  At the time of
2793  * the click, the frame div is resized to the dimensions of the linked html
2794  * element, and moved to the exact location of the linked element.
2795  *
2796  * References to the "frame" element refer to the single proxy element that
2797  * was created to be dragged in place of all DDProxy elements on the
2798  * page.
2799  *
2800  * @extends Roo.dd.DD
2801  * @constructor
2802  * @param {String} id the id of the linked html element
2803  * @param {String} sGroup the group of related DragDrop objects
2804  * @param {object} config an object containing configurable attributes
2805  *                Valid properties for DDProxy in addition to those in DragDrop:
2806  *                   resizeFrame, centerFrame, dragElId
2807  */
2808 Roo.dd.DDProxy = function(id, sGroup, config) {
2809     if (id) {
2810         this.init(id, sGroup, config);
2811         this.initFrame();
2812     }
2813 };
2814
2815 /**
2816  * The default drag frame div id
2817  * @property Roo.dd.DDProxy.dragElId
2818  * @type String
2819  * @static
2820  */
2821 Roo.dd.DDProxy.dragElId = "ygddfdiv";
2822
2823 Roo.extend(Roo.dd.DDProxy, Roo.dd.DD, {
2824
2825     /**
2826      * By default we resize the drag frame to be the same size as the element
2827      * we want to drag (this is to get the frame effect).  We can turn it off
2828      * if we want a different behavior.
2829      * @property resizeFrame
2830      * @type boolean
2831      */
2832     resizeFrame: true,
2833
2834     /**
2835      * By default the frame is positioned exactly where the drag element is, so
2836      * we use the cursor offset provided by Roo.dd.DD.  Another option that works only if
2837      * you do not have constraints on the obj is to have the drag frame centered
2838      * around the cursor.  Set centerFrame to true for this effect.
2839      * @property centerFrame
2840      * @type boolean
2841      */
2842     centerFrame: false,
2843
2844     /**
2845      * Creates the proxy element if it does not yet exist
2846      * @method createFrame
2847      */
2848     createFrame: function() {
2849         var self = this;
2850         var body = document.body;
2851
2852         if (!body || !body.firstChild) {
2853             setTimeout( function() { self.createFrame(); }, 50 );
2854             return;
2855         }
2856
2857         var div = this.getDragEl();
2858
2859         if (!div) {
2860             div    = document.createElement("div");
2861             div.id = this.dragElId;
2862             var s  = div.style;
2863
2864             s.position   = "absolute";
2865             s.visibility = "hidden";
2866             s.cursor     = "move";
2867             s.border     = "2px solid #aaa";
2868             s.zIndex     = 999;
2869
2870             // appendChild can blow up IE if invoked prior to the window load event
2871             // while rendering a table.  It is possible there are other scenarios
2872             // that would cause this to happen as well.
2873             body.insertBefore(div, body.firstChild);
2874         }
2875     },
2876
2877     /**
2878      * Initialization for the drag frame element.  Must be called in the
2879      * constructor of all subclasses
2880      * @method initFrame
2881      */
2882     initFrame: function() {
2883         this.createFrame();
2884     },
2885
2886     applyConfig: function() {
2887         Roo.dd.DDProxy.superclass.applyConfig.call(this);
2888
2889         this.resizeFrame = (this.config.resizeFrame !== false);
2890         this.centerFrame = (this.config.centerFrame);
2891         this.setDragElId(this.config.dragElId || Roo.dd.DDProxy.dragElId);
2892     },
2893
2894     /**
2895      * Resizes the drag frame to the dimensions of the clicked object, positions
2896      * it over the object, and finally displays it
2897      * @method showFrame
2898      * @param {int} iPageX X click position
2899      * @param {int} iPageY Y click position
2900      * @private
2901      */
2902     showFrame: function(iPageX, iPageY) {
2903         var el = this.getEl();
2904         var dragEl = this.getDragEl();
2905         var s = dragEl.style;
2906
2907         this._resizeProxy();
2908
2909         if (this.centerFrame) {
2910             this.setDelta( Math.round(parseInt(s.width,  10)/2),
2911                            Math.round(parseInt(s.height, 10)/2) );
2912         }
2913
2914         this.setDragElPos(iPageX, iPageY);
2915
2916         Roo.fly(dragEl).show();
2917     },
2918
2919     /**
2920      * The proxy is automatically resized to the dimensions of the linked
2921      * element when a drag is initiated, unless resizeFrame is set to false
2922      * @method _resizeProxy
2923      * @private
2924      */
2925     _resizeProxy: function() {
2926         if (this.resizeFrame) {
2927             var el = this.getEl();
2928             Roo.fly(this.getDragEl()).setSize(el.offsetWidth, el.offsetHeight);
2929         }
2930     },
2931
2932     // overrides Roo.dd.DragDrop
2933     b4MouseDown: function(e) {
2934         var x = e.getPageX();
2935         var y = e.getPageY();
2936         this.autoOffset(x, y);
2937         this.setDragElPos(x, y);
2938     },
2939
2940     // overrides Roo.dd.DragDrop
2941     b4StartDrag: function(x, y) {
2942         // show the drag frame
2943         this.showFrame(x, y);
2944     },
2945
2946     // overrides Roo.dd.DragDrop
2947     b4EndDrag: function(e) {
2948         Roo.fly(this.getDragEl()).hide();
2949     },
2950
2951     // overrides Roo.dd.DragDrop
2952     // By default we try to move the element to the last location of the frame.
2953     // This is so that the default behavior mirrors that of Roo.dd.DD.
2954     endDrag: function(e) {
2955
2956         var lel = this.getEl();
2957         var del = this.getDragEl();
2958
2959         // Show the drag frame briefly so we can get its position
2960         del.style.visibility = "";
2961
2962         this.beforeMove();
2963         // Hide the linked element before the move to get around a Safari
2964         // rendering bug.
2965         lel.style.visibility = "hidden";
2966         Roo.dd.DDM.moveToEl(lel, del);
2967         del.style.visibility = "hidden";
2968         lel.style.visibility = "";
2969
2970         this.afterDrag();
2971     },
2972
2973     beforeMove : function(){
2974
2975     },
2976
2977     afterDrag : function(){
2978
2979     },
2980
2981     toString: function() {
2982         return ("DDProxy " + this.id);
2983     }
2984
2985 });
2986 /*
2987  * Based on:
2988  * Ext JS Library 1.1.1
2989  * Copyright(c) 2006-2007, Ext JS, LLC.
2990  *
2991  * Originally Released Under LGPL - original licence link has changed is not relivant.
2992  *
2993  * Fork - LGPL
2994  * <script type="text/javascript">
2995  */
2996
2997  /**
2998  * @class Roo.dd.DDTarget
2999  * A DragDrop implementation that does not move, but can be a drop
3000  * target.  You would get the same result by simply omitting implementation
3001  * for the event callbacks, but this way we reduce the processing cost of the
3002  * event listener and the callbacks.
3003  * @extends Roo.dd.DragDrop
3004  * @constructor
3005  * @param {String} id the id of the element that is a drop target
3006  * @param {String} sGroup the group of related DragDrop objects
3007  * @param {object} config an object containing configurable attributes
3008  *                 Valid properties for DDTarget in addition to those in
3009  *                 DragDrop:
3010  *                    none
3011  */
3012 Roo.dd.DDTarget = function(id, sGroup, config) {
3013     if (id) {
3014         this.initTarget(id, sGroup, config);
3015     }
3016     if (config.listeners || config.events) { 
3017        Roo.dd.DragDrop.superclass.constructor.call(this,  { 
3018             listeners : config.listeners || {}, 
3019             events : config.events || {} 
3020         });    
3021     }
3022 };
3023
3024 // Roo.dd.DDTarget.prototype = new Roo.dd.DragDrop();
3025 Roo.extend(Roo.dd.DDTarget, Roo.dd.DragDrop, {
3026     toString: function() {
3027         return ("DDTarget " + this.id);
3028     }
3029 });
3030 /*
3031  * Based on:
3032  * Ext JS Library 1.1.1
3033  * Copyright(c) 2006-2007, Ext JS, LLC.
3034  *
3035  * Originally Released Under LGPL - original licence link has changed is not relivant.
3036  *
3037  * Fork - LGPL
3038  * <script type="text/javascript">
3039  */
3040  
3041
3042 /**
3043  * @class Roo.dd.ScrollManager
3044  * Provides automatic scrolling of overflow regions in the page during drag operations.<br><br>
3045  * <b>Note: This class uses "Point Mode" and is untested in "Intersect Mode".</b>
3046  * @singleton
3047  */
3048 Roo.dd.ScrollManager = function(){
3049     var ddm = Roo.dd.DragDropMgr;
3050     var els = {};
3051     var dragEl = null;
3052     var proc = {};
3053     
3054     
3055     
3056     var onStop = function(e){
3057         dragEl = null;
3058         clearProc();
3059     };
3060     
3061     var triggerRefresh = function(){
3062         if(ddm.dragCurrent){
3063              ddm.refreshCache(ddm.dragCurrent.groups);
3064         }
3065     };
3066     
3067     var doScroll = function(){
3068         if(ddm.dragCurrent){
3069             var dds = Roo.dd.ScrollManager;
3070             if(!dds.animate){
3071                 if(proc.el.scroll(proc.dir, dds.increment)){
3072                     triggerRefresh();
3073                 }
3074             }else{
3075                 proc.el.scroll(proc.dir, dds.increment, true, dds.animDuration, triggerRefresh);
3076             }
3077         }
3078     };
3079     
3080     var clearProc = function(){
3081         if(proc.id){
3082             clearInterval(proc.id);
3083         }
3084         proc.id = 0;
3085         proc.el = null;
3086         proc.dir = "";
3087     };
3088     
3089     var startProc = function(el, dir){
3090          Roo.log('scroll startproc');
3091         clearProc();
3092         proc.el = el;
3093         proc.dir = dir;
3094         proc.id = setInterval(doScroll, Roo.dd.ScrollManager.frequency);
3095     };
3096     
3097     var onFire = function(e, isDrop){
3098        
3099         if(isDrop || !ddm.dragCurrent){ return; }
3100         var dds = Roo.dd.ScrollManager;
3101         if(!dragEl || dragEl != ddm.dragCurrent){
3102             dragEl = ddm.dragCurrent;
3103             // refresh regions on drag start
3104             dds.refreshCache();
3105         }
3106         
3107         var xy = Roo.lib.Event.getXY(e);
3108         var pt = new Roo.lib.Point(xy[0], xy[1]);
3109         for(var id in els){
3110             var el = els[id], r = el._region;
3111             if(r && r.contains(pt) && el.isScrollable()){
3112                 if(r.bottom - pt.y <= dds.thresh){
3113                     if(proc.el != el){
3114                         startProc(el, "down");
3115                     }
3116                     return;
3117                 }else if(r.right - pt.x <= dds.thresh){
3118                     if(proc.el != el){
3119                         startProc(el, "left");
3120                     }
3121                     return;
3122                 }else if(pt.y - r.top <= dds.thresh){
3123                     if(proc.el != el){
3124                         startProc(el, "up");
3125                     }
3126                     return;
3127                 }else if(pt.x - r.left <= dds.thresh){
3128                     if(proc.el != el){
3129                         startProc(el, "right");
3130                     }
3131                     return;
3132                 }
3133             }
3134         }
3135         clearProc();
3136     };
3137     
3138     ddm.fireEvents = ddm.fireEvents.createSequence(onFire, ddm);
3139     ddm.stopDrag = ddm.stopDrag.createSequence(onStop, ddm);
3140     
3141     return {
3142         /**
3143          * Registers new overflow element(s) to auto scroll
3144          * @param {String/HTMLElement/Element/Array} el The id of or the element to be scrolled or an array of either
3145          */
3146         register : function(el){
3147             if(el instanceof Array){
3148                 for(var i = 0, len = el.length; i < len; i++) {
3149                         this.register(el[i]);
3150                 }
3151             }else{
3152                 el = Roo.get(el);
3153                 els[el.id] = el;
3154             }
3155             Roo.dd.ScrollManager.els = els;
3156         },
3157         
3158         /**
3159          * Unregisters overflow element(s) so they are no longer scrolled
3160          * @param {String/HTMLElement/Element/Array} el The id of or the element to be removed or an array of either
3161          */
3162         unregister : function(el){
3163             if(el instanceof Array){
3164                 for(var i = 0, len = el.length; i < len; i++) {
3165                         this.unregister(el[i]);
3166                 }
3167             }else{
3168                 el = Roo.get(el);
3169                 delete els[el.id];
3170             }
3171         },
3172         
3173         /**
3174          * The number of pixels from the edge of a container the pointer needs to be to 
3175          * trigger scrolling (defaults to 25)
3176          * @type Number
3177          */
3178         thresh : 25,
3179         
3180         /**
3181          * The number of pixels to scroll in each scroll increment (defaults to 50)
3182          * @type Number
3183          */
3184         increment : 100,
3185         
3186         /**
3187          * The frequency of scrolls in milliseconds (defaults to 500)
3188          * @type Number
3189          */
3190         frequency : 500,
3191         
3192         /**
3193          * True to animate the scroll (defaults to true)
3194          * @type Boolean
3195          */
3196         animate: true,
3197         
3198         /**
3199          * The animation duration in seconds - 
3200          * MUST BE less than Roo.dd.ScrollManager.frequency! (defaults to .4)
3201          * @type Number
3202          */
3203         animDuration: .4,
3204         
3205         /**
3206          * Manually trigger a cache refresh.
3207          */
3208         refreshCache : function(){
3209             for(var id in els){
3210                 if(typeof els[id] == 'object'){ // for people extending the object prototype
3211                     els[id]._region = els[id].getRegion();
3212                 }
3213             }
3214         }
3215     };
3216 }();/*
3217  * Based on:
3218  * Ext JS Library 1.1.1
3219  * Copyright(c) 2006-2007, Ext JS, LLC.
3220  *
3221  * Originally Released Under LGPL - original licence link has changed is not relivant.
3222  *
3223  * Fork - LGPL
3224  * <script type="text/javascript">
3225  */
3226  
3227
3228 /**
3229  * @class Roo.dd.Registry
3230  * Provides easy access to all drag drop components that are registered on a page.  Items can be retrieved either
3231  * directly by DOM node id, or by passing in the drag drop event that occurred and looking up the event target.
3232  * @singleton
3233  */
3234 Roo.dd.Registry = function(){
3235     var elements = {}; 
3236     var handles = {}; 
3237     var autoIdSeed = 0;
3238
3239     var getId = function(el, autogen){
3240         if(typeof el == "string"){
3241             return el;
3242         }
3243         var id = el.id;
3244         if(!id && autogen !== false){
3245             id = "roodd-" + (++autoIdSeed);
3246             el.id = id;
3247         }
3248         return id;
3249     };
3250     
3251     return {
3252     /**
3253      * Register a drag drop element
3254      * @param {String|HTMLElement} element The id or DOM node to register
3255      * @param {Object} data (optional) A custom data object that will be passed between the elements that are involved
3256      * in drag drop operations.  You can populate this object with any arbitrary properties that your own code
3257      * knows how to interpret, plus there are some specific properties known to the Registry that should be
3258      * populated in the data object (if applicable):
3259      * <pre>
3260 Value      Description<br />
3261 ---------  ------------------------------------------<br />
3262 handles    Array of DOM nodes that trigger dragging<br />
3263            for the element being registered<br />
3264 isHandle   True if the element passed in triggers<br />
3265            dragging itself, else false
3266 </pre>
3267      */
3268         register : function(el, data){
3269             data = data || {};
3270             if(typeof el == "string"){
3271                 el = document.getElementById(el);
3272             }
3273             data.ddel = el;
3274             elements[getId(el)] = data;
3275             if(data.isHandle !== false){
3276                 handles[data.ddel.id] = data;
3277             }
3278             if(data.handles){
3279                 var hs = data.handles;
3280                 for(var i = 0, len = hs.length; i < len; i++){
3281                         handles[getId(hs[i])] = data;
3282                 }
3283             }
3284         },
3285
3286     /**
3287      * Unregister a drag drop element
3288      * @param {String|HTMLElement}  element The id or DOM node to unregister
3289      */
3290         unregister : function(el){
3291             var id = getId(el, false);
3292             var data = elements[id];
3293             if(data){
3294                 delete elements[id];
3295                 if(data.handles){
3296                     var hs = data.handles;
3297                     for(var i = 0, len = hs.length; i < len; i++){
3298                         delete handles[getId(hs[i], false)];
3299                     }
3300                 }
3301             }
3302         },
3303
3304     /**
3305      * Returns the handle registered for a DOM Node by id
3306      * @param {String|HTMLElement} id The DOM node or id to look up
3307      * @return {Object} handle The custom handle data
3308      */
3309         getHandle : function(id){
3310             if(typeof id != "string"){ // must be element?
3311                 id = id.id;
3312             }
3313             return handles[id];
3314         },
3315
3316     /**
3317      * Returns the handle that is registered for the DOM node that is the target of the event
3318      * @param {Event} e The event
3319      * @return {Object} handle The custom handle data
3320      */
3321         getHandleFromEvent : function(e){
3322             var t = Roo.lib.Event.getTarget(e);
3323             return t ? handles[t.id] : null;
3324         },
3325
3326     /**
3327      * Returns a custom data object that is registered for a DOM node by id
3328      * @param {String|HTMLElement} id The DOM node or id to look up
3329      * @return {Object} data The custom data
3330      */
3331         getTarget : function(id){
3332             if(typeof id != "string"){ // must be element?
3333                 id = id.id;
3334             }
3335             return elements[id];
3336         },
3337
3338     /**
3339      * Returns a custom data object that is registered for the DOM node that is the target of the event
3340      * @param {Event} e The event
3341      * @return {Object} data The custom data
3342      */
3343         getTargetFromEvent : function(e){
3344             var t = Roo.lib.Event.getTarget(e);
3345             return t ? elements[t.id] || handles[t.id] : null;
3346         }
3347     };
3348 }();/*
3349  * Based on:
3350  * Ext JS Library 1.1.1
3351  * Copyright(c) 2006-2007, Ext JS, LLC.
3352  *
3353  * Originally Released Under LGPL - original licence link has changed is not relivant.
3354  *
3355  * Fork - LGPL
3356  * <script type="text/javascript">
3357  */
3358  
3359
3360 /**
3361  * @class Roo.dd.StatusProxy
3362  * A specialized drag proxy that supports a drop status icon, {@link Roo.Layer} styles and auto-repair.  This is the
3363  * default drag proxy used by all Roo.dd components.
3364  * @constructor
3365  * @param {Object} config
3366  */
3367 Roo.dd.StatusProxy = function(config){
3368     Roo.apply(this, config);
3369     this.id = this.id || Roo.id();
3370     this.el = new Roo.Layer({
3371         dh: {
3372             id: this.id, tag: "div", cls: "x-dd-drag-proxy "+this.dropNotAllowed, children: [
3373                 {tag: "div", cls: "x-dd-drop-icon"},
3374                 {tag: "div", cls: "x-dd-drag-ghost"}
3375             ]
3376         }, 
3377         shadow: !config || config.shadow !== false
3378     });
3379     this.ghost = Roo.get(this.el.dom.childNodes[1]);
3380     this.dropStatus = this.dropNotAllowed;
3381 };
3382
3383 Roo.dd.StatusProxy.prototype = {
3384     /**
3385      * @cfg {String} dropAllowed
3386      * The CSS class to apply to the status element when drop is allowed (defaults to "x-dd-drop-ok").
3387      */
3388     dropAllowed : "x-dd-drop-ok",
3389     /**
3390      * @cfg {String} dropNotAllowed
3391      * The CSS class to apply to the status element when drop is not allowed (defaults to "x-dd-drop-nodrop").
3392      */
3393     dropNotAllowed : "x-dd-drop-nodrop",
3394
3395     /**
3396      * Updates the proxy's visual element to indicate the status of whether or not drop is allowed
3397      * over the current target element.
3398      * @param {String} cssClass The css class for the new drop status indicator image
3399      */
3400     setStatus : function(cssClass){
3401         cssClass = cssClass || this.dropNotAllowed;
3402         if(this.dropStatus != cssClass){
3403             this.el.replaceClass(this.dropStatus, cssClass);
3404             this.dropStatus = cssClass;
3405         }
3406     },
3407
3408     /**
3409      * Resets the status indicator to the default dropNotAllowed value
3410      * @param {Boolean} clearGhost True to also remove all content from the ghost, false to preserve it
3411      */
3412     reset : function(clearGhost){
3413         this.el.dom.className = "x-dd-drag-proxy " + this.dropNotAllowed;
3414         this.dropStatus = this.dropNotAllowed;
3415         if(clearGhost){
3416             this.ghost.update("");
3417         }
3418     },
3419
3420     /**
3421      * Updates the contents of the ghost element
3422      * @param {String} html The html that will replace the current innerHTML of the ghost element
3423      */
3424     update : function(html){
3425         if(typeof html == "string"){
3426             this.ghost.update(html);
3427         }else{
3428             this.ghost.update("");
3429             html.style.margin = "0";
3430             this.ghost.dom.appendChild(html);
3431         }
3432         // ensure float = none set?? cant remember why though.
3433         var el = this.ghost.dom.firstChild;
3434                 if(el){
3435                         Roo.fly(el).setStyle('float', 'none');
3436                 }
3437     },
3438     
3439     /**
3440      * Returns the underlying proxy {@link Roo.Layer}
3441      * @return {Roo.Layer} el
3442     */
3443     getEl : function(){
3444         return this.el;
3445     },
3446
3447     /**
3448      * Returns the ghost element
3449      * @return {Roo.Element} el
3450      */
3451     getGhost : function(){
3452         return this.ghost;
3453     },
3454
3455     /**
3456      * Hides the proxy
3457      * @param {Boolean} clear True to reset the status and clear the ghost contents, false to preserve them
3458      */
3459     hide : function(clear){
3460         this.el.hide();
3461         if(clear){
3462             this.reset(true);
3463         }
3464     },
3465
3466     /**
3467      * Stops the repair animation if it's currently running
3468      */
3469     stop : function(){
3470         if(this.anim && this.anim.isAnimated && this.anim.isAnimated()){
3471             this.anim.stop();
3472         }
3473     },
3474
3475     /**
3476      * Displays this proxy
3477      */
3478     show : function(){
3479         this.el.show();
3480     },
3481
3482     /**
3483      * Force the Layer to sync its shadow and shim positions to the element
3484      */
3485     sync : function(){
3486         this.el.sync();
3487     },
3488
3489     /**
3490      * Causes the proxy to return to its position of origin via an animation.  Should be called after an
3491      * invalid drop operation by the item being dragged.
3492      * @param {Array} xy The XY position of the element ([x, y])
3493      * @param {Function} callback The function to call after the repair is complete
3494      * @param {Object} scope The scope in which to execute the callback
3495      */
3496     repair : function(xy, callback, scope){
3497         this.callback = callback;
3498         this.scope = scope;
3499         if(xy && this.animRepair !== false){
3500             this.el.addClass("x-dd-drag-repair");
3501             this.el.hideUnders(true);
3502             this.anim = this.el.shift({
3503                 duration: this.repairDuration || .5,
3504                 easing: 'easeOut',
3505                 xy: xy,
3506                 stopFx: true,
3507                 callback: this.afterRepair,
3508                 scope: this
3509             });
3510         }else{
3511             this.afterRepair();
3512         }
3513     },
3514
3515     // private
3516     afterRepair : function(){
3517         this.hide(true);
3518         if(typeof this.callback == "function"){
3519             this.callback.call(this.scope || this);
3520         }
3521         this.callback = null;
3522         this.scope = null;
3523     }
3524 };/*
3525  * Based on:
3526  * Ext JS Library 1.1.1
3527  * Copyright(c) 2006-2007, Ext JS, LLC.
3528  *
3529  * Originally Released Under LGPL - original licence link has changed is not relivant.
3530  *
3531  * Fork - LGPL
3532  * <script type="text/javascript">
3533  */
3534
3535 /**
3536  * @class Roo.dd.DragSource
3537  * @extends Roo.dd.DDProxy
3538  * A simple class that provides the basic implementation needed to make any element draggable.
3539  * @constructor
3540  * @param {String/HTMLElement/Element} el The container element
3541  * @param {Object} config
3542  */
3543 Roo.dd.DragSource = function(el, config){
3544     this.el = Roo.get(el);
3545     this.dragData = {};
3546     
3547     Roo.apply(this, config);
3548     
3549     if(!this.proxy){
3550         this.proxy = new Roo.dd.StatusProxy();
3551     }
3552
3553     Roo.dd.DragSource.superclass.constructor.call(this, this.el.dom, this.ddGroup || this.group,
3554           {dragElId : this.proxy.id, resizeFrame: false, isTarget: false, scroll: this.scroll === true});
3555     
3556     this.dragging = false;
3557 };
3558
3559 Roo.extend(Roo.dd.DragSource, Roo.dd.DDProxy, {
3560     /**
3561      * @cfg {String} dropAllowed
3562      * The CSS class returned to the drag source when drop is allowed (defaults to "x-dd-drop-ok").
3563      */
3564     dropAllowed : "x-dd-drop-ok",
3565     /**
3566      * @cfg {String} dropNotAllowed
3567      * The CSS class returned to the drag source when drop is not allowed (defaults to "x-dd-drop-nodrop").
3568      */
3569     dropNotAllowed : "x-dd-drop-nodrop",
3570
3571     /**
3572      * Returns the data object associated with this drag source
3573      * @return {Object} data An object containing arbitrary data
3574      */
3575     getDragData : function(e){
3576         return this.dragData;
3577     },
3578
3579     // private
3580     onDragEnter : function(e, id){
3581         var target = Roo.dd.DragDropMgr.getDDById(id);
3582         this.cachedTarget = target;
3583         if(this.beforeDragEnter(target, e, id) !== false){
3584             if(target.isNotifyTarget){
3585                 var status = target.notifyEnter(this, e, this.dragData);
3586                 this.proxy.setStatus(status);
3587             }else{
3588                 this.proxy.setStatus(this.dropAllowed);
3589             }
3590             
3591             if(this.afterDragEnter){
3592                 /**
3593                  * An empty function by default, but provided so that you can perform a custom action
3594                  * when the dragged item enters the drop target by providing an implementation.
3595                  * @param {Roo.dd.DragDrop} target The drop target
3596                  * @param {Event} e The event object
3597                  * @param {String} id The id of the dragged element
3598                  * @method afterDragEnter
3599                  */
3600                 this.afterDragEnter(target, e, id);
3601             }
3602         }
3603     },
3604
3605     /**
3606      * An empty function by default, but provided so that you can perform a custom action
3607      * before the dragged item enters the drop target and optionally cancel the onDragEnter.
3608      * @param {Roo.dd.DragDrop} target The drop target
3609      * @param {Event} e The event object
3610      * @param {String} id The id of the dragged element
3611      * @return {Boolean} isValid True if the drag event is valid, else false to cancel
3612      */
3613     beforeDragEnter : function(target, e, id){
3614         return true;
3615     },
3616
3617     // private
3618     alignElWithMouse: function() {
3619         Roo.dd.DragSource.superclass.alignElWithMouse.apply(this, arguments);
3620         this.proxy.sync();
3621     },
3622
3623     // private
3624     onDragOver : function(e, id){
3625         var target = this.cachedTarget || Roo.dd.DragDropMgr.getDDById(id);
3626         if(this.beforeDragOver(target, e, id) !== false){
3627             if(target.isNotifyTarget){
3628                 var status = target.notifyOver(this, e, this.dragData);
3629                 this.proxy.setStatus(status);
3630             }
3631
3632             if(this.afterDragOver){
3633                 /**
3634                  * An empty function by default, but provided so that you can perform a custom action
3635                  * while the dragged item is over the drop target by providing an implementation.
3636                  * @param {Roo.dd.DragDrop} target The drop target
3637                  * @param {Event} e The event object
3638                  * @param {String} id The id of the dragged element
3639                  * @method afterDragOver
3640                  */
3641                 this.afterDragOver(target, e, id);
3642             }
3643         }
3644     },
3645
3646     /**
3647      * An empty function by default, but provided so that you can perform a custom action
3648      * while the dragged item is over the drop target and optionally cancel the onDragOver.
3649      * @param {Roo.dd.DragDrop} target The drop target
3650      * @param {Event} e The event object
3651      * @param {String} id The id of the dragged element
3652      * @return {Boolean} isValid True if the drag event is valid, else false to cancel
3653      */
3654     beforeDragOver : function(target, e, id){
3655         return true;
3656     },
3657
3658     // private
3659     onDragOut : function(e, id){
3660         var target = this.cachedTarget || Roo.dd.DragDropMgr.getDDById(id);
3661         if(this.beforeDragOut(target, e, id) !== false){
3662             if(target.isNotifyTarget){
3663                 target.notifyOut(this, e, this.dragData);
3664             }
3665             this.proxy.reset();
3666             if(this.afterDragOut){
3667                 /**
3668                  * An empty function by default, but provided so that you can perform a custom action
3669                  * after the dragged item is dragged out of the target without dropping.
3670                  * @param {Roo.dd.DragDrop} target The drop target
3671                  * @param {Event} e The event object
3672                  * @param {String} id The id of the dragged element
3673                  * @method afterDragOut
3674                  */
3675                 this.afterDragOut(target, e, id);
3676             }
3677         }
3678         this.cachedTarget = null;
3679     },
3680
3681     /**
3682      * An empty function by default, but provided so that you can perform a custom action before the dragged
3683      * item is dragged out of the target without dropping, and optionally cancel the onDragOut.
3684      * @param {Roo.dd.DragDrop} target The drop target
3685      * @param {Event} e The event object
3686      * @param {String} id The id of the dragged element
3687      * @return {Boolean} isValid True if the drag event is valid, else false to cancel
3688      */
3689     beforeDragOut : function(target, e, id){
3690         return true;
3691     },
3692     
3693     // private
3694     onDragDrop : function(e, id){
3695         var target = this.cachedTarget || Roo.dd.DragDropMgr.getDDById(id);
3696         if(this.beforeDragDrop(target, e, id) !== false){
3697             if(target.isNotifyTarget){
3698                 if(target.notifyDrop(this, e, this.dragData)){ // valid drop?
3699                     this.onValidDrop(target, e, id);
3700                 }else{
3701                     this.onInvalidDrop(target, e, id);
3702                 }
3703             }else{
3704                 this.onValidDrop(target, e, id);
3705             }
3706             
3707             if(this.afterDragDrop){
3708                 /**
3709                  * An empty function by default, but provided so that you can perform a custom action
3710                  * after a valid drag drop has occurred by providing an implementation.
3711                  * @param {Roo.dd.DragDrop} target The drop target
3712                  * @param {Event} e The event object
3713                  * @param {String} id The id of the dropped element
3714                  * @method afterDragDrop
3715                  */
3716                 this.afterDragDrop(target, e, id);
3717             }
3718         }
3719         delete this.cachedTarget;
3720     },
3721
3722     /**
3723      * An empty function by default, but provided so that you can perform a custom action before the dragged
3724      * item is dropped onto the target and optionally cancel the onDragDrop.
3725      * @param {Roo.dd.DragDrop} target The drop target
3726      * @param {Event} e The event object
3727      * @param {String} id The id of the dragged element
3728      * @return {Boolean} isValid True if the drag drop event is valid, else false to cancel
3729      */
3730     beforeDragDrop : function(target, e, id){
3731         return true;
3732     },
3733
3734     // private
3735     onValidDrop : function(target, e, id){
3736         this.hideProxy();
3737         if(this.afterValidDrop){
3738             /**
3739              * An empty function by default, but provided so that you can perform a custom action
3740              * after a valid drop has occurred by providing an implementation.
3741              * @param {Object} target The target DD 
3742              * @param {Event} e The event object
3743              * @param {String} id The id of the dropped element
3744              * @method afterInvalidDrop
3745              */
3746             this.afterValidDrop(target, e, id);
3747         }
3748     },
3749
3750     // private
3751     getRepairXY : function(e, data){
3752         return this.el.getXY();  
3753     },
3754
3755     // private
3756     onInvalidDrop : function(target, e, id){
3757         this.beforeInvalidDrop(target, e, id);
3758         if(this.cachedTarget){
3759             if(this.cachedTarget.isNotifyTarget){
3760                 this.cachedTarget.notifyOut(this, e, this.dragData);
3761             }
3762             this.cacheTarget = null;
3763         }
3764         this.proxy.repair(this.getRepairXY(e, this.dragData), this.afterRepair, this);
3765
3766         if(this.afterInvalidDrop){
3767             /**
3768              * An empty function by default, but provided so that you can perform a custom action
3769              * after an invalid drop has occurred by providing an implementation.
3770              * @param {Event} e The event object
3771              * @param {String} id The id of the dropped element
3772              * @method afterInvalidDrop
3773              */
3774             this.afterInvalidDrop(e, id);
3775         }
3776     },
3777
3778     // private
3779     afterRepair : function(){
3780         if(Roo.enableFx){
3781             this.el.highlight(this.hlColor || "c3daf9");
3782         }
3783         this.dragging = false;
3784     },
3785
3786     /**
3787      * An empty function by default, but provided so that you can perform a custom action after an invalid
3788      * drop has occurred.
3789      * @param {Roo.dd.DragDrop} target The drop target
3790      * @param {Event} e The event object
3791      * @param {String} id The id of the dragged element
3792      * @return {Boolean} isValid True if the invalid drop should proceed, else false to cancel
3793      */
3794     beforeInvalidDrop : function(target, e, id){
3795         return true;
3796     },
3797
3798     // private
3799     handleMouseDown : function(e){
3800         if(this.dragging) {
3801             return;
3802         }
3803         var data = this.getDragData(e);
3804         if(data && this.onBeforeDrag(data, e) !== false){
3805             this.dragData = data;
3806             this.proxy.stop();
3807             Roo.dd.DragSource.superclass.handleMouseDown.apply(this, arguments);
3808         } 
3809     },
3810
3811     /**
3812      * An empty function by default, but provided so that you can perform a custom action before the initial
3813      * drag event begins and optionally cancel it.
3814      * @param {Object} data An object containing arbitrary data to be shared with drop targets
3815      * @param {Event} e The event object
3816      * @return {Boolean} isValid True if the drag event is valid, else false to cancel
3817      */
3818     onBeforeDrag : function(data, e){
3819         return true;
3820     },
3821
3822     /**
3823      * An empty function by default, but provided so that you can perform a custom action once the initial
3824      * drag event has begun.  The drag cannot be canceled from this function.
3825      * @param {Number} x The x position of the click on the dragged object
3826      * @param {Number} y The y position of the click on the dragged object
3827      */
3828     onStartDrag : Roo.emptyFn,
3829
3830     // private - YUI override
3831     startDrag : function(x, y){
3832         this.proxy.reset();
3833         this.dragging = true;
3834         this.proxy.update("");
3835         this.onInitDrag(x, y);
3836         this.proxy.show();
3837     },
3838
3839     // private
3840     onInitDrag : function(x, y){
3841         var clone = this.el.dom.cloneNode(true);
3842         clone.id = Roo.id(); // prevent duplicate ids
3843         this.proxy.update(clone);
3844         this.onStartDrag(x, y);
3845         return true;
3846     },
3847
3848     /**
3849      * Returns the drag source's underlying {@link Roo.dd.StatusProxy}
3850      * @return {Roo.dd.StatusProxy} proxy The StatusProxy
3851      */
3852     getProxy : function(){
3853         return this.proxy;  
3854     },
3855
3856     /**
3857      * Hides the drag source's {@link Roo.dd.StatusProxy}
3858      */
3859     hideProxy : function(){
3860         this.proxy.hide();  
3861         this.proxy.reset(true);
3862         this.dragging = false;
3863     },
3864
3865     // private
3866     triggerCacheRefresh : function(){
3867         Roo.dd.DDM.refreshCache(this.groups);
3868     },
3869
3870     // private - override to prevent hiding
3871     b4EndDrag: function(e) {
3872     },
3873
3874     // private - override to prevent moving
3875     endDrag : function(e){
3876         this.onEndDrag(this.dragData, e);
3877     },
3878
3879     // private
3880     onEndDrag : function(data, e){
3881     },
3882     
3883     // private - pin to cursor
3884     autoOffset : function(x, y) {
3885         this.setDelta(-12, -20);
3886     }    
3887 });/*
3888  * Based on:
3889  * Ext JS Library 1.1.1
3890  * Copyright(c) 2006-2007, Ext JS, LLC.
3891  *
3892  * Originally Released Under LGPL - original licence link has changed is not relivant.
3893  *
3894  * Fork - LGPL
3895  * <script type="text/javascript">
3896  */
3897
3898
3899 /**
3900  * @class Roo.dd.DropTarget
3901  * @extends Roo.dd.DDTarget
3902  * A simple class that provides the basic implementation needed to make any element a drop target that can have
3903  * draggable items dropped onto it.  The drop has no effect until an implementation of notifyDrop is provided.
3904  * @constructor
3905  * @param {String/HTMLElement/Element} el The container element
3906  * @param {Object} config
3907  */
3908 Roo.dd.DropTarget = function(el, config){
3909     this.el = Roo.get(el);
3910     
3911     var listeners = false; ;
3912     if (config && config.listeners) {
3913         listeners= config.listeners;
3914         delete config.listeners;
3915     }
3916     Roo.apply(this, config);
3917     
3918     if(this.containerScroll){
3919         Roo.dd.ScrollManager.register(this.el);
3920     }
3921     this.addEvents( {
3922          /**
3923          * @scope Roo.dd.DropTarget
3924          */
3925          
3926          /**
3927          * @event enter
3928          * The function a {@link Roo.dd.DragSource} calls once to notify this drop target that the source is now over the
3929          * target.  This default implementation adds the CSS class specified by overClass (if any) to the drop element
3930          * and returns the dropAllowed config value.  This method should be overridden if drop validation is required.
3931          * 
3932          * IMPORTANT : it should set this.overClass and this.dropAllowed
3933          * 
3934          * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop target
3935          * @param {Event} e The event
3936          * @param {Object} data An object containing arbitrary data supplied by the drag source
3937          */
3938         "enter" : true,
3939         
3940          /**
3941          * @event over
3942          * The function a {@link Roo.dd.DragSource} calls continuously while it is being dragged over the target.
3943          * This method will be called on every mouse movement while the drag source is over the drop target.
3944          * This default implementation simply returns the dropAllowed config value.
3945          * 
3946          * IMPORTANT : it should set this.dropAllowed
3947          * 
3948          * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop target
3949          * @param {Event} e The event
3950          * @param {Object} data An object containing arbitrary data supplied by the drag source
3951          
3952          */
3953         "over" : true,
3954         /**
3955          * @event out
3956          * The function a {@link Roo.dd.DragSource} calls once to notify this drop target that the source has been dragged
3957          * out of the target without dropping.  This default implementation simply removes the CSS class specified by
3958          * overClass (if any) from the drop element.
3959          * 
3960          * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop target
3961          * @param {Event} e The event
3962          * @param {Object} data An object containing arbitrary data supplied by the drag source
3963          */
3964          "out" : true,
3965          
3966         /**
3967          * @event drop
3968          * The function a {@link Roo.dd.DragSource} calls once to notify this drop target that the dragged item has
3969          * been dropped on it.  This method has no default implementation and returns false, so you must provide an
3970          * implementation that does something to process the drop event and returns true so that the drag source's
3971          * repair action does not run.
3972          * 
3973          * IMPORTANT : it should set this.success
3974          * 
3975          * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop target
3976          * @param {Event} e The event
3977          * @param {Object} data An object containing arbitrary data supplied by the drag source
3978         */
3979          "drop" : true
3980     });
3981             
3982      
3983     Roo.dd.DropTarget.superclass.constructor.call(  this, 
3984         this.el.dom, 
3985         this.ddGroup || this.group,
3986         {
3987             isTarget: true,
3988             listeners : listeners || {} 
3989            
3990         
3991         }
3992     );
3993
3994 };
3995
3996 Roo.extend(Roo.dd.DropTarget, Roo.dd.DDTarget, {
3997     /**
3998      * @cfg {String} overClass
3999      * The CSS class applied to the drop target element while the drag source is over it (defaults to "").
4000      */
4001      /**
4002      * @cfg {String} ddGroup
4003      * The drag drop group to handle drop events for
4004      */
4005      
4006     /**
4007      * @cfg {String} dropAllowed
4008      * The CSS class returned to the drag source when drop is allowed (defaults to "x-dd-drop-ok").
4009      */
4010     dropAllowed : "x-dd-drop-ok",
4011     /**
4012      * @cfg {String} dropNotAllowed
4013      * The CSS class returned to the drag source when drop is not allowed (defaults to "x-dd-drop-nodrop").
4014      */
4015     dropNotAllowed : "x-dd-drop-nodrop",
4016     /**
4017      * @cfg {boolean} success
4018      * set this after drop listener.. 
4019      */
4020     success : false,
4021     /**
4022      * @cfg {boolean|String} valid true/false or string (ok-add/ok-sub/ok/nodrop)
4023      * if the drop point is valid for over/enter..
4024      */
4025     valid : false,
4026     // private
4027     isTarget : true,
4028
4029     // private
4030     isNotifyTarget : true,
4031     
4032     /**
4033      * @hide
4034      */
4035     notifyEnter : function(dd, e, data)
4036     {
4037         this.valid = true;
4038         this.fireEvent('enter', dd, e, data);
4039         if(this.overClass){
4040             this.el.addClass(this.overClass);
4041         }
4042         return typeof(this.valid) == 'string' ? 'x-dd-drop-' + this.valid : (
4043             this.valid ? this.dropAllowed : this.dropNotAllowed
4044         );
4045     },
4046
4047     /**
4048      * @hide
4049      */
4050     notifyOver : function(dd, e, data)
4051     {
4052         this.valid = true;
4053         this.fireEvent('over', dd, e, data);
4054         return typeof(this.valid) == 'string' ? 'x-dd-drop-' + this.valid : (
4055             this.valid ? this.dropAllowed : this.dropNotAllowed
4056         );
4057     },
4058
4059     /**
4060      * @hide
4061      */
4062     notifyOut : function(dd, e, data)
4063     {
4064         this.fireEvent('out', dd, e, data);
4065         if(this.overClass){
4066             this.el.removeClass(this.overClass);
4067         }
4068     },
4069
4070     /**
4071      * @hide
4072      */
4073     notifyDrop : function(dd, e, data)
4074     {
4075         this.success = false;
4076         this.fireEvent('drop', dd, e, data);
4077         return this.success;
4078     }
4079 });/*
4080  * Based on:
4081  * Ext JS Library 1.1.1
4082  * Copyright(c) 2006-2007, Ext JS, LLC.
4083  *
4084  * Originally Released Under LGPL - original licence link has changed is not relivant.
4085  *
4086  * Fork - LGPL
4087  * <script type="text/javascript">
4088  */
4089
4090
4091 /**
4092  * @class Roo.dd.DragZone
4093  * @extends Roo.dd.DragSource
4094  * This class provides a container DD instance that proxies for multiple child node sources.<br />
4095  * By default, this class requires that draggable child nodes are registered with {@link Roo.dd.Registry}.
4096  * @constructor
4097  * @param {String/HTMLElement/Element} el The container element
4098  * @param {Object} config
4099  */
4100 Roo.dd.DragZone = function(el, config){
4101     Roo.dd.DragZone.superclass.constructor.call(this, el, config);
4102     if(this.containerScroll){
4103         Roo.dd.ScrollManager.register(this.el);
4104     }
4105 };
4106
4107 Roo.extend(Roo.dd.DragZone, Roo.dd.DragSource, {
4108     /**
4109      * @cfg {Boolean} containerScroll True to register this container with the Scrollmanager
4110      * for auto scrolling during drag operations.
4111      */
4112     /**
4113      * @cfg {String} hlColor The color to use when visually highlighting the drag source in the afterRepair
4114      * method after a failed drop (defaults to "c3daf9" - light blue)
4115      */
4116
4117     /**
4118      * Called when a mousedown occurs in this container. Looks in {@link Roo.dd.Registry}
4119      * for a valid target to drag based on the mouse down. Override this method
4120      * to provide your own lookup logic (e.g. finding a child by class name). Make sure your returned
4121      * object has a "ddel" attribute (with an HTML Element) for other functions to work.
4122      * @param {EventObject} e The mouse down event
4123      * @return {Object} The dragData
4124      */
4125     getDragData : function(e){
4126         return Roo.dd.Registry.getHandleFromEvent(e);
4127     },
4128     
4129     /**
4130      * Called once drag threshold has been reached to initialize the proxy element. By default, it clones the
4131      * this.dragData.ddel
4132      * @param {Number} x The x position of the click on the dragged object
4133      * @param {Number} y The y position of the click on the dragged object
4134      * @return {Boolean} true to continue the drag, false to cancel
4135      */
4136     onInitDrag : function(x, y){
4137         this.proxy.update(this.dragData.ddel.cloneNode(true));
4138         this.onStartDrag(x, y);
4139         return true;
4140     },
4141     
4142     /**
4143      * Called after a repair of an invalid drop. By default, highlights this.dragData.ddel 
4144      */
4145     afterRepair : function(){
4146         if(Roo.enableFx){
4147             Roo.Element.fly(this.dragData.ddel).highlight(this.hlColor || "c3daf9");
4148         }
4149         this.dragging = false;
4150     },
4151
4152     /**
4153      * Called before a repair of an invalid drop to get the XY to animate to. By default returns
4154      * the XY of this.dragData.ddel
4155      * @param {EventObject} e The mouse up event
4156      * @return {Array} The xy location (e.g. [100, 200])
4157      */
4158     getRepairXY : function(e){
4159         return Roo.Element.fly(this.dragData.ddel).getXY();  
4160     }
4161 });/*
4162  * Based on:
4163  * Ext JS Library 1.1.1
4164  * Copyright(c) 2006-2007, Ext JS, LLC.
4165  *
4166  * Originally Released Under LGPL - original licence link has changed is not relivant.
4167  *
4168  * Fork - LGPL
4169  * <script type="text/javascript">
4170  */
4171 /**
4172  * @class Roo.dd.DropZone
4173  * @extends Roo.dd.DropTarget
4174  * This class provides a container DD instance that proxies for multiple child node targets.<br />
4175  * By default, this class requires that child nodes accepting drop are registered with {@link Roo.dd.Registry}.
4176  * @constructor
4177  * @param {String/HTMLElement/Element} el The container element
4178  * @param {Object} config
4179  */
4180 Roo.dd.DropZone = function(el, config){
4181     Roo.dd.DropZone.superclass.constructor.call(this, el, config);
4182 };
4183
4184 Roo.extend(Roo.dd.DropZone, Roo.dd.DropTarget, {
4185     /**
4186      * Returns a custom data object associated with the DOM node that is the target of the event.  By default
4187      * this looks up the event target in the {@link Roo.dd.Registry}, although you can override this method to
4188      * provide your own custom lookup.
4189      * @param {Event} e The event
4190      * @return {Object} data The custom data
4191      */
4192     getTargetFromEvent : function(e){
4193         return Roo.dd.Registry.getTargetFromEvent(e);
4194     },
4195
4196     /**
4197      * Called internally when the DropZone determines that a {@link Roo.dd.DragSource} has entered a drop node
4198      * that it has registered.  This method has no default implementation and should be overridden to provide
4199      * node-specific processing if necessary.
4200      * @param {Object} nodeData The custom data associated with the drop node (this is the same value returned from 
4201      * {@link #getTargetFromEvent} for this node)
4202      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
4203      * @param {Event} e The event
4204      * @param {Object} data An object containing arbitrary data supplied by the drag source
4205      */
4206     onNodeEnter : function(n, dd, e, data){
4207         
4208     },
4209
4210     /**
4211      * Called internally while the DropZone determines that a {@link Roo.dd.DragSource} is over a drop node
4212      * that it has registered.  The default implementation returns this.dropNotAllowed, so it should be
4213      * overridden to provide the proper feedback.
4214      * @param {Object} nodeData The custom data associated with the drop node (this is the same value returned from
4215      * {@link #getTargetFromEvent} for this node)
4216      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
4217      * @param {Event} e The event
4218      * @param {Object} data An object containing arbitrary data supplied by the drag source
4219      * @return {String} status The CSS class that communicates the drop status back to the source so that the
4220      * underlying {@link Roo.dd.StatusProxy} can be updated
4221      */
4222     onNodeOver : function(n, dd, e, data){
4223         return this.dropAllowed;
4224     },
4225
4226     /**
4227      * Called internally when the DropZone determines that a {@link Roo.dd.DragSource} has been dragged out of
4228      * the drop node without dropping.  This method has no default implementation and should be overridden to provide
4229      * node-specific processing if necessary.
4230      * @param {Object} nodeData The custom data associated with the drop node (this is the same value returned from
4231      * {@link #getTargetFromEvent} for this node)
4232      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
4233      * @param {Event} e The event
4234      * @param {Object} data An object containing arbitrary data supplied by the drag source
4235      */
4236     onNodeOut : function(n, dd, e, data){
4237         
4238     },
4239
4240     /**
4241      * Called internally when the DropZone determines that a {@link Roo.dd.DragSource} has been dropped onto
4242      * the drop node.  The default implementation returns false, so it should be overridden to provide the
4243      * appropriate processing of the drop event and return true so that the drag source's repair action does not run.
4244      * @param {Object} nodeData The custom data associated with the drop node (this is the same value returned from
4245      * {@link #getTargetFromEvent} for this node)
4246      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
4247      * @param {Event} e The event
4248      * @param {Object} data An object containing arbitrary data supplied by the drag source
4249      * @return {Boolean} True if the drop was valid, else false
4250      */
4251     onNodeDrop : function(n, dd, e, data){
4252         return false;
4253     },
4254
4255     /**
4256      * Called internally while the DropZone determines that a {@link Roo.dd.DragSource} is being dragged over it,
4257      * but not over any of its registered drop nodes.  The default implementation returns this.dropNotAllowed, so
4258      * it should be overridden to provide the proper feedback if necessary.
4259      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
4260      * @param {Event} e The event
4261      * @param {Object} data An object containing arbitrary data supplied by the drag source
4262      * @return {String} status The CSS class that communicates the drop status back to the source so that the
4263      * underlying {@link Roo.dd.StatusProxy} can be updated
4264      */
4265     onContainerOver : function(dd, e, data){
4266         return this.dropNotAllowed;
4267     },
4268
4269     /**
4270      * Called internally when the DropZone determines that a {@link Roo.dd.DragSource} has been dropped on it,
4271      * but not on any of its registered drop nodes.  The default implementation returns false, so it should be
4272      * overridden to provide the appropriate processing of the drop event if you need the drop zone itself to
4273      * be able to accept drops.  It should return true when valid so that the drag source's repair action does not run.
4274      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
4275      * @param {Event} e The event
4276      * @param {Object} data An object containing arbitrary data supplied by the drag source
4277      * @return {Boolean} True if the drop was valid, else false
4278      */
4279     onContainerDrop : function(dd, e, data){
4280         return false;
4281     },
4282
4283     /**
4284      * The function a {@link Roo.dd.DragSource} calls once to notify this drop zone that the source is now over
4285      * the zone.  The default implementation returns this.dropNotAllowed and expects that only registered drop
4286      * nodes can process drag drop operations, so if you need the drop zone itself to be able to process drops
4287      * you should override this method and provide a custom implementation.
4288      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
4289      * @param {Event} e The event
4290      * @param {Object} data An object containing arbitrary data supplied by the drag source
4291      * @return {String} status The CSS class that communicates the drop status back to the source so that the
4292      * underlying {@link Roo.dd.StatusProxy} can be updated
4293      */
4294     notifyEnter : function(dd, e, data){
4295         return this.dropNotAllowed;
4296     },
4297
4298     /**
4299      * The function a {@link Roo.dd.DragSource} calls continuously while it is being dragged over the drop zone.
4300      * This method will be called on every mouse movement while the drag source is over the drop zone.
4301      * It will call {@link #onNodeOver} while the drag source is over a registered node, and will also automatically
4302      * delegate to the appropriate node-specific methods as necessary when the drag source enters and exits
4303      * registered nodes ({@link #onNodeEnter}, {@link #onNodeOut}). If the drag source is not currently over a
4304      * registered node, it will call {@link #onContainerOver}.
4305      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
4306      * @param {Event} e The event
4307      * @param {Object} data An object containing arbitrary data supplied by the drag source
4308      * @return {String} status The CSS class that communicates the drop status back to the source so that the
4309      * underlying {@link Roo.dd.StatusProxy} can be updated
4310      */
4311     notifyOver : function(dd, e, data){
4312         var n = this.getTargetFromEvent(e);
4313         if(!n){ // not over valid drop target
4314             if(this.lastOverNode){
4315                 this.onNodeOut(this.lastOverNode, dd, e, data);
4316                 this.lastOverNode = null;
4317             }
4318             return this.onContainerOver(dd, e, data);
4319         }
4320         if(this.lastOverNode != n){
4321             if(this.lastOverNode){
4322                 this.onNodeOut(this.lastOverNode, dd, e, data);
4323             }
4324             this.onNodeEnter(n, dd, e, data);
4325             this.lastOverNode = n;
4326         }
4327         return this.onNodeOver(n, dd, e, data);
4328     },
4329
4330     /**
4331      * The function a {@link Roo.dd.DragSource} calls once to notify this drop zone that the source has been dragged
4332      * out of the zone without dropping.  If the drag source is currently over a registered node, the notification
4333      * will be delegated to {@link #onNodeOut} for node-specific handling, otherwise it will be ignored.
4334      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop target
4335      * @param {Event} e The event
4336      * @param {Object} data An object containing arbitrary data supplied by the drag zone
4337      */
4338     notifyOut : function(dd, e, data){
4339         if(this.lastOverNode){
4340             this.onNodeOut(this.lastOverNode, dd, e, data);
4341             this.lastOverNode = null;
4342         }
4343     },
4344
4345     /**
4346      * The function a {@link Roo.dd.DragSource} calls once to notify this drop zone that the dragged item has
4347      * been dropped on it.  The drag zone will look up the target node based on the event passed in, and if there
4348      * is a node registered for that event, it will delegate to {@link #onNodeDrop} for node-specific handling,
4349      * otherwise it will call {@link #onContainerDrop}.
4350      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
4351      * @param {Event} e The event
4352      * @param {Object} data An object containing arbitrary data supplied by the drag source
4353      * @return {Boolean} True if the drop was valid, else false
4354      */
4355     notifyDrop : function(dd, e, data){
4356         if(this.lastOverNode){
4357             this.onNodeOut(this.lastOverNode, dd, e, data);
4358             this.lastOverNode = null;
4359         }
4360         var n = this.getTargetFromEvent(e);
4361         return n ?
4362             this.onNodeDrop(n, dd, e, data) :
4363             this.onContainerDrop(dd, e, data);
4364     },
4365
4366     // private
4367     triggerCacheRefresh : function(){
4368         Roo.dd.DDM.refreshCache(this.groups);
4369     }  
4370 });/*
4371  * Based on:
4372  * Ext JS Library 1.1.1
4373  * Copyright(c) 2006-2007, Ext JS, LLC.
4374  *
4375  * Originally Released Under LGPL - original licence link has changed is not relivant.
4376  *
4377  * Fork - LGPL
4378  * <script type="text/javascript">
4379  */
4380
4381
4382 /**
4383  * @class Roo.data.SortTypes
4384  * @singleton
4385  * Defines the default sorting (casting?) comparison functions used when sorting data.
4386  */
4387 Roo.data.SortTypes = {
4388     /**
4389      * Default sort that does nothing
4390      * @param {Mixed} s The value being converted
4391      * @return {Mixed} The comparison value
4392      */
4393     none : function(s){
4394         return s;
4395     },
4396     
4397     /**
4398      * The regular expression used to strip tags
4399      * @type {RegExp}
4400      * @property
4401      */
4402     stripTagsRE : /<\/?[^>]+>/gi,
4403     
4404     /**
4405      * Strips all HTML tags to sort on text only
4406      * @param {Mixed} s The value being converted
4407      * @return {String} The comparison value
4408      */
4409     asText : function(s){
4410         return String(s).replace(this.stripTagsRE, "");
4411     },
4412     
4413     /**
4414      * Strips all HTML tags to sort on text only - Case insensitive
4415      * @param {Mixed} s The value being converted
4416      * @return {String} The comparison value
4417      */
4418     asUCText : function(s){
4419         return String(s).toUpperCase().replace(this.stripTagsRE, "");
4420     },
4421     
4422     /**
4423      * Case insensitive string
4424      * @param {Mixed} s The value being converted
4425      * @return {String} The comparison value
4426      */
4427     asUCString : function(s) {
4428         return String(s).toUpperCase();
4429     },
4430     
4431     /**
4432      * Date sorting
4433      * @param {Mixed} s The value being converted
4434      * @return {Number} The comparison value
4435      */
4436     asDate : function(s) {
4437         if(!s){
4438             return 0;
4439         }
4440         if(s instanceof Date){
4441             return s.getTime();
4442         }
4443         return Date.parse(String(s));
4444     },
4445     
4446     /**
4447      * Float sorting
4448      * @param {Mixed} s The value being converted
4449      * @return {Float} The comparison value
4450      */
4451     asFloat : function(s) {
4452         var val = parseFloat(String(s).replace(/,/g, ""));
4453         if(isNaN(val)) val = 0;
4454         return val;
4455     },
4456     
4457     /**
4458      * Integer sorting
4459      * @param {Mixed} s The value being converted
4460      * @return {Number} The comparison value
4461      */
4462     asInt : function(s) {
4463         var val = parseInt(String(s).replace(/,/g, ""));
4464         if(isNaN(val)) val = 0;
4465         return val;
4466     }
4467 };/*
4468  * Based on:
4469  * Ext JS Library 1.1.1
4470  * Copyright(c) 2006-2007, Ext JS, LLC.
4471  *
4472  * Originally Released Under LGPL - original licence link has changed is not relivant.
4473  *
4474  * Fork - LGPL
4475  * <script type="text/javascript">
4476  */
4477
4478 /**
4479 * @class Roo.data.Record
4480  * Instances of this class encapsulate both record <em>definition</em> information, and record
4481  * <em>value</em> information for use in {@link Roo.data.Store} objects, or any code which needs
4482  * to access Records cached in an {@link Roo.data.Store} object.<br>
4483  * <p>
4484  * Constructors for this class are generated by passing an Array of field definition objects to {@link #create}.
4485  * Instances are usually only created by {@link Roo.data.Reader} implementations when processing unformatted data
4486  * objects.<br>
4487  * <p>
4488  * Record objects generated by this constructor inherit all the methods of Roo.data.Record listed below.
4489  * @constructor
4490  * This constructor should not be used to create Record objects. Instead, use the constructor generated by
4491  * {@link #create}. The parameters are the same.
4492  * @param {Array} data An associative Array of data values keyed by the field name.
4493  * @param {Object} id (Optional) The id of the record. This id should be unique, and is used by the
4494  * {@link Roo.data.Store} object which owns the Record to index its collection of Records. If
4495  * not specified an integer id is generated.
4496  */
4497 Roo.data.Record = function(data, id){
4498     this.id = (id || id === 0) ? id : ++Roo.data.Record.AUTO_ID;
4499     this.data = data;
4500 };
4501
4502 /**
4503  * Generate a constructor for a specific record layout.
4504  * @param {Array} o An Array of field definition objects which specify field names, and optionally,
4505  * data types, and a mapping for an {@link Roo.data.Reader} to extract the field's value from a data object.
4506  * Each field definition object may contain the following properties: <ul>
4507  * <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,
4508  * for example the <em>dataIndex</em> property in column definition objects passed to {@link Roo.grid.ColumnModel}</p></li>
4509  * <li><b>mapping</b> : String<p style="margin-left:1em">(Optional) A path specification for use by the {@link Roo.data.Reader} implementation
4510  * that is creating the Record to access the data value from the data object. If an {@link Roo.data.JsonReader}
4511  * is being used, then this is a string containing the javascript expression to reference the data relative to 
4512  * the record item's root. If an {@link Roo.data.XmlReader} is being used, this is an {@link Roo.DomQuery} path
4513  * to the data item relative to the record element. If the mapping expression is the same as the field name,
4514  * this may be omitted.</p></li>
4515  * <li><b>type</b> : String<p style="margin-left:1em">(Optional) The data type for conversion to displayable value. Possible values are
4516  * <ul><li>auto (Default, implies no conversion)</li>
4517  * <li>string</li>
4518  * <li>int</li>
4519  * <li>float</li>
4520  * <li>boolean</li>
4521  * <li>date</li></ul></p></li>
4522  * <li><b>sortType</b> : Mixed<p style="margin-left:1em">(Optional) A member of {@link Roo.data.SortTypes}.</p></li>
4523  * <li><b>sortDir</b> : String<p style="margin-left:1em">(Optional) Initial direction to sort. "ASC" or "DESC"</p></li>
4524  * <li><b>convert</b> : Function<p style="margin-left:1em">(Optional) A function which converts the value provided
4525  * by the Reader into an object that will be stored in the Record. It is passed the
4526  * following parameters:<ul>
4527  * <li><b>v</b> : Mixed<p style="margin-left:1em">The data value as read by the Reader.</p></li>
4528  * </ul></p></li>
4529  * <li><b>dateFormat</b> : String<p style="margin-left:1em">(Optional) A format String for the Date.parseDate function.</p></li>
4530  * </ul>
4531  * <br>usage:<br><pre><code>
4532 var TopicRecord = Roo.data.Record.create(
4533     {name: 'title', mapping: 'topic_title'},
4534     {name: 'author', mapping: 'username'},
4535     {name: 'totalPosts', mapping: 'topic_replies', type: 'int'},
4536     {name: 'lastPost', mapping: 'post_time', type: 'date'},
4537     {name: 'lastPoster', mapping: 'user2'},
4538     {name: 'excerpt', mapping: 'post_text'}
4539 );
4540
4541 var myNewRecord = new TopicRecord({
4542     title: 'Do my job please',
4543     author: 'noobie',
4544     totalPosts: 1,
4545     lastPost: new Date(),
4546     lastPoster: 'Animal',
4547     excerpt: 'No way dude!'
4548 });
4549 myStore.add(myNewRecord);
4550 </code></pre>
4551  * @method create
4552  * @static
4553  */
4554 Roo.data.Record.create = function(o){
4555     var f = function(){
4556         f.superclass.constructor.apply(this, arguments);
4557     };
4558     Roo.extend(f, Roo.data.Record);
4559     var p = f.prototype;
4560     p.fields = new Roo.util.MixedCollection(false, function(field){
4561         return field.name;
4562     });
4563     for(var i = 0, len = o.length; i < len; i++){
4564         p.fields.add(new Roo.data.Field(o[i]));
4565     }
4566     f.getField = function(name){
4567         return p.fields.get(name);  
4568     };
4569     return f;
4570 };
4571
4572 Roo.data.Record.AUTO_ID = 1000;
4573 Roo.data.Record.EDIT = 'edit';
4574 Roo.data.Record.REJECT = 'reject';
4575 Roo.data.Record.COMMIT = 'commit';
4576
4577 Roo.data.Record.prototype = {
4578     /**
4579      * Readonly flag - true if this record has been modified.
4580      * @type Boolean
4581      */
4582     dirty : false,
4583     editing : false,
4584     error: null,
4585     modified: null,
4586
4587     // private
4588     join : function(store){
4589         this.store = store;
4590     },
4591
4592     /**
4593      * Set the named field to the specified value.
4594      * @param {String} name The name of the field to set.
4595      * @param {Object} value The value to set the field to.
4596      */
4597     set : function(name, value){
4598         if(this.data[name] == value){
4599             return;
4600         }
4601         this.dirty = true;
4602         if(!this.modified){
4603             this.modified = {};
4604         }
4605         if(typeof this.modified[name] == 'undefined'){
4606             this.modified[name] = this.data[name];
4607         }
4608         this.data[name] = value;
4609         if(!this.editing && this.store){
4610             this.store.afterEdit(this);
4611         }       
4612     },
4613
4614     /**
4615      * Get the value of the named field.
4616      * @param {String} name The name of the field to get the value of.
4617      * @return {Object} The value of the field.
4618      */
4619     get : function(name){
4620         return this.data[name]; 
4621     },
4622
4623     // private
4624     beginEdit : function(){
4625         this.editing = true;
4626         this.modified = {}; 
4627     },
4628
4629     // private
4630     cancelEdit : function(){
4631         this.editing = false;
4632         delete this.modified;
4633     },
4634
4635     // private
4636     endEdit : function(){
4637         this.editing = false;
4638         if(this.dirty && this.store){
4639             this.store.afterEdit(this);
4640         }
4641     },
4642
4643     /**
4644      * Usually called by the {@link Roo.data.Store} which owns the Record.
4645      * Rejects all changes made to the Record since either creation, or the last commit operation.
4646      * Modified fields are reverted to their original values.
4647      * <p>
4648      * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
4649      * of reject operations.
4650      */
4651     reject : function(){
4652         var m = this.modified;
4653         for(var n in m){
4654             if(typeof m[n] != "function"){
4655                 this.data[n] = m[n];
4656             }
4657         }
4658         this.dirty = false;
4659         delete this.modified;
4660         this.editing = false;
4661         if(this.store){
4662             this.store.afterReject(this);
4663         }
4664     },
4665
4666     /**
4667      * Usually called by the {@link Roo.data.Store} which owns the Record.
4668      * Commits all changes made to the Record since either creation, or the last commit operation.
4669      * <p>
4670      * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
4671      * of commit operations.
4672      */
4673     commit : function(){
4674         this.dirty = false;
4675         delete this.modified;
4676         this.editing = false;
4677         if(this.store){
4678             this.store.afterCommit(this);
4679         }
4680     },
4681
4682     // private
4683     hasError : function(){
4684         return this.error != null;
4685     },
4686
4687     // private
4688     clearError : function(){
4689         this.error = null;
4690     },
4691
4692     /**
4693      * Creates a copy of this record.
4694      * @param {String} id (optional) A new record id if you don't want to use this record's id
4695      * @return {Record}
4696      */
4697     copy : function(newId) {
4698         return new this.constructor(Roo.apply({}, this.data), newId || this.id);
4699     }
4700 };/*
4701  * Based on:
4702  * Ext JS Library 1.1.1
4703  * Copyright(c) 2006-2007, Ext JS, LLC.
4704  *
4705  * Originally Released Under LGPL - original licence link has changed is not relivant.
4706  *
4707  * Fork - LGPL
4708  * <script type="text/javascript">
4709  */
4710
4711
4712
4713 /**
4714  * @class Roo.data.Store
4715  * @extends Roo.util.Observable
4716  * The Store class encapsulates a client side cache of {@link Roo.data.Record} objects which provide input data
4717  * for widgets such as the Roo.grid.Grid, or the Roo.form.ComboBox.<br>
4718  * <p>
4719  * 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
4720  * has no knowledge of the format of the data returned by the Proxy.<br>
4721  * <p>
4722  * A Store object uses its configured implementation of {@link Roo.data.DataReader} to create {@link Roo.data.Record}
4723  * instances from the data object. These records are cached and made available through accessor functions.
4724  * @constructor
4725  * Creates a new Store.
4726  * @param {Object} config A config object containing the objects needed for the Store to access data,
4727  * and read the data into Records.
4728  */
4729 Roo.data.Store = function(config){
4730     this.data = new Roo.util.MixedCollection(false);
4731     this.data.getKey = function(o){
4732         return o.id;
4733     };
4734     this.baseParams = {};
4735     // private
4736     this.paramNames = {
4737         "start" : "start",
4738         "limit" : "limit",
4739         "sort" : "sort",
4740         "dir" : "dir",
4741         "multisort" : "_multisort"
4742     };
4743
4744     if(config && config.data){
4745         this.inlineData = config.data;
4746         delete config.data;
4747     }
4748
4749     Roo.apply(this, config);
4750     
4751     if(this.reader){ // reader passed
4752         this.reader = Roo.factory(this.reader, Roo.data);
4753         this.reader.xmodule = this.xmodule || false;
4754         if(!this.recordType){
4755             this.recordType = this.reader.recordType;
4756         }
4757         if(this.reader.onMetaChange){
4758             this.reader.onMetaChange = this.onMetaChange.createDelegate(this);
4759         }
4760     }
4761
4762     if(this.recordType){
4763         this.fields = this.recordType.prototype.fields;
4764     }
4765     this.modified = [];
4766
4767     this.addEvents({
4768         /**
4769          * @event datachanged
4770          * Fires when the data cache has changed, and a widget which is using this Store
4771          * as a Record cache should refresh its view.
4772          * @param {Store} this
4773          */
4774         datachanged : true,
4775         /**
4776          * @event metachange
4777          * Fires when this store's reader provides new metadata (fields). This is currently only support for JsonReaders.
4778          * @param {Store} this
4779          * @param {Object} meta The JSON metadata
4780          */
4781         metachange : true,
4782         /**
4783          * @event add
4784          * Fires when Records have been added to the Store
4785          * @param {Store} this
4786          * @param {Roo.data.Record[]} records The array of Records added
4787          * @param {Number} index The index at which the record(s) were added
4788          */
4789         add : true,
4790         /**
4791          * @event remove
4792          * Fires when a Record has been removed from the Store
4793          * @param {Store} this
4794          * @param {Roo.data.Record} record The Record that was removed
4795          * @param {Number} index The index at which the record was removed
4796          */
4797         remove : true,
4798         /**
4799          * @event update
4800          * Fires when a Record has been updated
4801          * @param {Store} this
4802          * @param {Roo.data.Record} record The Record that was updated
4803          * @param {String} operation The update operation being performed.  Value may be one of:
4804          * <pre><code>
4805  Roo.data.Record.EDIT
4806  Roo.data.Record.REJECT
4807  Roo.data.Record.COMMIT
4808          * </code></pre>
4809          */
4810         update : true,
4811         /**
4812          * @event clear
4813          * Fires when the data cache has been cleared.
4814          * @param {Store} this
4815          */
4816         clear : true,
4817         /**
4818          * @event beforeload
4819          * Fires before a request is made for a new data object.  If the beforeload handler returns false
4820          * the load action will be canceled.
4821          * @param {Store} this
4822          * @param {Object} options The loading options that were specified (see {@link #load} for details)
4823          */
4824         beforeload : true,
4825         /**
4826          * @event beforeloadadd
4827          * Fires after a new set of Records has been loaded.
4828          * @param {Store} this
4829          * @param {Roo.data.Record[]} records The Records that were loaded
4830          * @param {Object} options The loading options that were specified (see {@link #load} for details)
4831          */
4832         beforeloadadd : true,
4833         /**
4834          * @event load
4835          * Fires after a new set of Records has been loaded, before they are added to the store.
4836          * @param {Store} this
4837          * @param {Roo.data.Record[]} records The Records that were loaded
4838          * @param {Object} options The loading options that were specified (see {@link #load} for details)
4839          * @params {Object} return from reader
4840          */
4841         load : true,
4842         /**
4843          * @event loadexception
4844          * Fires if an exception occurs in the Proxy during loading.
4845          * Called with the signature of the Proxy's "loadexception" event.
4846          * If you return Json { data: [] , success: false, .... } then this will be thrown with the following args
4847          * 
4848          * @param {Proxy} 
4849          * @param {Object} return from JsonData.reader() - success, totalRecords, records
4850          * @param {Object} load options 
4851          * @param {Object} jsonData from your request (normally this contains the Exception)
4852          */
4853         loadexception : true
4854     });
4855     
4856     if(this.proxy){
4857         this.proxy = Roo.factory(this.proxy, Roo.data);
4858         this.proxy.xmodule = this.xmodule || false;
4859         this.relayEvents(this.proxy,  ["loadexception"]);
4860     }
4861     this.sortToggle = {};
4862     this.sortOrder = []; // array of order of sorting - updated by grid if multisort is enabled.
4863
4864     Roo.data.Store.superclass.constructor.call(this);
4865
4866     if(this.inlineData){
4867         this.loadData(this.inlineData);
4868         delete this.inlineData;
4869     }
4870 };
4871
4872 Roo.extend(Roo.data.Store, Roo.util.Observable, {
4873      /**
4874     * @cfg {boolean} isLocal   flag if data is locally available (and can be always looked up
4875     * without a remote query - used by combo/forms at present.
4876     */
4877     
4878     /**
4879     * @cfg {Roo.data.DataProxy} proxy The Proxy object which provides access to a data object.
4880     */
4881     /**
4882     * @cfg {Array} data Inline data to be loaded when the store is initialized.
4883     */
4884     /**
4885     * @cfg {Roo.data.Reader} reader The Reader object which processes the data object and returns
4886     * an Array of Roo.data.record objects which are cached keyed by their <em>id</em> property.
4887     */
4888     /**
4889     * @cfg {Object} baseParams An object containing properties which are to be sent as parameters
4890     * on any HTTP request
4891     */
4892     /**
4893     * @cfg {Object} sortInfo A config object in the format: {field: "fieldName", direction: "ASC|DESC"}
4894     */
4895     /**
4896     * @cfg {Boolean} multiSort enable multi column sorting (sort is based on the order of columns, remote only at present)
4897     */
4898     multiSort: false,
4899     /**
4900     * @cfg {boolean} remoteSort True if sorting is to be handled by requesting the Proxy to provide a refreshed
4901     * version of the data object in sorted order, as opposed to sorting the Record cache in place (defaults to false).
4902     */
4903     remoteSort : false,
4904
4905     /**
4906     * @cfg {boolean} pruneModifiedRecords True to clear all modified record information each time the store is
4907      * loaded or when a record is removed. (defaults to false).
4908     */
4909     pruneModifiedRecords : false,
4910
4911     // private
4912     lastOptions : null,
4913
4914     /**
4915      * Add Records to the Store and fires the add event.
4916      * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
4917      */
4918     add : function(records){
4919         records = [].concat(records);
4920         for(var i = 0, len = records.length; i < len; i++){
4921             records[i].join(this);
4922         }
4923         var index = this.data.length;
4924         this.data.addAll(records);
4925         this.fireEvent("add", this, records, index);
4926     },
4927
4928     /**
4929      * Remove a Record from the Store and fires the remove event.
4930      * @param {Ext.data.Record} record The Roo.data.Record object to remove from the cache.
4931      */
4932     remove : function(record){
4933         var index = this.data.indexOf(record);
4934         this.data.removeAt(index);
4935         if(this.pruneModifiedRecords){
4936             this.modified.remove(record);
4937         }
4938         this.fireEvent("remove", this, record, index);
4939     },
4940
4941     /**
4942      * Remove all Records from the Store and fires the clear event.
4943      */
4944     removeAll : function(){
4945         this.data.clear();
4946         if(this.pruneModifiedRecords){
4947             this.modified = [];
4948         }
4949         this.fireEvent("clear", this);
4950     },
4951
4952     /**
4953      * Inserts Records to the Store at the given index and fires the add event.
4954      * @param {Number} index The start index at which to insert the passed Records.
4955      * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
4956      */
4957     insert : function(index, records){
4958         records = [].concat(records);
4959         for(var i = 0, len = records.length; i < len; i++){
4960             this.data.insert(index, records[i]);
4961             records[i].join(this);
4962         }
4963         this.fireEvent("add", this, records, index);
4964     },
4965
4966     /**
4967      * Get the index within the cache of the passed Record.
4968      * @param {Roo.data.Record} record The Roo.data.Record object to to find.
4969      * @return {Number} The index of the passed Record. Returns -1 if not found.
4970      */
4971     indexOf : function(record){
4972         return this.data.indexOf(record);
4973     },
4974
4975     /**
4976      * Get the index within the cache of the Record with the passed id.
4977      * @param {String} id The id of the Record to find.
4978      * @return {Number} The index of the Record. Returns -1 if not found.
4979      */
4980     indexOfId : function(id){
4981         return this.data.indexOfKey(id);
4982     },
4983
4984     /**
4985      * Get the Record with the specified id.
4986      * @param {String} id The id of the Record to find.
4987      * @return {Roo.data.Record} The Record with the passed id. Returns undefined if not found.
4988      */
4989     getById : function(id){
4990         return this.data.key(id);
4991     },
4992
4993     /**
4994      * Get the Record at the specified index.
4995      * @param {Number} index The index of the Record to find.
4996      * @return {Roo.data.Record} The Record at the passed index. Returns undefined if not found.
4997      */
4998     getAt : function(index){
4999         return this.data.itemAt(index);
5000     },
5001
5002     /**
5003      * Returns a range of Records between specified indices.
5004      * @param {Number} startIndex (optional) The starting index (defaults to 0)
5005      * @param {Number} endIndex (optional) The ending index (defaults to the last Record in the Store)
5006      * @return {Roo.data.Record[]} An array of Records
5007      */
5008     getRange : function(start, end){
5009         return this.data.getRange(start, end);
5010     },
5011
5012     // private
5013     storeOptions : function(o){
5014         o = Roo.apply({}, o);
5015         delete o.callback;
5016         delete o.scope;
5017         this.lastOptions = o;
5018     },
5019
5020     /**
5021      * Loads the Record cache from the configured Proxy using the configured Reader.
5022      * <p>
5023      * If using remote paging, then the first load call must specify the <em>start</em>
5024      * and <em>limit</em> properties in the options.params property to establish the initial
5025      * position within the dataset, and the number of Records to cache on each read from the Proxy.
5026      * <p>
5027      * <strong>It is important to note that for remote data sources, loading is asynchronous,
5028      * and this call will return before the new data has been loaded. Perform any post-processing
5029      * in a callback function, or in a "load" event handler.</strong>
5030      * <p>
5031      * @param {Object} options An object containing properties which control loading options:<ul>
5032      * <li>params {Object} An object containing properties to pass as HTTP parameters to a remote data source.</li>
5033      * <li>callback {Function} A function to be called after the Records have been loaded. The callback is
5034      * passed the following arguments:<ul>
5035      * <li>r : Roo.data.Record[]</li>
5036      * <li>options: Options object from the load call</li>
5037      * <li>success: Boolean success indicator</li></ul></li>
5038      * <li>scope {Object} Scope with which to call the callback (defaults to the Store object)</li>
5039      * <li>add {Boolean} indicator to append loaded records rather than replace the current cache.</li>
5040      * </ul>
5041      */
5042     load : function(options){
5043         options = options || {};
5044         if(this.fireEvent("beforeload", this, options) !== false){
5045             this.storeOptions(options);
5046             var p = Roo.apply(options.params || {}, this.baseParams);
5047             // if meta was not loaded from remote source.. try requesting it.
5048             if (!this.reader.metaFromRemote) {
5049                 p._requestMeta = 1;
5050             }
5051             if(this.sortInfo && this.remoteSort){
5052                 var pn = this.paramNames;
5053                 p[pn["sort"]] = this.sortInfo.field;
5054                 p[pn["dir"]] = this.sortInfo.direction;
5055             }
5056             if (this.multiSort) {
5057                 var pn = this.paramNames;
5058                 p[pn["multisort"]] = Roo.encode( { sort : this.sortToggle, order: this.sortOrder });
5059             }
5060             
5061             this.proxy.load(p, this.reader, this.loadRecords, this, options);
5062         }
5063     },
5064
5065     /**
5066      * Reloads the Record cache from the configured Proxy using the configured Reader and
5067      * the options from the last load operation performed.
5068      * @param {Object} options (optional) An object containing properties which may override the options
5069      * used in the last load operation. See {@link #load} for details (defaults to null, in which case
5070      * the most recently used options are reused).
5071      */
5072     reload : function(options){
5073         this.load(Roo.applyIf(options||{}, this.lastOptions));
5074     },
5075
5076     // private
5077     // Called as a callback by the Reader during a load operation.
5078     loadRecords : function(o, options, success){
5079         if(!o || success === false){
5080             if(success !== false){
5081                 this.fireEvent("load", this, [], options, o);
5082             }
5083             if(options.callback){
5084                 options.callback.call(options.scope || this, [], options, false);
5085             }
5086             return;
5087         }
5088         // if data returned failure - throw an exception.
5089         if (o.success === false) {
5090             // show a message if no listener is registered.
5091             if (!this.hasListener('loadexception') && typeof(o.raw.errorMsg) != 'undefined') {
5092                     Roo.MessageBox.alert("Error loading",o.raw.errorMsg);
5093             }
5094             // loadmask wil be hooked into this..
5095             this.fireEvent("loadexception", this, o, options, o.raw.errorMsg);
5096             return;
5097         }
5098         var r = o.records, t = o.totalRecords || r.length;
5099         
5100         this.fireEvent("beforeloadadd", this, r, options, o);
5101         
5102         if(!options || options.add !== true){
5103             if(this.pruneModifiedRecords){
5104                 this.modified = [];
5105             }
5106             for(var i = 0, len = r.length; i < len; i++){
5107                 r[i].join(this);
5108             }
5109             if(this.snapshot){
5110                 this.data = this.snapshot;
5111                 delete this.snapshot;
5112             }
5113             this.data.clear();
5114             this.data.addAll(r);
5115             this.totalLength = t;
5116             this.applySort();
5117             this.fireEvent("datachanged", this);
5118         }else{
5119             this.totalLength = Math.max(t, this.data.length+r.length);
5120             this.add(r);
5121         }
5122         this.fireEvent("load", this, r, options, o);
5123         if(options.callback){
5124             options.callback.call(options.scope || this, r, options, true);
5125         }
5126     },
5127
5128
5129     /**
5130      * Loads data from a passed data block. A Reader which understands the format of the data
5131      * must have been configured in the constructor.
5132      * @param {Object} data The data block from which to read the Records.  The format of the data expected
5133      * is dependent on the type of Reader that is configured and should correspond to that Reader's readRecords parameter.
5134      * @param {Boolean} append (Optional) True to append the new Records rather than replace the existing cache.
5135      */
5136     loadData : function(o, append){
5137         var r = this.reader.readRecords(o);
5138         this.loadRecords(r, {add: append}, true);
5139     },
5140
5141     /**
5142      * Gets the number of cached records.
5143      * <p>
5144      * <em>If using paging, this may not be the total size of the dataset. If the data object
5145      * used by the Reader contains the dataset size, then the getTotalCount() function returns
5146      * the data set size</em>
5147      */
5148     getCount : function(){
5149         return this.data.length || 0;
5150     },
5151
5152     /**
5153      * Gets the total number of records in the dataset as returned by the server.
5154      * <p>
5155      * <em>If using paging, for this to be accurate, the data object used by the Reader must contain
5156      * the dataset size</em>
5157      */
5158     getTotalCount : function(){
5159         return this.totalLength || 0;
5160     },
5161
5162     /**
5163      * Returns the sort state of the Store as an object with two properties:
5164      * <pre><code>
5165  field {String} The name of the field by which the Records are sorted
5166  direction {String} The sort order, "ASC" or "DESC"
5167      * </code></pre>
5168      */
5169     getSortState : function(){
5170         return this.sortInfo;
5171     },
5172
5173     // private
5174     applySort : function(){
5175         if(this.sortInfo && !this.remoteSort){
5176             var s = this.sortInfo, f = s.field;
5177             var st = this.fields.get(f).sortType;
5178             var fn = function(r1, r2){
5179                 var v1 = st(r1.data[f]), v2 = st(r2.data[f]);
5180                 return v1 > v2 ? 1 : (v1 < v2 ? -1 : 0);
5181             };
5182             this.data.sort(s.direction, fn);
5183             if(this.snapshot && this.snapshot != this.data){
5184                 this.snapshot.sort(s.direction, fn);
5185             }
5186         }
5187     },
5188
5189     /**
5190      * Sets the default sort column and order to be used by the next load operation.
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     setDefaultSort : function(field, dir){
5195         this.sortInfo = {field: field, direction: dir ? dir.toUpperCase() : "ASC"};
5196     },
5197
5198     /**
5199      * Sort the Records.
5200      * If remote sorting is used, the sort is performed on the server, and the cache is
5201      * reloaded. If local sorting is used, the cache is sorted internally.
5202      * @param {String} fieldName The name of the field to sort by.
5203      * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
5204      */
5205     sort : function(fieldName, dir){
5206         var f = this.fields.get(fieldName);
5207         if(!dir){
5208             this.sortToggle[f.name] = this.sortToggle[f.name] || f.sortDir;
5209             
5210             if(this.multiSort || (this.sortInfo && this.sortInfo.field == f.name) ){ // toggle sort dir
5211                 dir = (this.sortToggle[f.name] || "ASC").toggle("ASC", "DESC");
5212             }else{
5213                 dir = f.sortDir;
5214             }
5215         }
5216         this.sortToggle[f.name] = dir;
5217         this.sortInfo = {field: f.name, direction: dir};
5218         if(!this.remoteSort){
5219             this.applySort();
5220             this.fireEvent("datachanged", this);
5221         }else{
5222             this.load(this.lastOptions);
5223         }
5224     },
5225
5226     /**
5227      * Calls the specified function for each of the Records in the cache.
5228      * @param {Function} fn The function to call. The Record is passed as the first parameter.
5229      * Returning <em>false</em> aborts and exits the iteration.
5230      * @param {Object} scope (optional) The scope in which to call the function (defaults to the Record).
5231      */
5232     each : function(fn, scope){
5233         this.data.each(fn, scope);
5234     },
5235
5236     /**
5237      * Gets all records modified since the last commit.  Modified records are persisted across load operations
5238      * (e.g., during paging).
5239      * @return {Roo.data.Record[]} An array of Records containing outstanding modifications.
5240      */
5241     getModifiedRecords : function(){
5242         return this.modified;
5243     },
5244
5245     // private
5246     createFilterFn : function(property, value, anyMatch){
5247         if(!value.exec){ // not a regex
5248             value = String(value);
5249             if(value.length == 0){
5250                 return false;
5251             }
5252             value = new RegExp((anyMatch === true ? '' : '^') + Roo.escapeRe(value), "i");
5253         }
5254         return function(r){
5255             return value.test(r.data[property]);
5256         };
5257     },
5258
5259     /**
5260      * Sums the value of <i>property</i> for each record between start and end and returns the result.
5261      * @param {String} property A field on your records
5262      * @param {Number} start The record index to start at (defaults to 0)
5263      * @param {Number} end The last record index to include (defaults to length - 1)
5264      * @return {Number} The sum
5265      */
5266     sum : function(property, start, end){
5267         var rs = this.data.items, v = 0;
5268         start = start || 0;
5269         end = (end || end === 0) ? end : rs.length-1;
5270
5271         for(var i = start; i <= end; i++){
5272             v += (rs[i].data[property] || 0);
5273         }
5274         return v;
5275     },
5276
5277     /**
5278      * Filter the records by a specified property.
5279      * @param {String} field A field on your records
5280      * @param {String/RegExp} value Either a string that the field
5281      * should start with or a RegExp to test against the field
5282      * @param {Boolean} anyMatch True to match any part not just the beginning
5283      */
5284     filter : function(property, value, anyMatch){
5285         var fn = this.createFilterFn(property, value, anyMatch);
5286         return fn ? this.filterBy(fn) : this.clearFilter();
5287     },
5288
5289     /**
5290      * Filter by a function. The specified function will be called with each
5291      * record in this data source. If the function returns true the record is included,
5292      * otherwise it is filtered.
5293      * @param {Function} fn The function to be called, it will receive 2 args (record, id)
5294      * @param {Object} scope (optional) The scope of the function (defaults to this)
5295      */
5296     filterBy : function(fn, scope){
5297         this.snapshot = this.snapshot || this.data;
5298         this.data = this.queryBy(fn, scope||this);
5299         this.fireEvent("datachanged", this);
5300     },
5301
5302     /**
5303      * Query the records by a specified property.
5304      * @param {String} field A field on your records
5305      * @param {String/RegExp} value Either a string that the field
5306      * should start with or a RegExp to test against the field
5307      * @param {Boolean} anyMatch True to match any part not just the beginning
5308      * @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
5309      */
5310     query : function(property, value, anyMatch){
5311         var fn = this.createFilterFn(property, value, anyMatch);
5312         return fn ? this.queryBy(fn) : this.data.clone();
5313     },
5314
5315     /**
5316      * Query by a function. The specified function will be called with each
5317      * record in this data source. If the function returns true the record is included
5318      * in the results.
5319      * @param {Function} fn The function to be called, it will receive 2 args (record, id)
5320      * @param {Object} scope (optional) The scope of the function (defaults to this)
5321       @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
5322      **/
5323     queryBy : function(fn, scope){
5324         var data = this.snapshot || this.data;
5325         return data.filterBy(fn, scope||this);
5326     },
5327
5328     /**
5329      * Collects unique values for a particular dataIndex from this store.
5330      * @param {String} dataIndex The property to collect
5331      * @param {Boolean} allowNull (optional) Pass true to allow null, undefined or empty string values
5332      * @param {Boolean} bypassFilter (optional) Pass true to collect from all records, even ones which are filtered
5333      * @return {Array} An array of the unique values
5334      **/
5335     collect : function(dataIndex, allowNull, bypassFilter){
5336         var d = (bypassFilter === true && this.snapshot) ?
5337                 this.snapshot.items : this.data.items;
5338         var v, sv, r = [], l = {};
5339         for(var i = 0, len = d.length; i < len; i++){
5340             v = d[i].data[dataIndex];
5341             sv = String(v);
5342             if((allowNull || !Roo.isEmpty(v)) && !l[sv]){
5343                 l[sv] = true;
5344                 r[r.length] = v;
5345             }
5346         }
5347         return r;
5348     },
5349
5350     /**
5351      * Revert to a view of the Record cache with no filtering applied.
5352      * @param {Boolean} suppressEvent If true the filter is cleared silently without notifying listeners
5353      */
5354     clearFilter : function(suppressEvent){
5355         if(this.snapshot && this.snapshot != this.data){
5356             this.data = this.snapshot;
5357             delete this.snapshot;
5358             if(suppressEvent !== true){
5359                 this.fireEvent("datachanged", this);
5360             }
5361         }
5362     },
5363
5364     // private
5365     afterEdit : function(record){
5366         if(this.modified.indexOf(record) == -1){
5367             this.modified.push(record);
5368         }
5369         this.fireEvent("update", this, record, Roo.data.Record.EDIT);
5370     },
5371     
5372     // private
5373     afterReject : function(record){
5374         this.modified.remove(record);
5375         this.fireEvent("update", this, record, Roo.data.Record.REJECT);
5376     },
5377
5378     // private
5379     afterCommit : function(record){
5380         this.modified.remove(record);
5381         this.fireEvent("update", this, record, Roo.data.Record.COMMIT);
5382     },
5383
5384     /**
5385      * Commit all Records with outstanding changes. To handle updates for changes, subscribe to the
5386      * Store's "update" event, and perform updating when the third parameter is Roo.data.Record.COMMIT.
5387      */
5388     commitChanges : 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].commit();
5393         }
5394     },
5395
5396     /**
5397      * Cancel outstanding changes on all changed records.
5398      */
5399     rejectChanges : function(){
5400         var m = this.modified.slice(0);
5401         this.modified = [];
5402         for(var i = 0, len = m.length; i < len; i++){
5403             m[i].reject();
5404         }
5405     },
5406
5407     onMetaChange : function(meta, rtype, o){
5408         this.recordType = rtype;
5409         this.fields = rtype.prototype.fields;
5410         delete this.snapshot;
5411         this.sortInfo = meta.sortInfo || this.sortInfo;
5412         this.modified = [];
5413         this.fireEvent('metachange', this, this.reader.meta);
5414     }
5415 });/*
5416  * Based on:
5417  * Ext JS Library 1.1.1
5418  * Copyright(c) 2006-2007, Ext JS, LLC.
5419  *
5420  * Originally Released Under LGPL - original licence link has changed is not relivant.
5421  *
5422  * Fork - LGPL
5423  * <script type="text/javascript">
5424  */
5425
5426 /**
5427  * @class Roo.data.SimpleStore
5428  * @extends Roo.data.Store
5429  * Small helper class to make creating Stores from Array data easier.
5430  * @cfg {Number} id The array index of the record id. Leave blank to auto generate ids.
5431  * @cfg {Array} fields An array of field definition objects, or field name strings.
5432  * @cfg {Array} data The multi-dimensional array of data
5433  * @constructor
5434  * @param {Object} config
5435  */
5436 Roo.data.SimpleStore = function(config){
5437     Roo.data.SimpleStore.superclass.constructor.call(this, {
5438         isLocal : true,
5439         reader: new Roo.data.ArrayReader({
5440                 id: config.id
5441             },
5442             Roo.data.Record.create(config.fields)
5443         ),
5444         proxy : new Roo.data.MemoryProxy(config.data)
5445     });
5446     this.load();
5447 };
5448 Roo.extend(Roo.data.SimpleStore, Roo.data.Store);/*
5449  * Based on:
5450  * Ext JS Library 1.1.1
5451  * Copyright(c) 2006-2007, Ext JS, LLC.
5452  *
5453  * Originally Released Under LGPL - original licence link has changed is not relivant.
5454  *
5455  * Fork - LGPL
5456  * <script type="text/javascript">
5457  */
5458
5459 /**
5460 /**
5461  * @extends Roo.data.Store
5462  * @class Roo.data.JsonStore
5463  * Small helper class to make creating Stores for JSON data easier. <br/>
5464 <pre><code>
5465 var store = new Roo.data.JsonStore({
5466     url: 'get-images.php',
5467     root: 'images',
5468     fields: ['name', 'url', {name:'size', type: 'float'}, {name:'lastmod', type:'date'}]
5469 });
5470 </code></pre>
5471  * <b>Note: Although they are not listed, this class inherits all of the config options of Store,
5472  * JsonReader and HttpProxy (unless inline data is provided).</b>
5473  * @cfg {Array} fields An array of field definition objects, or field name strings.
5474  * @constructor
5475  * @param {Object} config
5476  */
5477 Roo.data.JsonStore = function(c){
5478     Roo.data.JsonStore.superclass.constructor.call(this, Roo.apply(c, {
5479         proxy: !c.data ? new Roo.data.HttpProxy({url: c.url}) : undefined,
5480         reader: new Roo.data.JsonReader(c, c.fields)
5481     }));
5482 };
5483 Roo.extend(Roo.data.JsonStore, Roo.data.Store);/*
5484  * Based on:
5485  * Ext JS Library 1.1.1
5486  * Copyright(c) 2006-2007, Ext JS, LLC.
5487  *
5488  * Originally Released Under LGPL - original licence link has changed is not relivant.
5489  *
5490  * Fork - LGPL
5491  * <script type="text/javascript">
5492  */
5493
5494  
5495 Roo.data.Field = function(config){
5496     if(typeof config == "string"){
5497         config = {name: config};
5498     }
5499     Roo.apply(this, config);
5500     
5501     if(!this.type){
5502         this.type = "auto";
5503     }
5504     
5505     var st = Roo.data.SortTypes;
5506     // named sortTypes are supported, here we look them up
5507     if(typeof this.sortType == "string"){
5508         this.sortType = st[this.sortType];
5509     }
5510     
5511     // set default sortType for strings and dates
5512     if(!this.sortType){
5513         switch(this.type){
5514             case "string":
5515                 this.sortType = st.asUCString;
5516                 break;
5517             case "date":
5518                 this.sortType = st.asDate;
5519                 break;
5520             default:
5521                 this.sortType = st.none;
5522         }
5523     }
5524
5525     // define once
5526     var stripRe = /[\$,%]/g;
5527
5528     // prebuilt conversion function for this field, instead of
5529     // switching every time we're reading a value
5530     if(!this.convert){
5531         var cv, dateFormat = this.dateFormat;
5532         switch(this.type){
5533             case "":
5534             case "auto":
5535             case undefined:
5536                 cv = function(v){ return v; };
5537                 break;
5538             case "string":
5539                 cv = function(v){ return (v === undefined || v === null) ? '' : String(v); };
5540                 break;
5541             case "int":
5542                 cv = function(v){
5543                     return v !== undefined && v !== null && v !== '' ?
5544                            parseInt(String(v).replace(stripRe, ""), 10) : '';
5545                     };
5546                 break;
5547             case "float":
5548                 cv = function(v){
5549                     return v !== undefined && v !== null && v !== '' ?
5550                            parseFloat(String(v).replace(stripRe, ""), 10) : ''; 
5551                     };
5552                 break;
5553             case "bool":
5554             case "boolean":
5555                 cv = function(v){ return v === true || v === "true" || v == 1; };
5556                 break;
5557             case "date":
5558                 cv = function(v){
5559                     if(!v){
5560                         return '';
5561                     }
5562                     if(v instanceof Date){
5563                         return v;
5564                     }
5565                     if(dateFormat){
5566                         if(dateFormat == "timestamp"){
5567                             return new Date(v*1000);
5568                         }
5569                         return Date.parseDate(v, dateFormat);
5570                     }
5571                     var parsed = Date.parse(v);
5572                     return parsed ? new Date(parsed) : null;
5573                 };
5574              break;
5575             
5576         }
5577         this.convert = cv;
5578     }
5579 };
5580
5581 Roo.data.Field.prototype = {
5582     dateFormat: null,
5583     defaultValue: "",
5584     mapping: null,
5585     sortType : null,
5586     sortDir : "ASC"
5587 };/*
5588  * Based on:
5589  * Ext JS Library 1.1.1
5590  * Copyright(c) 2006-2007, Ext JS, LLC.
5591  *
5592  * Originally Released Under LGPL - original licence link has changed is not relivant.
5593  *
5594  * Fork - LGPL
5595  * <script type="text/javascript">
5596  */
5597  
5598 // Base class for reading structured data from a data source.  This class is intended to be
5599 // extended (see ArrayReader, JsonReader and XmlReader) and should not be created directly.
5600
5601 /**
5602  * @class Roo.data.DataReader
5603  * Base class for reading structured data from a data source.  This class is intended to be
5604  * extended (see {Roo.data.ArrayReader}, {Roo.data.JsonReader} and {Roo.data.XmlReader}) and should not be created directly.
5605  */
5606
5607 Roo.data.DataReader = function(meta, recordType){
5608     
5609     this.meta = meta;
5610     
5611     this.recordType = recordType instanceof Array ? 
5612         Roo.data.Record.create(recordType) : recordType;
5613 };
5614
5615 Roo.data.DataReader.prototype = {
5616      /**
5617      * Create an empty record
5618      * @param {Object} data (optional) - overlay some values
5619      * @return {Roo.data.Record} record created.
5620      */
5621     newRow :  function(d) {
5622         var da =  {};
5623         this.recordType.prototype.fields.each(function(c) {
5624             switch( c.type) {
5625                 case 'int' : da[c.name] = 0; break;
5626                 case 'date' : da[c.name] = new Date(); break;
5627                 case 'float' : da[c.name] = 0.0; break;
5628                 case 'boolean' : da[c.name] = false; break;
5629                 default : da[c.name] = ""; break;
5630             }
5631             
5632         });
5633         return new this.recordType(Roo.apply(da, d));
5634     }
5635     
5636 };/*
5637  * Based on:
5638  * Ext JS Library 1.1.1
5639  * Copyright(c) 2006-2007, Ext JS, LLC.
5640  *
5641  * Originally Released Under LGPL - original licence link has changed is not relivant.
5642  *
5643  * Fork - LGPL
5644  * <script type="text/javascript">
5645  */
5646
5647 /**
5648  * @class Roo.data.DataProxy
5649  * @extends Roo.data.Observable
5650  * This class is an abstract base class for implementations which provide retrieval of
5651  * unformatted data objects.<br>
5652  * <p>
5653  * DataProxy implementations are usually used in conjunction with an implementation of Roo.data.DataReader
5654  * (of the appropriate type which knows how to parse the data object) to provide a block of
5655  * {@link Roo.data.Records} to an {@link Roo.data.Store}.<br>
5656  * <p>
5657  * Custom implementations must implement the load method as described in
5658  * {@link Roo.data.HttpProxy#load}.
5659  */
5660 Roo.data.DataProxy = function(){
5661     this.addEvents({
5662         /**
5663          * @event beforeload
5664          * Fires before a network request is made to retrieve a data object.
5665          * @param {Object} This DataProxy object.
5666          * @param {Object} params The params parameter to the load function.
5667          */
5668         beforeload : true,
5669         /**
5670          * @event load
5671          * Fires before the load method's callback is called.
5672          * @param {Object} This DataProxy object.
5673          * @param {Object} o The data object.
5674          * @param {Object} arg The callback argument object passed to the load function.
5675          */
5676         load : true,
5677         /**
5678          * @event loadexception
5679          * Fires if an Exception occurs during data retrieval.
5680          * @param {Object} This DataProxy object.
5681          * @param {Object} o The data object.
5682          * @param {Object} arg The callback argument object passed to the load function.
5683          * @param {Object} e The Exception.
5684          */
5685         loadexception : true
5686     });
5687     Roo.data.DataProxy.superclass.constructor.call(this);
5688 };
5689
5690 Roo.extend(Roo.data.DataProxy, Roo.util.Observable);
5691
5692     /**
5693      * @cfg {void} listeners (Not available) Constructor blocks listeners from being set
5694      */
5695 /*
5696  * Based on:
5697  * Ext JS Library 1.1.1
5698  * Copyright(c) 2006-2007, Ext JS, LLC.
5699  *
5700  * Originally Released Under LGPL - original licence link has changed is not relivant.
5701  *
5702  * Fork - LGPL
5703  * <script type="text/javascript">
5704  */
5705 /**
5706  * @class Roo.data.MemoryProxy
5707  * An implementation of Roo.data.DataProxy that simply passes the data specified in its constructor
5708  * to the Reader when its load method is called.
5709  * @constructor
5710  * @param {Object} data The data object which the Reader uses to construct a block of Roo.data.Records.
5711  */
5712 Roo.data.MemoryProxy = function(data){
5713     if (data.data) {
5714         data = data.data;
5715     }
5716     Roo.data.MemoryProxy.superclass.constructor.call(this);
5717     this.data = data;
5718 };
5719
5720 Roo.extend(Roo.data.MemoryProxy, Roo.data.DataProxy, {
5721     /**
5722      * Load data from the requested source (in this case an in-memory
5723      * data object passed to the constructor), read the data object into
5724      * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
5725      * process that block using the passed callback.
5726      * @param {Object} params This parameter is not used by the MemoryProxy class.
5727      * @param {Roo.data.DataReader} reader The Reader object which converts the data
5728      * object into a block of Roo.data.Records.
5729      * @param {Function} callback The function into which to pass the block of Roo.data.records.
5730      * The function must be passed <ul>
5731      * <li>The Record block object</li>
5732      * <li>The "arg" argument from the load function</li>
5733      * <li>A boolean success indicator</li>
5734      * </ul>
5735      * @param {Object} scope The scope in which to call the callback
5736      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
5737      */
5738     load : function(params, reader, callback, scope, arg){
5739         params = params || {};
5740         var result;
5741         try {
5742             result = reader.readRecords(this.data);
5743         }catch(e){
5744             this.fireEvent("loadexception", this, arg, null, e);
5745             callback.call(scope, null, arg, false);
5746             return;
5747         }
5748         callback.call(scope, result, arg, true);
5749     },
5750     
5751     // private
5752     update : function(params, records){
5753         
5754     }
5755 });/*
5756  * Based on:
5757  * Ext JS Library 1.1.1
5758  * Copyright(c) 2006-2007, Ext JS, LLC.
5759  *
5760  * Originally Released Under LGPL - original licence link has changed is not relivant.
5761  *
5762  * Fork - LGPL
5763  * <script type="text/javascript">
5764  */
5765 /**
5766  * @class Roo.data.HttpProxy
5767  * @extends Roo.data.DataProxy
5768  * An implementation of {@link Roo.data.DataProxy} that reads a data object from an {@link Roo.data.Connection} object
5769  * configured to reference a certain URL.<br><br>
5770  * <p>
5771  * <em>Note that this class cannot be used to retrieve data from a domain other than the domain
5772  * from which the running page was served.<br><br>
5773  * <p>
5774  * For cross-domain access to remote data, use an {@link Roo.data.ScriptTagProxy}.</em><br><br>
5775  * <p>
5776  * Be aware that to enable the browser to parse an XML document, the server must set
5777  * the Content-Type header in the HTTP response to "text/xml".
5778  * @constructor
5779  * @param {Object} conn Connection config options to add to each request (e.g. {url: 'foo.php'} or
5780  * an {@link Roo.data.Connection} object.  If a Connection config is passed, the singleton {@link Roo.Ajax} object
5781  * will be used to make the request.
5782  */
5783 Roo.data.HttpProxy = function(conn){
5784     Roo.data.HttpProxy.superclass.constructor.call(this);
5785     // is conn a conn config or a real conn?
5786     this.conn = conn;
5787     this.useAjax = !conn || !conn.events;
5788   
5789 };
5790
5791 Roo.extend(Roo.data.HttpProxy, Roo.data.DataProxy, {
5792     // thse are take from connection...
5793     
5794     /**
5795      * @cfg {String} url (Optional) The default URL to be used for requests to the server. (defaults to undefined)
5796      */
5797     /**
5798      * @cfg {Object} extraParams (Optional) An object containing properties which are used as
5799      * extra parameters to each request made by this object. (defaults to undefined)
5800      */
5801     /**
5802      * @cfg {Object} defaultHeaders (Optional) An object containing request headers which are added
5803      *  to each request made by this object. (defaults to undefined)
5804      */
5805     /**
5806      * @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)
5807      */
5808     /**
5809      * @cfg {Number} timeout (Optional) The timeout in milliseconds to be used for requests. (defaults to 30000)
5810      */
5811      /**
5812      * @cfg {Boolean} autoAbort (Optional) Whether this request should abort any pending requests. (defaults to false)
5813      * @type Boolean
5814      */
5815   
5816
5817     /**
5818      * @cfg {Boolean} disableCaching (Optional) True to add a unique cache-buster param to GET requests. (defaults to true)
5819      * @type Boolean
5820      */
5821     /**
5822      * Return the {@link Roo.data.Connection} object being used by this Proxy.
5823      * @return {Connection} The Connection object. This object may be used to subscribe to events on
5824      * a finer-grained basis than the DataProxy events.
5825      */
5826     getConnection : function(){
5827         return this.useAjax ? Roo.Ajax : this.conn;
5828     },
5829
5830     /**
5831      * Load data from the configured {@link Roo.data.Connection}, read the data object into
5832      * a block of Roo.data.Records using the passed {@link Roo.data.DataReader} implementation, and
5833      * process that block using the passed callback.
5834      * @param {Object} params An object containing properties which are to be used as HTTP parameters
5835      * for the request to the remote server.
5836      * @param {Roo.data.DataReader} reader The Reader object which converts the data
5837      * object into a block of Roo.data.Records.
5838      * @param {Function} callback The function into which to pass the block of Roo.data.Records.
5839      * The function must be passed <ul>
5840      * <li>The Record block object</li>
5841      * <li>The "arg" argument from the load function</li>
5842      * <li>A boolean success indicator</li>
5843      * </ul>
5844      * @param {Object} scope The scope in which to call the callback
5845      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
5846      */
5847     load : function(params, reader, callback, scope, arg){
5848         if(this.fireEvent("beforeload", this, params) !== false){
5849             var  o = {
5850                 params : params || {},
5851                 request: {
5852                     callback : callback,
5853                     scope : scope,
5854                     arg : arg
5855                 },
5856                 reader: reader,
5857                 callback : this.loadResponse,
5858                 scope: this
5859             };
5860             if(this.useAjax){
5861                 Roo.applyIf(o, this.conn);
5862                 if(this.activeRequest){
5863                     Roo.Ajax.abort(this.activeRequest);
5864                 }
5865                 this.activeRequest = Roo.Ajax.request(o);
5866             }else{
5867                 this.conn.request(o);
5868             }
5869         }else{
5870             callback.call(scope||this, null, arg, false);
5871         }
5872     },
5873
5874     // private
5875     loadResponse : function(o, success, response){
5876         delete this.activeRequest;
5877         if(!success){
5878             this.fireEvent("loadexception", this, o, response);
5879             o.request.callback.call(o.request.scope, null, o.request.arg, false);
5880             return;
5881         }
5882         var result;
5883         try {
5884             result = o.reader.read(response);
5885         }catch(e){
5886             this.fireEvent("loadexception", this, o, response, e);
5887             o.request.callback.call(o.request.scope, null, o.request.arg, false);
5888             return;
5889         }
5890         
5891         this.fireEvent("load", this, o, o.request.arg);
5892         o.request.callback.call(o.request.scope, result, o.request.arg, true);
5893     },
5894
5895     // private
5896     update : function(dataSet){
5897
5898     },
5899
5900     // private
5901     updateResponse : function(dataSet){
5902
5903     }
5904 });/*
5905  * Based on:
5906  * Ext JS Library 1.1.1
5907  * Copyright(c) 2006-2007, Ext JS, LLC.
5908  *
5909  * Originally Released Under LGPL - original licence link has changed is not relivant.
5910  *
5911  * Fork - LGPL
5912  * <script type="text/javascript">
5913  */
5914
5915 /**
5916  * @class Roo.data.ScriptTagProxy
5917  * An implementation of Roo.data.DataProxy that reads a data object from a URL which may be in a domain
5918  * other than the originating domain of the running page.<br><br>
5919  * <p>
5920  * <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
5921  * of the running page, you must use this class, rather than DataProxy.</em><br><br>
5922  * <p>
5923  * The content passed back from a server resource requested by a ScriptTagProxy is executable JavaScript
5924  * source code that is used as the source inside a &lt;script> tag.<br><br>
5925  * <p>
5926  * In order for the browser to process the returned data, the server must wrap the data object
5927  * with a call to a callback function, the name of which is passed as a parameter by the ScriptTagProxy.
5928  * Below is a Java example for a servlet which returns data for either a ScriptTagProxy, or an HttpProxy
5929  * depending on whether the callback name was passed:
5930  * <p>
5931  * <pre><code>
5932 boolean scriptTag = false;
5933 String cb = request.getParameter("callback");
5934 if (cb != null) {
5935     scriptTag = true;
5936     response.setContentType("text/javascript");
5937 } else {
5938     response.setContentType("application/x-json");
5939 }
5940 Writer out = response.getWriter();
5941 if (scriptTag) {
5942     out.write(cb + "(");
5943 }
5944 out.print(dataBlock.toJsonString());
5945 if (scriptTag) {
5946     out.write(");");
5947 }
5948 </pre></code>
5949  *
5950  * @constructor
5951  * @param {Object} config A configuration object.
5952  */
5953 Roo.data.ScriptTagProxy = function(config){
5954     Roo.data.ScriptTagProxy.superclass.constructor.call(this);
5955     Roo.apply(this, config);
5956     this.head = document.getElementsByTagName("head")[0];
5957 };
5958
5959 Roo.data.ScriptTagProxy.TRANS_ID = 1000;
5960
5961 Roo.extend(Roo.data.ScriptTagProxy, Roo.data.DataProxy, {
5962     /**
5963      * @cfg {String} url The URL from which to request the data object.
5964      */
5965     /**
5966      * @cfg {Number} timeout (Optional) The number of milliseconds to wait for a response. Defaults to 30 seconds.
5967      */
5968     timeout : 30000,
5969     /**
5970      * @cfg {String} callbackParam (Optional) The name of the parameter to pass to the server which tells
5971      * the server the name of the callback function set up by the load call to process the returned data object.
5972      * Defaults to "callback".<p>The server-side processing must read this parameter value, and generate
5973      * javascript output which calls this named function passing the data object as its only parameter.
5974      */
5975     callbackParam : "callback",
5976     /**
5977      *  @cfg {Boolean} nocache (Optional) Defaults to true. Disable cacheing by adding a unique parameter
5978      * name to the request.
5979      */
5980     nocache : true,
5981
5982     /**
5983      * Load data from the configured URL, read the data object into
5984      * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
5985      * process that block using the passed callback.
5986      * @param {Object} params An object containing properties which are to be used as HTTP parameters
5987      * for the request to the remote server.
5988      * @param {Roo.data.DataReader} reader The Reader object which converts the data
5989      * object into a block of Roo.data.Records.
5990      * @param {Function} callback The function into which to pass the block of Roo.data.Records.
5991      * The function must be passed <ul>
5992      * <li>The Record block object</li>
5993      * <li>The "arg" argument from the load function</li>
5994      * <li>A boolean success indicator</li>
5995      * </ul>
5996      * @param {Object} scope The scope in which to call the callback
5997      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
5998      */
5999     load : function(params, reader, callback, scope, arg){
6000         if(this.fireEvent("beforeload", this, params) !== false){
6001
6002             var p = Roo.urlEncode(Roo.apply(params, this.extraParams));
6003
6004             var url = this.url;
6005             url += (url.indexOf("?") != -1 ? "&" : "?") + p;
6006             if(this.nocache){
6007                 url += "&_dc=" + (new Date().getTime());
6008             }
6009             var transId = ++Roo.data.ScriptTagProxy.TRANS_ID;
6010             var trans = {
6011                 id : transId,
6012                 cb : "stcCallback"+transId,
6013                 scriptId : "stcScript"+transId,
6014                 params : params,
6015                 arg : arg,
6016                 url : url,
6017                 callback : callback,
6018                 scope : scope,
6019                 reader : reader
6020             };
6021             var conn = this;
6022
6023             window[trans.cb] = function(o){
6024                 conn.handleResponse(o, trans);
6025             };
6026
6027             url += String.format("&{0}={1}", this.callbackParam, trans.cb);
6028
6029             if(this.autoAbort !== false){
6030                 this.abort();
6031             }
6032
6033             trans.timeoutId = this.handleFailure.defer(this.timeout, this, [trans]);
6034
6035             var script = document.createElement("script");
6036             script.setAttribute("src", url);
6037             script.setAttribute("type", "text/javascript");
6038             script.setAttribute("id", trans.scriptId);
6039             this.head.appendChild(script);
6040
6041             this.trans = trans;
6042         }else{
6043             callback.call(scope||this, null, arg, false);
6044         }
6045     },
6046
6047     // private
6048     isLoading : function(){
6049         return this.trans ? true : false;
6050     },
6051
6052     /**
6053      * Abort the current server request.
6054      */
6055     abort : function(){
6056         if(this.isLoading()){
6057             this.destroyTrans(this.trans);
6058         }
6059     },
6060
6061     // private
6062     destroyTrans : function(trans, isLoaded){
6063         this.head.removeChild(document.getElementById(trans.scriptId));
6064         clearTimeout(trans.timeoutId);
6065         if(isLoaded){
6066             window[trans.cb] = undefined;
6067             try{
6068                 delete window[trans.cb];
6069             }catch(e){}
6070         }else{
6071             // if hasn't been loaded, wait for load to remove it to prevent script error
6072             window[trans.cb] = function(){
6073                 window[trans.cb] = undefined;
6074                 try{
6075                     delete window[trans.cb];
6076                 }catch(e){}
6077             };
6078         }
6079     },
6080
6081     // private
6082     handleResponse : function(o, trans){
6083         this.trans = false;
6084         this.destroyTrans(trans, true);
6085         var result;
6086         try {
6087             result = trans.reader.readRecords(o);
6088         }catch(e){
6089             this.fireEvent("loadexception", this, o, trans.arg, e);
6090             trans.callback.call(trans.scope||window, null, trans.arg, false);
6091             return;
6092         }
6093         this.fireEvent("load", this, o, trans.arg);
6094         trans.callback.call(trans.scope||window, result, trans.arg, true);
6095     },
6096
6097     // private
6098     handleFailure : function(trans){
6099         this.trans = false;
6100         this.destroyTrans(trans, false);
6101         this.fireEvent("loadexception", this, null, trans.arg);
6102         trans.callback.call(trans.scope||window, null, trans.arg, false);
6103     }
6104 });/*
6105  * Based on:
6106  * Ext JS Library 1.1.1
6107  * Copyright(c) 2006-2007, Ext JS, LLC.
6108  *
6109  * Originally Released Under LGPL - original licence link has changed is not relivant.
6110  *
6111  * Fork - LGPL
6112  * <script type="text/javascript">
6113  */
6114
6115 /**
6116  * @class Roo.data.JsonReader
6117  * @extends Roo.data.DataReader
6118  * Data reader class to create an Array of Roo.data.Record objects from a JSON response
6119  * based on mappings in a provided Roo.data.Record constructor.
6120  * 
6121  * The default behaviour of a store is to send ?_requestMeta=1, unless the class has recieved 'metaData' property
6122  * in the reply previously. 
6123  * 
6124  * <p>
6125  * Example code:
6126  * <pre><code>
6127 var RecordDef = Roo.data.Record.create([
6128     {name: 'name', mapping: 'name'},     // "mapping" property not needed if it's the same as "name"
6129     {name: 'occupation'}                 // This field will use "occupation" as the mapping.
6130 ]);
6131 var myReader = new Roo.data.JsonReader({
6132     totalProperty: "results",    // The property which contains the total dataset size (optional)
6133     root: "rows",                // The property which contains an Array of row objects
6134     id: "id"                     // The property within each row object that provides an ID for the record (optional)
6135 }, RecordDef);
6136 </code></pre>
6137  * <p>
6138  * This would consume a JSON file like this:
6139  * <pre><code>
6140 { 'results': 2, 'rows': [
6141     { 'id': 1, 'name': 'Bill', occupation: 'Gardener' },
6142     { 'id': 2, 'name': 'Ben', occupation: 'Horticulturalist' } ]
6143 }
6144 </code></pre>
6145  * @cfg {String} totalProperty Name of the property from which to retrieve the total number of records
6146  * in the dataset. This is only needed if the whole dataset is not passed in one go, but is being
6147  * paged from the remote server.
6148  * @cfg {String} successProperty Name of the property from which to retrieve the success attribute used by forms.
6149  * @cfg {String} root name of the property which contains the Array of row objects.
6150  * @cfg {String} id Name of the property within a row object that contains a record identifier value.
6151  * @constructor
6152  * Create a new JsonReader
6153  * @param {Object} meta Metadata configuration options
6154  * @param {Object} recordType Either an Array of field definition objects,
6155  * or an {@link Roo.data.Record} object created using {@link Roo.data.Record#create}.
6156  */
6157 Roo.data.JsonReader = function(meta, recordType){
6158     
6159     meta = meta || {};
6160     // set some defaults:
6161     Roo.applyIf(meta, {
6162         totalProperty: 'total',
6163         successProperty : 'success',
6164         root : 'data',
6165         id : 'id'
6166     });
6167     
6168     Roo.data.JsonReader.superclass.constructor.call(this, meta, recordType||meta.fields);
6169 };
6170 Roo.extend(Roo.data.JsonReader, Roo.data.DataReader, {
6171     
6172     /**
6173      * @prop {Boolean} metaFromRemote  - if the meta data was loaded from the remote source.
6174      * Used by Store query builder to append _requestMeta to params.
6175      * 
6176      */
6177     metaFromRemote : false,
6178     /**
6179      * This method is only used by a DataProxy which has retrieved data from a remote server.
6180      * @param {Object} response The XHR object which contains the JSON data in its responseText.
6181      * @return {Object} data A data block which is used by an Roo.data.Store object as
6182      * a cache of Roo.data.Records.
6183      */
6184     read : function(response){
6185         var json = response.responseText;
6186        
6187         var o = /* eval:var:o */ eval("("+json+")");
6188         if(!o) {
6189             throw {message: "JsonReader.read: Json object not found"};
6190         }
6191         
6192         if(o.metaData){
6193             
6194             delete this.ef;
6195             this.metaFromRemote = true;
6196             this.meta = o.metaData;
6197             this.recordType = Roo.data.Record.create(o.metaData.fields);
6198             this.onMetaChange(this.meta, this.recordType, o);
6199         }
6200         return this.readRecords(o);
6201     },
6202
6203     // private function a store will implement
6204     onMetaChange : function(meta, recordType, o){
6205
6206     },
6207
6208     /**
6209          * @ignore
6210          */
6211     simpleAccess: function(obj, subsc) {
6212         return obj[subsc];
6213     },
6214
6215         /**
6216          * @ignore
6217          */
6218     getJsonAccessor: function(){
6219         var re = /[\[\.]/;
6220         return function(expr) {
6221             try {
6222                 return(re.test(expr))
6223                     ? new Function("obj", "return obj." + expr)
6224                     : function(obj){
6225                         return obj[expr];
6226                     };
6227             } catch(e){}
6228             return Roo.emptyFn;
6229         };
6230     }(),
6231
6232     /**
6233      * Create a data block containing Roo.data.Records from an XML document.
6234      * @param {Object} o An object which contains an Array of row objects in the property specified
6235      * in the config as 'root, and optionally a property, specified in the config as 'totalProperty'
6236      * which contains the total size of the dataset.
6237      * @return {Object} data A data block which is used by an Roo.data.Store object as
6238      * a cache of Roo.data.Records.
6239      */
6240     readRecords : function(o){
6241         /**
6242          * After any data loads, the raw JSON data is available for further custom processing.
6243          * @type Object
6244          */
6245         this.o = o;
6246         var s = this.meta, Record = this.recordType,
6247             f = Record.prototype.fields, fi = f.items, fl = f.length;
6248
6249 //      Generate extraction functions for the totalProperty, the root, the id, and for each field
6250         if (!this.ef) {
6251             if(s.totalProperty) {
6252                     this.getTotal = this.getJsonAccessor(s.totalProperty);
6253                 }
6254                 if(s.successProperty) {
6255                     this.getSuccess = this.getJsonAccessor(s.successProperty);
6256                 }
6257                 this.getRoot = s.root ? this.getJsonAccessor(s.root) : function(p){return p;};
6258                 if (s.id) {
6259                         var g = this.getJsonAccessor(s.id);
6260                         this.getId = function(rec) {
6261                                 var r = g(rec);
6262                                 return (r === undefined || r === "") ? null : r;
6263                         };
6264                 } else {
6265                         this.getId = function(){return null;};
6266                 }
6267             this.ef = [];
6268             for(var jj = 0; jj < fl; jj++){
6269                 f = fi[jj];
6270                 var map = (f.mapping !== undefined && f.mapping !== null) ? f.mapping : f.name;
6271                 this.ef[jj] = this.getJsonAccessor(map);
6272             }
6273         }
6274
6275         var root = this.getRoot(o), c = root.length, totalRecords = c, success = true;
6276         if(s.totalProperty){
6277             var vt = parseInt(this.getTotal(o), 10);
6278             if(!isNaN(vt)){
6279                 totalRecords = vt;
6280             }
6281         }
6282         if(s.successProperty){
6283             var vs = this.getSuccess(o);
6284             if(vs === false || vs === 'false'){
6285                 success = false;
6286             }
6287         }
6288         var records = [];
6289             for(var i = 0; i < c; i++){
6290                     var n = root[i];
6291                 var values = {};
6292                 var id = this.getId(n);
6293                 for(var j = 0; j < fl; j++){
6294                     f = fi[j];
6295                 var v = this.ef[j](n);
6296                 if (!f.convert) {
6297                     Roo.log('missing convert for ' + f.name);
6298                     Roo.log(f);
6299                     continue;
6300                 }
6301                 values[f.name] = f.convert((v !== undefined) ? v : f.defaultValue);
6302                 }
6303                 var record = new Record(values, id);
6304                 record.json = n;
6305                 records[i] = record;
6306             }
6307             return {
6308             raw : o,
6309                 success : success,
6310                 records : records,
6311                 totalRecords : totalRecords
6312             };
6313     }
6314 });/*
6315  * Based on:
6316  * Ext JS Library 1.1.1
6317  * Copyright(c) 2006-2007, Ext JS, LLC.
6318  *
6319  * Originally Released Under LGPL - original licence link has changed is not relivant.
6320  *
6321  * Fork - LGPL
6322  * <script type="text/javascript">
6323  */
6324
6325 /**
6326  * @class Roo.data.XmlReader
6327  * @extends Roo.data.DataReader
6328  * Data reader class to create an Array of {@link Roo.data.Record} objects from an XML document
6329  * based on mappings in a provided Roo.data.Record constructor.<br><br>
6330  * <p>
6331  * <em>Note that in order for the browser to parse a returned XML document, the Content-Type
6332  * header in the HTTP response must be set to "text/xml".</em>
6333  * <p>
6334  * Example code:
6335  * <pre><code>
6336 var RecordDef = Roo.data.Record.create([
6337    {name: 'name', mapping: 'name'},     // "mapping" property not needed if it's the same as "name"
6338    {name: 'occupation'}                 // This field will use "occupation" as the mapping.
6339 ]);
6340 var myReader = new Roo.data.XmlReader({
6341    totalRecords: "results", // The element which contains the total dataset size (optional)
6342    record: "row",           // The repeated element which contains row information
6343    id: "id"                 // The element within the row that provides an ID for the record (optional)
6344 }, RecordDef);
6345 </code></pre>
6346  * <p>
6347  * This would consume an XML file like this:
6348  * <pre><code>
6349 &lt;?xml?>
6350 &lt;dataset>
6351  &lt;results>2&lt;/results>
6352  &lt;row>
6353    &lt;id>1&lt;/id>
6354    &lt;name>Bill&lt;/name>
6355    &lt;occupation>Gardener&lt;/occupation>
6356  &lt;/row>
6357  &lt;row>
6358    &lt;id>2&lt;/id>
6359    &lt;name>Ben&lt;/name>
6360    &lt;occupation>Horticulturalist&lt;/occupation>
6361  &lt;/row>
6362 &lt;/dataset>
6363 </code></pre>
6364  * @cfg {String} totalRecords The DomQuery path from which to retrieve the total number of records
6365  * in the dataset. This is only needed if the whole dataset is not passed in one go, but is being
6366  * paged from the remote server.
6367  * @cfg {String} record The DomQuery path to the repeated element which contains record information.
6368  * @cfg {String} success The DomQuery path to the success attribute used by forms.
6369  * @cfg {String} id The DomQuery path relative from the record element to the element that contains
6370  * a record identifier value.
6371  * @constructor
6372  * Create a new XmlReader
6373  * @param {Object} meta Metadata configuration options
6374  * @param {Mixed} recordType The definition of the data record type to produce.  This can be either a valid
6375  * Record subclass created with {@link Roo.data.Record#create}, or an array of objects with which to call
6376  * Roo.data.Record.create.  See the {@link Roo.data.Record} class for more details.
6377  */
6378 Roo.data.XmlReader = function(meta, recordType){
6379     meta = meta || {};
6380     Roo.data.XmlReader.superclass.constructor.call(this, meta, recordType||meta.fields);
6381 };
6382 Roo.extend(Roo.data.XmlReader, Roo.data.DataReader, {
6383     /**
6384      * This method is only used by a DataProxy which has retrieved data from a remote server.
6385          * @param {Object} response The XHR object which contains the parsed XML document.  The response is expected
6386          * to contain a method called 'responseXML' that returns an XML document object.
6387      * @return {Object} records A data block which is used by an {@link Roo.data.Store} as
6388      * a cache of Roo.data.Records.
6389      */
6390     read : function(response){
6391         var doc = response.responseXML;
6392         if(!doc) {
6393             throw {message: "XmlReader.read: XML Document not available"};
6394         }
6395         return this.readRecords(doc);
6396     },
6397
6398     /**
6399      * Create a data block containing Roo.data.Records from an XML document.
6400          * @param {Object} doc A parsed XML document.
6401      * @return {Object} records A data block which is used by an {@link Roo.data.Store} as
6402      * a cache of Roo.data.Records.
6403      */
6404     readRecords : function(doc){
6405         /**
6406          * After any data loads/reads, the raw XML Document is available for further custom processing.
6407          * @type XMLDocument
6408          */
6409         this.xmlData = doc;
6410         var root = doc.documentElement || doc;
6411         var q = Roo.DomQuery;
6412         var recordType = this.recordType, fields = recordType.prototype.fields;
6413         var sid = this.meta.id;
6414         var totalRecords = 0, success = true;
6415         if(this.meta.totalRecords){
6416             totalRecords = q.selectNumber(this.meta.totalRecords, root, 0);
6417         }
6418         
6419         if(this.meta.success){
6420             var sv = q.selectValue(this.meta.success, root, true);
6421             success = sv !== false && sv !== 'false';
6422         }
6423         var records = [];
6424         var ns = q.select(this.meta.record, root);
6425         for(var i = 0, len = ns.length; i < len; i++) {
6426                 var n = ns[i];
6427                 var values = {};
6428                 var id = sid ? q.selectValue(sid, n) : undefined;
6429                 for(var j = 0, jlen = fields.length; j < jlen; j++){
6430                     var f = fields.items[j];
6431                 var v = q.selectValue(f.mapping || f.name, n, f.defaultValue);
6432                     v = f.convert(v);
6433                     values[f.name] = v;
6434                 }
6435                 var record = new recordType(values, id);
6436                 record.node = n;
6437                 records[records.length] = record;
6438             }
6439
6440             return {
6441                 success : success,
6442                 records : records,
6443                 totalRecords : totalRecords || records.length
6444             };
6445     }
6446 });/*
6447  * Based on:
6448  * Ext JS Library 1.1.1
6449  * Copyright(c) 2006-2007, Ext JS, LLC.
6450  *
6451  * Originally Released Under LGPL - original licence link has changed is not relivant.
6452  *
6453  * Fork - LGPL
6454  * <script type="text/javascript">
6455  */
6456
6457 /**
6458  * @class Roo.data.ArrayReader
6459  * @extends Roo.data.DataReader
6460  * Data reader class to create an Array of Roo.data.Record objects from an Array.
6461  * Each element of that Array represents a row of data fields. The
6462  * fields are pulled into a Record object using as a subscript, the <em>mapping</em> property
6463  * of the field definition if it exists, or the field's ordinal position in the definition.<br>
6464  * <p>
6465  * Example code:.
6466  * <pre><code>
6467 var RecordDef = Roo.data.Record.create([
6468     {name: 'name', mapping: 1},         // "mapping" only needed if an "id" field is present which
6469     {name: 'occupation', mapping: 2}    // precludes using the ordinal position as the index.
6470 ]);
6471 var myReader = new Roo.data.ArrayReader({
6472     id: 0                     // The subscript within row Array that provides an ID for the Record (optional)
6473 }, RecordDef);
6474 </code></pre>
6475  * <p>
6476  * This would consume an Array like this:
6477  * <pre><code>
6478 [ [1, 'Bill', 'Gardener'], [2, 'Ben', 'Horticulturalist'] ]
6479   </code></pre>
6480  * @cfg {String} id (optional) The subscript within row Array that provides an ID for the Record
6481  * @constructor
6482  * Create a new JsonReader
6483  * @param {Object} meta Metadata configuration options.
6484  * @param {Object} recordType Either an Array of field definition objects
6485  * as specified to {@link Roo.data.Record#create},
6486  * or an {@link Roo.data.Record} object
6487  * created using {@link Roo.data.Record#create}.
6488  */
6489 Roo.data.ArrayReader = function(meta, recordType){
6490     Roo.data.ArrayReader.superclass.constructor.call(this, meta, recordType);
6491 };
6492
6493 Roo.extend(Roo.data.ArrayReader, Roo.data.JsonReader, {
6494     /**
6495      * Create a data block containing Roo.data.Records from an XML document.
6496      * @param {Object} o An Array of row objects which represents the dataset.
6497      * @return {Object} data A data block which is used by an Roo.data.Store object as
6498      * a cache of Roo.data.Records.
6499      */
6500     readRecords : function(o){
6501         var sid = this.meta ? this.meta.id : null;
6502         var recordType = this.recordType, fields = recordType.prototype.fields;
6503         var records = [];
6504         var root = o;
6505             for(var i = 0; i < root.length; i++){
6506                     var n = root[i];
6507                 var values = {};
6508                 var id = ((sid || sid === 0) && n[sid] !== undefined && n[sid] !== "" ? n[sid] : null);
6509                 for(var j = 0, jlen = fields.length; j < jlen; j++){
6510                 var f = fields.items[j];
6511                 var k = f.mapping !== undefined && f.mapping !== null ? f.mapping : j;
6512                 var v = n[k] !== undefined ? n[k] : f.defaultValue;
6513                 v = f.convert(v);
6514                 values[f.name] = v;
6515             }
6516                 var record = new recordType(values, id);
6517                 record.json = n;
6518                 records[records.length] = record;
6519             }
6520             return {
6521                 records : records,
6522                 totalRecords : records.length
6523             };
6524     }
6525 });/*
6526  * Based on:
6527  * Ext JS Library 1.1.1
6528  * Copyright(c) 2006-2007, Ext JS, LLC.
6529  *
6530  * Originally Released Under LGPL - original licence link has changed is not relivant.
6531  *
6532  * Fork - LGPL
6533  * <script type="text/javascript">
6534  */
6535
6536
6537 /**
6538  * @class Roo.data.Tree
6539  * @extends Roo.util.Observable
6540  * Represents a tree data structure and bubbles all the events for its nodes. The nodes
6541  * in the tree have most standard DOM functionality.
6542  * @constructor
6543  * @param {Node} root (optional) The root node
6544  */
6545 Roo.data.Tree = function(root){
6546    this.nodeHash = {};
6547    /**
6548     * The root node for this tree
6549     * @type Node
6550     */
6551    this.root = null;
6552    if(root){
6553        this.setRootNode(root);
6554    }
6555    this.addEvents({
6556        /**
6557         * @event append
6558         * Fires when a new child node is appended to a node in this tree.
6559         * @param {Tree} tree The owner tree
6560         * @param {Node} parent The parent node
6561         * @param {Node} node The newly appended node
6562         * @param {Number} index The index of the newly appended node
6563         */
6564        "append" : true,
6565        /**
6566         * @event remove
6567         * Fires when a child node is removed from a node in this tree.
6568         * @param {Tree} tree The owner tree
6569         * @param {Node} parent The parent node
6570         * @param {Node} node The child node removed
6571         */
6572        "remove" : true,
6573        /**
6574         * @event move
6575         * Fires when a node is moved to a new location in the tree
6576         * @param {Tree} tree The owner tree
6577         * @param {Node} node The node moved
6578         * @param {Node} oldParent The old parent of this node
6579         * @param {Node} newParent The new parent of this node
6580         * @param {Number} index The index it was moved to
6581         */
6582        "move" : true,
6583        /**
6584         * @event insert
6585         * Fires when a new child node is inserted in a node in this tree.
6586         * @param {Tree} tree The owner tree
6587         * @param {Node} parent The parent node
6588         * @param {Node} node The child node inserted
6589         * @param {Node} refNode The child node the node was inserted before
6590         */
6591        "insert" : true,
6592        /**
6593         * @event beforeappend
6594         * Fires before a new child is appended to a node in this tree, return false to cancel the append.
6595         * @param {Tree} tree The owner tree
6596         * @param {Node} parent The parent node
6597         * @param {Node} node The child node to be appended
6598         */
6599        "beforeappend" : true,
6600        /**
6601         * @event beforeremove
6602         * Fires before a child is removed from a node in this tree, return false to cancel the remove.
6603         * @param {Tree} tree The owner tree
6604         * @param {Node} parent The parent node
6605         * @param {Node} node The child node to be removed
6606         */
6607        "beforeremove" : true,
6608        /**
6609         * @event beforemove
6610         * Fires before a node is moved to a new location in the tree. Return false to cancel the move.
6611         * @param {Tree} tree The owner tree
6612         * @param {Node} node The node being moved
6613         * @param {Node} oldParent The parent of the node
6614         * @param {Node} newParent The new parent the node is moving to
6615         * @param {Number} index The index it is being moved to
6616         */
6617        "beforemove" : true,
6618        /**
6619         * @event beforeinsert
6620         * Fires before a new child is inserted in a node in this tree, return false to cancel the insert.
6621         * @param {Tree} tree The owner tree
6622         * @param {Node} parent The parent node
6623         * @param {Node} node The child node to be inserted
6624         * @param {Node} refNode The child node the node is being inserted before
6625         */
6626        "beforeinsert" : true
6627    });
6628
6629     Roo.data.Tree.superclass.constructor.call(this);
6630 };
6631
6632 Roo.extend(Roo.data.Tree, Roo.util.Observable, {
6633     pathSeparator: "/",
6634
6635     proxyNodeEvent : function(){
6636         return this.fireEvent.apply(this, arguments);
6637     },
6638
6639     /**
6640      * Returns the root node for this tree.
6641      * @return {Node}
6642      */
6643     getRootNode : function(){
6644         return this.root;
6645     },
6646
6647     /**
6648      * Sets the root node for this tree.
6649      * @param {Node} node
6650      * @return {Node}
6651      */
6652     setRootNode : function(node){
6653         this.root = node;
6654         node.ownerTree = this;
6655         node.isRoot = true;
6656         this.registerNode(node);
6657         return node;
6658     },
6659
6660     /**
6661      * Gets a node in this tree by its id.
6662      * @param {String} id
6663      * @return {Node}
6664      */
6665     getNodeById : function(id){
6666         return this.nodeHash[id];
6667     },
6668
6669     registerNode : function(node){
6670         this.nodeHash[node.id] = node;
6671     },
6672
6673     unregisterNode : function(node){
6674         delete this.nodeHash[node.id];
6675     },
6676
6677     toString : function(){
6678         return "[Tree"+(this.id?" "+this.id:"")+"]";
6679     }
6680 });
6681
6682 /**
6683  * @class Roo.data.Node
6684  * @extends Roo.util.Observable
6685  * @cfg {Boolean} leaf true if this node is a leaf and does not have children
6686  * @cfg {String} id The id for this node. If one is not specified, one is generated.
6687  * @constructor
6688  * @param {Object} attributes The attributes/config for the node
6689  */
6690 Roo.data.Node = function(attributes){
6691     /**
6692      * The attributes supplied for the node. You can use this property to access any custom attributes you supplied.
6693      * @type {Object}
6694      */
6695     this.attributes = attributes || {};
6696     this.leaf = this.attributes.leaf;
6697     /**
6698      * The node id. @type String
6699      */
6700     this.id = this.attributes.id;
6701     if(!this.id){
6702         this.id = Roo.id(null, "ynode-");
6703         this.attributes.id = this.id;
6704     }
6705      
6706     
6707     /**
6708      * All child nodes of this node. @type Array
6709      */
6710     this.childNodes = [];
6711     if(!this.childNodes.indexOf){ // indexOf is a must
6712         this.childNodes.indexOf = function(o){
6713             for(var i = 0, len = this.length; i < len; i++){
6714                 if(this[i] == o) {
6715                     return i;
6716                 }
6717             }
6718             return -1;
6719         };
6720     }
6721     /**
6722      * The parent node for this node. @type Node
6723      */
6724     this.parentNode = null;
6725     /**
6726      * The first direct child node of this node, or null if this node has no child nodes. @type Node
6727      */
6728     this.firstChild = null;
6729     /**
6730      * The last direct child node of this node, or null if this node has no child nodes. @type Node
6731      */
6732     this.lastChild = null;
6733     /**
6734      * The node immediately preceding this node in the tree, or null if there is no sibling node. @type Node
6735      */
6736     this.previousSibling = null;
6737     /**
6738      * The node immediately following this node in the tree, or null if there is no sibling node. @type Node
6739      */
6740     this.nextSibling = null;
6741
6742     this.addEvents({
6743        /**
6744         * @event append
6745         * Fires when a new child node is appended
6746         * @param {Tree} tree The owner tree
6747         * @param {Node} this This node
6748         * @param {Node} node The newly appended node
6749         * @param {Number} index The index of the newly appended node
6750         */
6751        "append" : true,
6752        /**
6753         * @event remove
6754         * Fires when a child node is removed
6755         * @param {Tree} tree The owner tree
6756         * @param {Node} this This node
6757         * @param {Node} node The removed node
6758         */
6759        "remove" : true,
6760        /**
6761         * @event move
6762         * Fires when this node is moved to a new location in the tree
6763         * @param {Tree} tree The owner tree
6764         * @param {Node} this This node
6765         * @param {Node} oldParent The old parent of this node
6766         * @param {Node} newParent The new parent of this node
6767         * @param {Number} index The index it was moved to
6768         */
6769        "move" : true,
6770        /**
6771         * @event insert
6772         * Fires when a new child node is inserted.
6773         * @param {Tree} tree The owner tree
6774         * @param {Node} this This node
6775         * @param {Node} node The child node inserted
6776         * @param {Node} refNode The child node the node was inserted before
6777         */
6778        "insert" : true,
6779        /**
6780         * @event beforeappend
6781         * Fires before a new child is appended, return false to cancel the append.
6782         * @param {Tree} tree The owner tree
6783         * @param {Node} this This node
6784         * @param {Node} node The child node to be appended
6785         */
6786        "beforeappend" : true,
6787        /**
6788         * @event beforeremove
6789         * Fires before a child is removed, return false to cancel the remove.
6790         * @param {Tree} tree The owner tree
6791         * @param {Node} this This node
6792         * @param {Node} node The child node to be removed
6793         */
6794        "beforeremove" : true,
6795        /**
6796         * @event beforemove
6797         * Fires before this node is moved to a new location in the tree. Return false to cancel the move.
6798         * @param {Tree} tree The owner tree
6799         * @param {Node} this This node
6800         * @param {Node} oldParent The parent of this node
6801         * @param {Node} newParent The new parent this node is moving to
6802         * @param {Number} index The index it is being moved to
6803         */
6804        "beforemove" : true,
6805        /**
6806         * @event beforeinsert
6807         * Fires before a new child is inserted, return false to cancel the insert.
6808         * @param {Tree} tree The owner tree
6809         * @param {Node} this This node
6810         * @param {Node} node The child node to be inserted
6811         * @param {Node} refNode The child node the node is being inserted before
6812         */
6813        "beforeinsert" : true
6814    });
6815     this.listeners = this.attributes.listeners;
6816     Roo.data.Node.superclass.constructor.call(this);
6817 };
6818
6819 Roo.extend(Roo.data.Node, Roo.util.Observable, {
6820     fireEvent : function(evtName){
6821         // first do standard event for this node
6822         if(Roo.data.Node.superclass.fireEvent.apply(this, arguments) === false){
6823             return false;
6824         }
6825         // then bubble it up to the tree if the event wasn't cancelled
6826         var ot = this.getOwnerTree();
6827         if(ot){
6828             if(ot.proxyNodeEvent.apply(ot, arguments) === false){
6829                 return false;
6830             }
6831         }
6832         return true;
6833     },
6834
6835     /**
6836      * Returns true if this node is a leaf
6837      * @return {Boolean}
6838      */
6839     isLeaf : function(){
6840         return this.leaf === true;
6841     },
6842
6843     // private
6844     setFirstChild : function(node){
6845         this.firstChild = node;
6846     },
6847
6848     //private
6849     setLastChild : function(node){
6850         this.lastChild = node;
6851     },
6852
6853
6854     /**
6855      * Returns true if this node is the last child of its parent
6856      * @return {Boolean}
6857      */
6858     isLast : function(){
6859        return (!this.parentNode ? true : this.parentNode.lastChild == this);
6860     },
6861
6862     /**
6863      * Returns true if this node is the first child of its parent
6864      * @return {Boolean}
6865      */
6866     isFirst : function(){
6867        return (!this.parentNode ? true : this.parentNode.firstChild == this);
6868     },
6869
6870     hasChildNodes : function(){
6871         return !this.isLeaf() && this.childNodes.length > 0;
6872     },
6873
6874     /**
6875      * Insert node(s) as the last child node of this node.
6876      * @param {Node/Array} node The node or Array of nodes to append
6877      * @return {Node} The appended node if single append, or null if an array was passed
6878      */
6879     appendChild : function(node){
6880         var multi = false;
6881         if(node instanceof Array){
6882             multi = node;
6883         }else if(arguments.length > 1){
6884             multi = arguments;
6885         }
6886         // if passed an array or multiple args do them one by one
6887         if(multi){
6888             for(var i = 0, len = multi.length; i < len; i++) {
6889                 this.appendChild(multi[i]);
6890             }
6891         }else{
6892             if(this.fireEvent("beforeappend", this.ownerTree, this, node) === false){
6893                 return false;
6894             }
6895             var index = this.childNodes.length;
6896             var oldParent = node.parentNode;
6897             // it's a move, make sure we move it cleanly
6898             if(oldParent){
6899                 if(node.fireEvent("beforemove", node.getOwnerTree(), node, oldParent, this, index) === false){
6900                     return false;
6901                 }
6902                 oldParent.removeChild(node);
6903             }
6904             index = this.childNodes.length;
6905             if(index == 0){
6906                 this.setFirstChild(node);
6907             }
6908             this.childNodes.push(node);
6909             node.parentNode = this;
6910             var ps = this.childNodes[index-1];
6911             if(ps){
6912                 node.previousSibling = ps;
6913                 ps.nextSibling = node;
6914             }else{
6915                 node.previousSibling = null;
6916             }
6917             node.nextSibling = null;
6918             this.setLastChild(node);
6919             node.setOwnerTree(this.getOwnerTree());
6920             this.fireEvent("append", this.ownerTree, this, node, index);
6921             if(oldParent){
6922                 node.fireEvent("move", this.ownerTree, node, oldParent, this, index);
6923             }
6924             return node;
6925         }
6926     },
6927
6928     /**
6929      * Removes a child node from this node.
6930      * @param {Node} node The node to remove
6931      * @return {Node} The removed node
6932      */
6933     removeChild : function(node){
6934         var index = this.childNodes.indexOf(node);
6935         if(index == -1){
6936             return false;
6937         }
6938         if(this.fireEvent("beforeremove", this.ownerTree, this, node) === false){
6939             return false;
6940         }
6941
6942         // remove it from childNodes collection
6943         this.childNodes.splice(index, 1);
6944
6945         // update siblings
6946         if(node.previousSibling){
6947             node.previousSibling.nextSibling = node.nextSibling;
6948         }
6949         if(node.nextSibling){
6950             node.nextSibling.previousSibling = node.previousSibling;
6951         }
6952
6953         // update child refs
6954         if(this.firstChild == node){
6955             this.setFirstChild(node.nextSibling);
6956         }
6957         if(this.lastChild == node){
6958             this.setLastChild(node.previousSibling);
6959         }
6960
6961         node.setOwnerTree(null);
6962         // clear any references from the node
6963         node.parentNode = null;
6964         node.previousSibling = null;
6965         node.nextSibling = null;
6966         this.fireEvent("remove", this.ownerTree, this, node);
6967         return node;
6968     },
6969
6970     /**
6971      * Inserts the first node before the second node in this nodes childNodes collection.
6972      * @param {Node} node The node to insert
6973      * @param {Node} refNode The node to insert before (if null the node is appended)
6974      * @return {Node} The inserted node
6975      */
6976     insertBefore : function(node, refNode){
6977         if(!refNode){ // like standard Dom, refNode can be null for append
6978             return this.appendChild(node);
6979         }
6980         // nothing to do
6981         if(node == refNode){
6982             return false;
6983         }
6984
6985         if(this.fireEvent("beforeinsert", this.ownerTree, this, node, refNode) === false){
6986             return false;
6987         }
6988         var index = this.childNodes.indexOf(refNode);
6989         var oldParent = node.parentNode;
6990         var refIndex = index;
6991
6992         // when moving internally, indexes will change after remove
6993         if(oldParent == this && this.childNodes.indexOf(node) < index){
6994             refIndex--;
6995         }
6996
6997         // it's a move, make sure we move it cleanly
6998         if(oldParent){
6999             if(node.fireEvent("beforemove", node.getOwnerTree(), node, oldParent, this, index, refNode) === false){
7000                 return false;
7001             }
7002             oldParent.removeChild(node);
7003         }
7004         if(refIndex == 0){
7005             this.setFirstChild(node);
7006         }
7007         this.childNodes.splice(refIndex, 0, node);
7008         node.parentNode = this;
7009         var ps = this.childNodes[refIndex-1];
7010         if(ps){
7011             node.previousSibling = ps;
7012             ps.nextSibling = node;
7013         }else{
7014             node.previousSibling = null;
7015         }
7016         node.nextSibling = refNode;
7017         refNode.previousSibling = node;
7018         node.setOwnerTree(this.getOwnerTree());
7019         this.fireEvent("insert", this.ownerTree, this, node, refNode);
7020         if(oldParent){
7021             node.fireEvent("move", this.ownerTree, node, oldParent, this, refIndex, refNode);
7022         }
7023         return node;
7024     },
7025
7026     /**
7027      * Returns the child node at the specified index.
7028      * @param {Number} index
7029      * @return {Node}
7030      */
7031     item : function(index){
7032         return this.childNodes[index];
7033     },
7034
7035     /**
7036      * Replaces one child node in this node with another.
7037      * @param {Node} newChild The replacement node
7038      * @param {Node} oldChild The node to replace
7039      * @return {Node} The replaced node
7040      */
7041     replaceChild : function(newChild, oldChild){
7042         this.insertBefore(newChild, oldChild);
7043         this.removeChild(oldChild);
7044         return oldChild;
7045     },
7046
7047     /**
7048      * Returns the index of a child node
7049      * @param {Node} node
7050      * @return {Number} The index of the node or -1 if it was not found
7051      */
7052     indexOf : function(child){
7053         return this.childNodes.indexOf(child);
7054     },
7055
7056     /**
7057      * Returns the tree this node is in.
7058      * @return {Tree}
7059      */
7060     getOwnerTree : function(){
7061         // if it doesn't have one, look for one
7062         if(!this.ownerTree){
7063             var p = this;
7064             while(p){
7065                 if(p.ownerTree){
7066                     this.ownerTree = p.ownerTree;
7067                     break;
7068                 }
7069                 p = p.parentNode;
7070             }
7071         }
7072         return this.ownerTree;
7073     },
7074
7075     /**
7076      * Returns depth of this node (the root node has a depth of 0)
7077      * @return {Number}
7078      */
7079     getDepth : function(){
7080         var depth = 0;
7081         var p = this;
7082         while(p.parentNode){
7083             ++depth;
7084             p = p.parentNode;
7085         }
7086         return depth;
7087     },
7088
7089     // private
7090     setOwnerTree : function(tree){
7091         // if it's move, we need to update everyone
7092         if(tree != this.ownerTree){
7093             if(this.ownerTree){
7094                 this.ownerTree.unregisterNode(this);
7095             }
7096             this.ownerTree = tree;
7097             var cs = this.childNodes;
7098             for(var i = 0, len = cs.length; i < len; i++) {
7099                 cs[i].setOwnerTree(tree);
7100             }
7101             if(tree){
7102                 tree.registerNode(this);
7103             }
7104         }
7105     },
7106
7107     /**
7108      * Returns the path for this node. The path can be used to expand or select this node programmatically.
7109      * @param {String} attr (optional) The attr to use for the path (defaults to the node's id)
7110      * @return {String} The path
7111      */
7112     getPath : function(attr){
7113         attr = attr || "id";
7114         var p = this.parentNode;
7115         var b = [this.attributes[attr]];
7116         while(p){
7117             b.unshift(p.attributes[attr]);
7118             p = p.parentNode;
7119         }
7120         var sep = this.getOwnerTree().pathSeparator;
7121         return sep + b.join(sep);
7122     },
7123
7124     /**
7125      * Bubbles up the tree from this node, calling the specified function with each node. The scope (<i>this</i>) of
7126      * function call will be the scope provided or the current node. The arguments to the function
7127      * will be the args provided or the current node. If the function returns false at any point,
7128      * the bubble is stopped.
7129      * @param {Function} fn The function to call
7130      * @param {Object} scope (optional) The scope of the function (defaults to current node)
7131      * @param {Array} args (optional) The args to call the function with (default to passing the current node)
7132      */
7133     bubble : function(fn, scope, args){
7134         var p = this;
7135         while(p){
7136             if(fn.call(scope || p, args || p) === false){
7137                 break;
7138             }
7139             p = p.parentNode;
7140         }
7141     },
7142
7143     /**
7144      * Cascades down the tree from this node, calling the specified function with each node. The scope (<i>this</i>) of
7145      * function call will be the scope provided or the current node. The arguments to the function
7146      * will be the args provided or the current node. If the function returns false at any point,
7147      * the cascade is stopped on that branch.
7148      * @param {Function} fn The function to call
7149      * @param {Object} scope (optional) The scope of the function (defaults to current node)
7150      * @param {Array} args (optional) The args to call the function with (default to passing the current node)
7151      */
7152     cascade : function(fn, scope, args){
7153         if(fn.call(scope || this, args || this) !== false){
7154             var cs = this.childNodes;
7155             for(var i = 0, len = cs.length; i < len; i++) {
7156                 cs[i].cascade(fn, scope, args);
7157             }
7158         }
7159     },
7160
7161     /**
7162      * Interates the child nodes of this node, calling the specified function with each node. The scope (<i>this</i>) of
7163      * function call will be the scope provided or the current node. The arguments to the function
7164      * will be the args provided or the current node. If the function returns false at any point,
7165      * the iteration stops.
7166      * @param {Function} fn The function to call
7167      * @param {Object} scope (optional) The scope of the function (defaults to current node)
7168      * @param {Array} args (optional) The args to call the function with (default to passing the current node)
7169      */
7170     eachChild : function(fn, scope, args){
7171         var cs = this.childNodes;
7172         for(var i = 0, len = cs.length; i < len; i++) {
7173                 if(fn.call(scope || this, args || cs[i]) === false){
7174                     break;
7175                 }
7176         }
7177     },
7178
7179     /**
7180      * Finds the first child that has the attribute with the specified value.
7181      * @param {String} attribute The attribute name
7182      * @param {Mixed} value The value to search for
7183      * @return {Node} The found child or null if none was found
7184      */
7185     findChild : function(attribute, value){
7186         var cs = this.childNodes;
7187         for(var i = 0, len = cs.length; i < len; i++) {
7188                 if(cs[i].attributes[attribute] == value){
7189                     return cs[i];
7190                 }
7191         }
7192         return null;
7193     },
7194
7195     /**
7196      * Finds the first child by a custom function. The child matches if the function passed
7197      * returns true.
7198      * @param {Function} fn
7199      * @param {Object} scope (optional)
7200      * @return {Node} The found child or null if none was found
7201      */
7202     findChildBy : function(fn, scope){
7203         var cs = this.childNodes;
7204         for(var i = 0, len = cs.length; i < len; i++) {
7205                 if(fn.call(scope||cs[i], cs[i]) === true){
7206                     return cs[i];
7207                 }
7208         }
7209         return null;
7210     },
7211
7212     /**
7213      * Sorts this nodes children using the supplied sort function
7214      * @param {Function} fn
7215      * @param {Object} scope (optional)
7216      */
7217     sort : function(fn, scope){
7218         var cs = this.childNodes;
7219         var len = cs.length;
7220         if(len > 0){
7221             var sortFn = scope ? function(){fn.apply(scope, arguments);} : fn;
7222             cs.sort(sortFn);
7223             for(var i = 0; i < len; i++){
7224                 var n = cs[i];
7225                 n.previousSibling = cs[i-1];
7226                 n.nextSibling = cs[i+1];
7227                 if(i == 0){
7228                     this.setFirstChild(n);
7229                 }
7230                 if(i == len-1){
7231                     this.setLastChild(n);
7232                 }
7233             }
7234         }
7235     },
7236
7237     /**
7238      * Returns true if this node is an ancestor (at any point) of the passed node.
7239      * @param {Node} node
7240      * @return {Boolean}
7241      */
7242     contains : function(node){
7243         return node.isAncestor(this);
7244     },
7245
7246     /**
7247      * Returns true if the passed node is an ancestor (at any point) of this node.
7248      * @param {Node} node
7249      * @return {Boolean}
7250      */
7251     isAncestor : function(node){
7252         var p = this.parentNode;
7253         while(p){
7254             if(p == node){
7255                 return true;
7256             }
7257             p = p.parentNode;
7258         }
7259         return false;
7260     },
7261
7262     toString : function(){
7263         return "[Node"+(this.id?" "+this.id:"")+"]";
7264     }
7265 });/*
7266  * Based on:
7267  * Ext JS Library 1.1.1
7268  * Copyright(c) 2006-2007, Ext JS, LLC.
7269  *
7270  * Originally Released Under LGPL - original licence link has changed is not relivant.
7271  *
7272  * Fork - LGPL
7273  * <script type="text/javascript">
7274  */
7275  
7276
7277 /**
7278  * @class Roo.ComponentMgr
7279  * Provides a common registry of all components on a page so that they can be easily accessed by component id (see {@link Roo.getCmp}).
7280  * @singleton
7281  */
7282 Roo.ComponentMgr = function(){
7283     var all = new Roo.util.MixedCollection();
7284
7285     return {
7286         /**
7287          * Registers a component.
7288          * @param {Roo.Component} c The component
7289          */
7290         register : function(c){
7291             all.add(c);
7292         },
7293
7294         /**
7295          * Unregisters a component.
7296          * @param {Roo.Component} c The component
7297          */
7298         unregister : function(c){
7299             all.remove(c);
7300         },
7301
7302         /**
7303          * Returns a component by id
7304          * @param {String} id The component id
7305          */
7306         get : function(id){
7307             return all.get(id);
7308         },
7309
7310         /**
7311          * Registers a function that will be called when a specified component is added to ComponentMgr
7312          * @param {String} id The component id
7313          * @param {Funtction} fn The callback function
7314          * @param {Object} scope The scope of the callback
7315          */
7316         onAvailable : function(id, fn, scope){
7317             all.on("add", function(index, o){
7318                 if(o.id == id){
7319                     fn.call(scope || o, o);
7320                     all.un("add", fn, scope);
7321                 }
7322             });
7323         }
7324     };
7325 }();/*
7326  * Based on:
7327  * Ext JS Library 1.1.1
7328  * Copyright(c) 2006-2007, Ext JS, LLC.
7329  *
7330  * Originally Released Under LGPL - original licence link has changed is not relivant.
7331  *
7332  * Fork - LGPL
7333  * <script type="text/javascript">
7334  */
7335  
7336 /**
7337  * @class Roo.Component
7338  * @extends Roo.util.Observable
7339  * Base class for all major Roo components.  All subclasses of Component can automatically participate in the standard
7340  * Roo component lifecycle of creation, rendering and destruction.  They also have automatic support for basic hide/show
7341  * and enable/disable behavior.  Component allows any subclass to be lazy-rendered into any {@link Roo.Container} and
7342  * to be automatically registered with the {@link Roo.ComponentMgr} so that it can be referenced at any time via {@link Roo.getCmp}.
7343  * All visual components (widgets) that require rendering into a layout should subclass Component.
7344  * @constructor
7345  * @param {Roo.Element/String/Object} config The configuration options.  If an element is passed, it is set as the internal
7346  * 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
7347  * and is used as the component id.  Otherwise, it is assumed to be a standard config object and is applied to the component.
7348  */
7349 Roo.Component = function(config){
7350     config = config || {};
7351     if(config.tagName || config.dom || typeof config == "string"){ // element object
7352         config = {el: config, id: config.id || config};
7353     }
7354     this.initialConfig = config;
7355
7356     Roo.apply(this, config);
7357     this.addEvents({
7358         /**
7359          * @event disable
7360          * Fires after the component is disabled.
7361              * @param {Roo.Component} this
7362              */
7363         disable : true,
7364         /**
7365          * @event enable
7366          * Fires after the component is enabled.
7367              * @param {Roo.Component} this
7368              */
7369         enable : true,
7370         /**
7371          * @event beforeshow
7372          * Fires before the component is shown.  Return false to stop the show.
7373              * @param {Roo.Component} this
7374              */
7375         beforeshow : true,
7376         /**
7377          * @event show
7378          * Fires after the component is shown.
7379              * @param {Roo.Component} this
7380              */
7381         show : true,
7382         /**
7383          * @event beforehide
7384          * Fires before the component is hidden. Return false to stop the hide.
7385              * @param {Roo.Component} this
7386              */
7387         beforehide : true,
7388         /**
7389          * @event hide
7390          * Fires after the component is hidden.
7391              * @param {Roo.Component} this
7392              */
7393         hide : true,
7394         /**
7395          * @event beforerender
7396          * Fires before the component is rendered. Return false to stop the render.
7397              * @param {Roo.Component} this
7398              */
7399         beforerender : true,
7400         /**
7401          * @event render
7402          * Fires after the component is rendered.
7403              * @param {Roo.Component} this
7404              */
7405         render : true,
7406         /**
7407          * @event beforedestroy
7408          * Fires before the component is destroyed. Return false to stop the destroy.
7409              * @param {Roo.Component} this
7410              */
7411         beforedestroy : true,
7412         /**
7413          * @event destroy
7414          * Fires after the component is destroyed.
7415              * @param {Roo.Component} this
7416              */
7417         destroy : true
7418     });
7419     if(!this.id){
7420         this.id = "ext-comp-" + (++Roo.Component.AUTO_ID);
7421     }
7422     Roo.ComponentMgr.register(this);
7423     Roo.Component.superclass.constructor.call(this);
7424     this.initComponent();
7425     if(this.renderTo){ // not supported by all components yet. use at your own risk!
7426         this.render(this.renderTo);
7427         delete this.renderTo;
7428     }
7429 };
7430
7431 /** @private */
7432 Roo.Component.AUTO_ID = 1000;
7433
7434 Roo.extend(Roo.Component, Roo.util.Observable, {
7435     /**
7436      * @scope Roo.Component.prototype
7437      * @type {Boolean}
7438      * true if this component is hidden. Read-only.
7439      */
7440     hidden : false,
7441     /**
7442      * @type {Boolean}
7443      * true if this component is disabled. Read-only.
7444      */
7445     disabled : false,
7446     /**
7447      * @type {Boolean}
7448      * true if this component has been rendered. Read-only.
7449      */
7450     rendered : false,
7451     
7452     /** @cfg {String} disableClass
7453      * CSS class added to the component when it is disabled (defaults to "x-item-disabled").
7454      */
7455     disabledClass : "x-item-disabled",
7456         /** @cfg {Boolean} allowDomMove
7457          * Whether the component can move the Dom node when rendering (defaults to true).
7458          */
7459     allowDomMove : true,
7460     /** @cfg {String} hideMode
7461      * How this component should hidden. Supported values are
7462      * "visibility" (css visibility), "offsets" (negative offset position) and
7463      * "display" (css display) - defaults to "display".
7464      */
7465     hideMode: 'display',
7466
7467     /** @private */
7468     ctype : "Roo.Component",
7469
7470     /**
7471      * @cfg {String} actionMode 
7472      * which property holds the element that used for  hide() / show() / disable() / enable()
7473      * default is 'el' 
7474      */
7475     actionMode : "el",
7476
7477     /** @private */
7478     getActionEl : function(){
7479         return this[this.actionMode];
7480     },
7481
7482     initComponent : Roo.emptyFn,
7483     /**
7484      * If this is a lazy rendering component, render it to its container element.
7485      * @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.
7486      */
7487     render : function(container, position){
7488         if(!this.rendered && this.fireEvent("beforerender", this) !== false){
7489             if(!container && this.el){
7490                 this.el = Roo.get(this.el);
7491                 container = this.el.dom.parentNode;
7492                 this.allowDomMove = false;
7493             }
7494             this.container = Roo.get(container);
7495             this.rendered = true;
7496             if(position !== undefined){
7497                 if(typeof position == 'number'){
7498                     position = this.container.dom.childNodes[position];
7499                 }else{
7500                     position = Roo.getDom(position);
7501                 }
7502             }
7503             this.onRender(this.container, position || null);
7504             if(this.cls){
7505                 this.el.addClass(this.cls);
7506                 delete this.cls;
7507             }
7508             if(this.style){
7509                 this.el.applyStyles(this.style);
7510                 delete this.style;
7511             }
7512             this.fireEvent("render", this);
7513             this.afterRender(this.container);
7514             if(this.hidden){
7515                 this.hide();
7516             }
7517             if(this.disabled){
7518                 this.disable();
7519             }
7520         }
7521         return this;
7522     },
7523
7524     /** @private */
7525     // default function is not really useful
7526     onRender : function(ct, position){
7527         if(this.el){
7528             this.el = Roo.get(this.el);
7529             if(this.allowDomMove !== false){
7530                 ct.dom.insertBefore(this.el.dom, position);
7531             }
7532         }
7533     },
7534
7535     /** @private */
7536     getAutoCreate : function(){
7537         var cfg = typeof this.autoCreate == "object" ?
7538                       this.autoCreate : Roo.apply({}, this.defaultAutoCreate);
7539         if(this.id && !cfg.id){
7540             cfg.id = this.id;
7541         }
7542         return cfg;
7543     },
7544
7545     /** @private */
7546     afterRender : Roo.emptyFn,
7547
7548     /**
7549      * Destroys this component by purging any event listeners, removing the component's element from the DOM,
7550      * removing the component from its {@link Roo.Container} (if applicable) and unregistering it from {@link Roo.ComponentMgr}.
7551      */
7552     destroy : function(){
7553         if(this.fireEvent("beforedestroy", this) !== false){
7554             this.purgeListeners();
7555             this.beforeDestroy();
7556             if(this.rendered){
7557                 this.el.removeAllListeners();
7558                 this.el.remove();
7559                 if(this.actionMode == "container"){
7560                     this.container.remove();
7561                 }
7562             }
7563             this.onDestroy();
7564             Roo.ComponentMgr.unregister(this);
7565             this.fireEvent("destroy", this);
7566         }
7567     },
7568
7569         /** @private */
7570     beforeDestroy : function(){
7571
7572     },
7573
7574         /** @private */
7575         onDestroy : function(){
7576
7577     },
7578
7579     /**
7580      * Returns the underlying {@link Roo.Element}.
7581      * @return {Roo.Element} The element
7582      */
7583     getEl : function(){
7584         return this.el;
7585     },
7586
7587     /**
7588      * Returns the id of this component.
7589      * @return {String}
7590      */
7591     getId : function(){
7592         return this.id;
7593     },
7594
7595     /**
7596      * Try to focus this component.
7597      * @param {Boolean} selectText True to also select the text in this component (if applicable)
7598      * @return {Roo.Component} this
7599      */
7600     focus : function(selectText){
7601         if(this.rendered){
7602             this.el.focus();
7603             if(selectText === true){
7604                 this.el.dom.select();
7605             }
7606         }
7607         return this;
7608     },
7609
7610     /** @private */
7611     blur : function(){
7612         if(this.rendered){
7613             this.el.blur();
7614         }
7615         return this;
7616     },
7617
7618     /**
7619      * Disable this component.
7620      * @return {Roo.Component} this
7621      */
7622     disable : function(){
7623         if(this.rendered){
7624             this.onDisable();
7625         }
7626         this.disabled = true;
7627         this.fireEvent("disable", this);
7628         return this;
7629     },
7630
7631         // private
7632     onDisable : function(){
7633         this.getActionEl().addClass(this.disabledClass);
7634         this.el.dom.disabled = true;
7635     },
7636
7637     /**
7638      * Enable this component.
7639      * @return {Roo.Component} this
7640      */
7641     enable : function(){
7642         if(this.rendered){
7643             this.onEnable();
7644         }
7645         this.disabled = false;
7646         this.fireEvent("enable", this);
7647         return this;
7648     },
7649
7650         // private
7651     onEnable : function(){
7652         this.getActionEl().removeClass(this.disabledClass);
7653         this.el.dom.disabled = false;
7654     },
7655
7656     /**
7657      * Convenience function for setting disabled/enabled by boolean.
7658      * @param {Boolean} disabled
7659      */
7660     setDisabled : function(disabled){
7661         this[disabled ? "disable" : "enable"]();
7662     },
7663
7664     /**
7665      * Show this component.
7666      * @return {Roo.Component} this
7667      */
7668     show: function(){
7669         if(this.fireEvent("beforeshow", this) !== false){
7670             this.hidden = false;
7671             if(this.rendered){
7672                 this.onShow();
7673             }
7674             this.fireEvent("show", this);
7675         }
7676         return this;
7677     },
7678
7679     // private
7680     onShow : function(){
7681         var ae = this.getActionEl();
7682         if(this.hideMode == 'visibility'){
7683             ae.dom.style.visibility = "visible";
7684         }else if(this.hideMode == 'offsets'){
7685             ae.removeClass('x-hidden');
7686         }else{
7687             ae.dom.style.display = "";
7688         }
7689     },
7690
7691     /**
7692      * Hide this component.
7693      * @return {Roo.Component} this
7694      */
7695     hide: function(){
7696         if(this.fireEvent("beforehide", this) !== false){
7697             this.hidden = true;
7698             if(this.rendered){
7699                 this.onHide();
7700             }
7701             this.fireEvent("hide", this);
7702         }
7703         return this;
7704     },
7705
7706     // private
7707     onHide : function(){
7708         var ae = this.getActionEl();
7709         if(this.hideMode == 'visibility'){
7710             ae.dom.style.visibility = "hidden";
7711         }else if(this.hideMode == 'offsets'){
7712             ae.addClass('x-hidden');
7713         }else{
7714             ae.dom.style.display = "none";
7715         }
7716     },
7717
7718     /**
7719      * Convenience function to hide or show this component by boolean.
7720      * @param {Boolean} visible True to show, false to hide
7721      * @return {Roo.Component} this
7722      */
7723     setVisible: function(visible){
7724         if(visible) {
7725             this.show();
7726         }else{
7727             this.hide();
7728         }
7729         return this;
7730     },
7731
7732     /**
7733      * Returns true if this component is visible.
7734      */
7735     isVisible : function(){
7736         return this.getActionEl().isVisible();
7737     },
7738
7739     cloneConfig : function(overrides){
7740         overrides = overrides || {};
7741         var id = overrides.id || Roo.id();
7742         var cfg = Roo.applyIf(overrides, this.initialConfig);
7743         cfg.id = id; // prevent dup id
7744         return new this.constructor(cfg);
7745     }
7746 });/*
7747  * Based on:
7748  * Ext JS Library 1.1.1
7749  * Copyright(c) 2006-2007, Ext JS, LLC.
7750  *
7751  * Originally Released Under LGPL - original licence link has changed is not relivant.
7752  *
7753  * Fork - LGPL
7754  * <script type="text/javascript">
7755  */
7756  (function(){ 
7757 /**
7758  * @class Roo.Layer
7759  * @extends Roo.Element
7760  * An extended {@link Roo.Element} object that supports a shadow and shim, constrain to viewport and
7761  * automatic maintaining of shadow/shim positions.
7762  * @cfg {Boolean} shim False to disable the iframe shim in browsers which need one (defaults to true)
7763  * @cfg {String/Boolean} shadow True to create a shadow element with default class "x-layer-shadow", or
7764  * you can pass a string with a CSS class name. False turns off the shadow.
7765  * @cfg {Object} dh DomHelper object config to create element with (defaults to {tag: "div", cls: "x-layer"}).
7766  * @cfg {Boolean} constrain False to disable constrain to viewport (defaults to true)
7767  * @cfg {String} cls CSS class to add to the element
7768  * @cfg {Number} zindex Starting z-index (defaults to 11000)
7769  * @cfg {Number} shadowOffset Number of pixels to offset the shadow (defaults to 3)
7770  * @constructor
7771  * @param {Object} config An object with config options.
7772  * @param {String/HTMLElement} existingEl (optional) Uses an existing DOM element. If the element is not found it creates it.
7773  */
7774
7775 Roo.Layer = function(config, existingEl){
7776     config = config || {};
7777     var dh = Roo.DomHelper;
7778     var cp = config.parentEl, pel = cp ? Roo.getDom(cp) : document.body;
7779     if(existingEl){
7780         this.dom = Roo.getDom(existingEl);
7781     }
7782     if(!this.dom){
7783         var o = config.dh || {tag: "div", cls: "x-layer"};
7784         this.dom = dh.append(pel, o);
7785     }
7786     if(config.cls){
7787         this.addClass(config.cls);
7788     }
7789     this.constrain = config.constrain !== false;
7790     this.visibilityMode = Roo.Element.VISIBILITY;
7791     if(config.id){
7792         this.id = this.dom.id = config.id;
7793     }else{
7794         this.id = Roo.id(this.dom);
7795     }
7796     this.zindex = config.zindex || this.getZIndex();
7797     this.position("absolute", this.zindex);
7798     if(config.shadow){
7799         this.shadowOffset = config.shadowOffset || 4;
7800         this.shadow = new Roo.Shadow({
7801             offset : this.shadowOffset,
7802             mode : config.shadow
7803         });
7804     }else{
7805         this.shadowOffset = 0;
7806     }
7807     this.useShim = config.shim !== false && Roo.useShims;
7808     this.useDisplay = config.useDisplay;
7809     this.hide();
7810 };
7811
7812 var supr = Roo.Element.prototype;
7813
7814 // shims are shared among layer to keep from having 100 iframes
7815 var shims = [];
7816
7817 Roo.extend(Roo.Layer, Roo.Element, {
7818
7819     getZIndex : function(){
7820         return this.zindex || parseInt(this.getStyle("z-index"), 10) || 11000;
7821     },
7822
7823     getShim : function(){
7824         if(!this.useShim){
7825             return null;
7826         }
7827         if(this.shim){
7828             return this.shim;
7829         }
7830         var shim = shims.shift();
7831         if(!shim){
7832             shim = this.createShim();
7833             shim.enableDisplayMode('block');
7834             shim.dom.style.display = 'none';
7835             shim.dom.style.visibility = 'visible';
7836         }
7837         var pn = this.dom.parentNode;
7838         if(shim.dom.parentNode != pn){
7839             pn.insertBefore(shim.dom, this.dom);
7840         }
7841         shim.setStyle('z-index', this.getZIndex()-2);
7842         this.shim = shim;
7843         return shim;
7844     },
7845
7846     hideShim : function(){
7847         if(this.shim){
7848             this.shim.setDisplayed(false);
7849             shims.push(this.shim);
7850             delete this.shim;
7851         }
7852     },
7853
7854     disableShadow : function(){
7855         if(this.shadow){
7856             this.shadowDisabled = true;
7857             this.shadow.hide();
7858             this.lastShadowOffset = this.shadowOffset;
7859             this.shadowOffset = 0;
7860         }
7861     },
7862
7863     enableShadow : function(show){
7864         if(this.shadow){
7865             this.shadowDisabled = false;
7866             this.shadowOffset = this.lastShadowOffset;
7867             delete this.lastShadowOffset;
7868             if(show){
7869                 this.sync(true);
7870             }
7871         }
7872     },
7873
7874     // private
7875     // this code can execute repeatedly in milliseconds (i.e. during a drag) so
7876     // code size was sacrificed for effeciency (e.g. no getBox/setBox, no XY calls)
7877     sync : function(doShow){
7878         var sw = this.shadow;
7879         if(!this.updating && this.isVisible() && (sw || this.useShim)){
7880             var sh = this.getShim();
7881
7882             var w = this.getWidth(),
7883                 h = this.getHeight();
7884
7885             var l = this.getLeft(true),
7886                 t = this.getTop(true);
7887
7888             if(sw && !this.shadowDisabled){
7889                 if(doShow && !sw.isVisible()){
7890                     sw.show(this);
7891                 }else{
7892                     sw.realign(l, t, w, h);
7893                 }
7894                 if(sh){
7895                     if(doShow){
7896                        sh.show();
7897                     }
7898                     // fit the shim behind the shadow, so it is shimmed too
7899                     var a = sw.adjusts, s = sh.dom.style;
7900                     s.left = (Math.min(l, l+a.l))+"px";
7901                     s.top = (Math.min(t, t+a.t))+"px";
7902                     s.width = (w+a.w)+"px";
7903                     s.height = (h+a.h)+"px";
7904                 }
7905             }else if(sh){
7906                 if(doShow){
7907                    sh.show();
7908                 }
7909                 sh.setSize(w, h);
7910                 sh.setLeftTop(l, t);
7911             }
7912             
7913         }
7914     },
7915
7916     // private
7917     destroy : function(){
7918         this.hideShim();
7919         if(this.shadow){
7920             this.shadow.hide();
7921         }
7922         this.removeAllListeners();
7923         var pn = this.dom.parentNode;
7924         if(pn){
7925             pn.removeChild(this.dom);
7926         }
7927         Roo.Element.uncache(this.id);
7928     },
7929
7930     remove : function(){
7931         this.destroy();
7932     },
7933
7934     // private
7935     beginUpdate : function(){
7936         this.updating = true;
7937     },
7938
7939     // private
7940     endUpdate : function(){
7941         this.updating = false;
7942         this.sync(true);
7943     },
7944
7945     // private
7946     hideUnders : function(negOffset){
7947         if(this.shadow){
7948             this.shadow.hide();
7949         }
7950         this.hideShim();
7951     },
7952
7953     // private
7954     constrainXY : function(){
7955         if(this.constrain){
7956             var vw = Roo.lib.Dom.getViewWidth(),
7957                 vh = Roo.lib.Dom.getViewHeight();
7958             var s = Roo.get(document).getScroll();
7959
7960             var xy = this.getXY();
7961             var x = xy[0], y = xy[1];   
7962             var w = this.dom.offsetWidth+this.shadowOffset, h = this.dom.offsetHeight+this.shadowOffset;
7963             // only move it if it needs it
7964             var moved = false;
7965             // first validate right/bottom
7966             if((x + w) > vw+s.left){
7967                 x = vw - w - this.shadowOffset;
7968                 moved = true;
7969             }
7970             if((y + h) > vh+s.top){
7971                 y = vh - h - this.shadowOffset;
7972                 moved = true;
7973             }
7974             // then make sure top/left isn't negative
7975             if(x < s.left){
7976                 x = s.left;
7977                 moved = true;
7978             }
7979             if(y < s.top){
7980                 y = s.top;
7981                 moved = true;
7982             }
7983             if(moved){
7984                 if(this.avoidY){
7985                     var ay = this.avoidY;
7986                     if(y <= ay && (y+h) >= ay){
7987                         y = ay-h-5;   
7988                     }
7989                 }
7990                 xy = [x, y];
7991                 this.storeXY(xy);
7992                 supr.setXY.call(this, xy);
7993                 this.sync();
7994             }
7995         }
7996     },
7997
7998     isVisible : function(){
7999         return this.visible;    
8000     },
8001
8002     // private
8003     showAction : function(){
8004         this.visible = true; // track visibility to prevent getStyle calls
8005         if(this.useDisplay === true){
8006             this.setDisplayed("");
8007         }else if(this.lastXY){
8008             supr.setXY.call(this, this.lastXY);
8009         }else if(this.lastLT){
8010             supr.setLeftTop.call(this, this.lastLT[0], this.lastLT[1]);
8011         }
8012     },
8013
8014     // private
8015     hideAction : function(){
8016         this.visible = false;
8017         if(this.useDisplay === true){
8018             this.setDisplayed(false);
8019         }else{
8020             this.setLeftTop(-10000,-10000);
8021         }
8022     },
8023
8024     // overridden Element method
8025     setVisible : function(v, a, d, c, e){
8026         if(v){
8027             this.showAction();
8028         }
8029         if(a && v){
8030             var cb = function(){
8031                 this.sync(true);
8032                 if(c){
8033                     c();
8034                 }
8035             }.createDelegate(this);
8036             supr.setVisible.call(this, true, true, d, cb, e);
8037         }else{
8038             if(!v){
8039                 this.hideUnders(true);
8040             }
8041             var cb = c;
8042             if(a){
8043                 cb = function(){
8044                     this.hideAction();
8045                     if(c){
8046                         c();
8047                     }
8048                 }.createDelegate(this);
8049             }
8050             supr.setVisible.call(this, v, a, d, cb, e);
8051             if(v){
8052                 this.sync(true);
8053             }else if(!a){
8054                 this.hideAction();
8055             }
8056         }
8057     },
8058
8059     storeXY : function(xy){
8060         delete this.lastLT;
8061         this.lastXY = xy;
8062     },
8063
8064     storeLeftTop : function(left, top){
8065         delete this.lastXY;
8066         this.lastLT = [left, top];
8067     },
8068
8069     // private
8070     beforeFx : function(){
8071         this.beforeAction();
8072         return Roo.Layer.superclass.beforeFx.apply(this, arguments);
8073     },
8074
8075     // private
8076     afterFx : function(){
8077         Roo.Layer.superclass.afterFx.apply(this, arguments);
8078         this.sync(this.isVisible());
8079     },
8080
8081     // private
8082     beforeAction : function(){
8083         if(!this.updating && this.shadow){
8084             this.shadow.hide();
8085         }
8086     },
8087
8088     // overridden Element method
8089     setLeft : function(left){
8090         this.storeLeftTop(left, this.getTop(true));
8091         supr.setLeft.apply(this, arguments);
8092         this.sync();
8093     },
8094
8095     setTop : function(top){
8096         this.storeLeftTop(this.getLeft(true), top);
8097         supr.setTop.apply(this, arguments);
8098         this.sync();
8099     },
8100
8101     setLeftTop : function(left, top){
8102         this.storeLeftTop(left, top);
8103         supr.setLeftTop.apply(this, arguments);
8104         this.sync();
8105     },
8106
8107     setXY : function(xy, a, d, c, e){
8108         this.fixDisplay();
8109         this.beforeAction();
8110         this.storeXY(xy);
8111         var cb = this.createCB(c);
8112         supr.setXY.call(this, xy, a, d, cb, e);
8113         if(!a){
8114             cb();
8115         }
8116     },
8117
8118     // private
8119     createCB : function(c){
8120         var el = this;
8121         return function(){
8122             el.constrainXY();
8123             el.sync(true);
8124             if(c){
8125                 c();
8126             }
8127         };
8128     },
8129
8130     // overridden Element method
8131     setX : function(x, a, d, c, e){
8132         this.setXY([x, this.getY()], a, d, c, e);
8133     },
8134
8135     // overridden Element method
8136     setY : function(y, a, d, c, e){
8137         this.setXY([this.getX(), y], a, d, c, e);
8138     },
8139
8140     // overridden Element method
8141     setSize : function(w, h, a, d, c, e){
8142         this.beforeAction();
8143         var cb = this.createCB(c);
8144         supr.setSize.call(this, w, h, a, d, cb, e);
8145         if(!a){
8146             cb();
8147         }
8148     },
8149
8150     // overridden Element method
8151     setWidth : function(w, a, d, c, e){
8152         this.beforeAction();
8153         var cb = this.createCB(c);
8154         supr.setWidth.call(this, w, a, d, cb, e);
8155         if(!a){
8156             cb();
8157         }
8158     },
8159
8160     // overridden Element method
8161     setHeight : function(h, a, d, c, e){
8162         this.beforeAction();
8163         var cb = this.createCB(c);
8164         supr.setHeight.call(this, h, a, d, cb, e);
8165         if(!a){
8166             cb();
8167         }
8168     },
8169
8170     // overridden Element method
8171     setBounds : function(x, y, w, h, a, d, c, e){
8172         this.beforeAction();
8173         var cb = this.createCB(c);
8174         if(!a){
8175             this.storeXY([x, y]);
8176             supr.setXY.call(this, [x, y]);
8177             supr.setSize.call(this, w, h, a, d, cb, e);
8178             cb();
8179         }else{
8180             supr.setBounds.call(this, x, y, w, h, a, d, cb, e);
8181         }
8182         return this;
8183     },
8184     
8185     /**
8186      * Sets the z-index of this layer and adjusts any shadow and shim z-indexes. The layer z-index is automatically
8187      * incremented by two more than the value passed in so that it always shows above any shadow or shim (the shadow
8188      * element, if any, will be assigned z-index + 1, and the shim element, if any, will be assigned the unmodified z-index).
8189      * @param {Number} zindex The new z-index to set
8190      * @return {this} The Layer
8191      */
8192     setZIndex : function(zindex){
8193         this.zindex = zindex;
8194         this.setStyle("z-index", zindex + 2);
8195         if(this.shadow){
8196             this.shadow.setZIndex(zindex + 1);
8197         }
8198         if(this.shim){
8199             this.shim.setStyle("z-index", zindex);
8200         }
8201     }
8202 });
8203 })();/*
8204  * Based on:
8205  * Ext JS Library 1.1.1
8206  * Copyright(c) 2006-2007, Ext JS, LLC.
8207  *
8208  * Originally Released Under LGPL - original licence link has changed is not relivant.
8209  *
8210  * Fork - LGPL
8211  * <script type="text/javascript">
8212  */
8213
8214
8215 /**
8216  * @class Roo.Shadow
8217  * Simple class that can provide a shadow effect for any element.  Note that the element MUST be absolutely positioned,
8218  * and the shadow does not provide any shimming.  This should be used only in simple cases -- for more advanced
8219  * functionality that can also provide the same shadow effect, see the {@link Roo.Layer} class.
8220  * @constructor
8221  * Create a new Shadow
8222  * @param {Object} config The config object
8223  */
8224 Roo.Shadow = function(config){
8225     Roo.apply(this, config);
8226     if(typeof this.mode != "string"){
8227         this.mode = this.defaultMode;
8228     }
8229     var o = this.offset, a = {h: 0};
8230     var rad = Math.floor(this.offset/2);
8231     switch(this.mode.toLowerCase()){ // all this hideous nonsense calculates the various offsets for shadows
8232         case "drop":
8233             a.w = 0;
8234             a.l = a.t = o;
8235             a.t -= 1;
8236             if(Roo.isIE){
8237                 a.l -= this.offset + rad;
8238                 a.t -= this.offset + rad;
8239                 a.w -= rad;
8240                 a.h -= rad;
8241                 a.t += 1;
8242             }
8243         break;
8244         case "sides":
8245             a.w = (o*2);
8246             a.l = -o;
8247             a.t = o-1;
8248             if(Roo.isIE){
8249                 a.l -= (this.offset - rad);
8250                 a.t -= this.offset + rad;
8251                 a.l += 1;
8252                 a.w -= (this.offset - rad)*2;
8253                 a.w -= rad + 1;
8254                 a.h -= 1;
8255             }
8256         break;
8257         case "frame":
8258             a.w = a.h = (o*2);
8259             a.l = a.t = -o;
8260             a.t += 1;
8261             a.h -= 2;
8262             if(Roo.isIE){
8263                 a.l -= (this.offset - rad);
8264                 a.t -= (this.offset - rad);
8265                 a.l += 1;
8266                 a.w -= (this.offset + rad + 1);
8267                 a.h -= (this.offset + rad);
8268                 a.h += 1;
8269             }
8270         break;
8271     };
8272
8273     this.adjusts = a;
8274 };
8275
8276 Roo.Shadow.prototype = {
8277     /**
8278      * @cfg {String} mode
8279      * The shadow display mode.  Supports the following options:<br />
8280      * sides: Shadow displays on both sides and bottom only<br />
8281      * frame: Shadow displays equally on all four sides<br />
8282      * drop: Traditional bottom-right drop shadow (default)
8283      */
8284     /**
8285      * @cfg {String} offset
8286      * The number of pixels to offset the shadow from the element (defaults to 4)
8287      */
8288     offset: 4,
8289
8290     // private
8291     defaultMode: "drop",
8292
8293     /**
8294      * Displays the shadow under the target element
8295      * @param {String/HTMLElement/Element} targetEl The id or element under which the shadow should display
8296      */
8297     show : function(target){
8298         target = Roo.get(target);
8299         if(!this.el){
8300             this.el = Roo.Shadow.Pool.pull();
8301             if(this.el.dom.nextSibling != target.dom){
8302                 this.el.insertBefore(target);
8303             }
8304         }
8305         this.el.setStyle("z-index", this.zIndex || parseInt(target.getStyle("z-index"), 10)-1);
8306         if(Roo.isIE){
8307             this.el.dom.style.filter="progid:DXImageTransform.Microsoft.alpha(opacity=50) progid:DXImageTransform.Microsoft.Blur(pixelradius="+(this.offset)+")";
8308         }
8309         this.realign(
8310             target.getLeft(true),
8311             target.getTop(true),
8312             target.getWidth(),
8313             target.getHeight()
8314         );
8315         this.el.dom.style.display = "block";
8316     },
8317
8318     /**
8319      * Returns true if the shadow is visible, else false
8320      */
8321     isVisible : function(){
8322         return this.el ? true : false;  
8323     },
8324
8325     /**
8326      * Direct alignment when values are already available. Show must be called at least once before
8327      * calling this method to ensure it is initialized.
8328      * @param {Number} left The target element left position
8329      * @param {Number} top The target element top position
8330      * @param {Number} width The target element width
8331      * @param {Number} height The target element height
8332      */
8333     realign : function(l, t, w, h){
8334         if(!this.el){
8335             return;
8336         }
8337         var a = this.adjusts, d = this.el.dom, s = d.style;
8338         var iea = 0;
8339         s.left = (l+a.l)+"px";
8340         s.top = (t+a.t)+"px";
8341         var sw = (w+a.w), sh = (h+a.h), sws = sw +"px", shs = sh + "px";
8342  
8343         if(s.width != sws || s.height != shs){
8344             s.width = sws;
8345             s.height = shs;
8346             if(!Roo.isIE){
8347                 var cn = d.childNodes;
8348                 var sww = Math.max(0, (sw-12))+"px";
8349                 cn[0].childNodes[1].style.width = sww;
8350                 cn[1].childNodes[1].style.width = sww;
8351                 cn[2].childNodes[1].style.width = sww;
8352                 cn[1].style.height = Math.max(0, (sh-12))+"px";
8353             }
8354         }
8355     },
8356
8357     /**
8358      * Hides this shadow
8359      */
8360     hide : function(){
8361         if(this.el){
8362             this.el.dom.style.display = "none";
8363             Roo.Shadow.Pool.push(this.el);
8364             delete this.el;
8365         }
8366     },
8367
8368     /**
8369      * Adjust the z-index of this shadow
8370      * @param {Number} zindex The new z-index
8371      */
8372     setZIndex : function(z){
8373         this.zIndex = z;
8374         if(this.el){
8375             this.el.setStyle("z-index", z);
8376         }
8377     }
8378 };
8379
8380 // Private utility class that manages the internal Shadow cache
8381 Roo.Shadow.Pool = function(){
8382     var p = [];
8383     var markup = Roo.isIE ?
8384                  '<div class="x-ie-shadow"></div>' :
8385                  '<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>';
8386     return {
8387         pull : function(){
8388             var sh = p.shift();
8389             if(!sh){
8390                 sh = Roo.get(Roo.DomHelper.insertHtml("beforeBegin", document.body.firstChild, markup));
8391                 sh.autoBoxAdjust = false;
8392             }
8393             return sh;
8394         },
8395
8396         push : function(sh){
8397             p.push(sh);
8398         }
8399     };
8400 }();/*
8401  * Based on:
8402  * Ext JS Library 1.1.1
8403  * Copyright(c) 2006-2007, Ext JS, LLC.
8404  *
8405  * Originally Released Under LGPL - original licence link has changed is not relivant.
8406  *
8407  * Fork - LGPL
8408  * <script type="text/javascript">
8409  */
8410
8411 /**
8412  * @class Roo.BoxComponent
8413  * @extends Roo.Component
8414  * Base class for any visual {@link Roo.Component} that uses a box container.  BoxComponent provides automatic box
8415  * model adjustments for sizing and positioning and will work correctly withnin the Component rendering model.  All
8416  * container classes should subclass BoxComponent so that they will work consistently when nested within other Ext
8417  * layout containers.
8418  * @constructor
8419  * @param {Roo.Element/String/Object} config The configuration options.
8420  */
8421 Roo.BoxComponent = function(config){
8422     Roo.Component.call(this, config);
8423     this.addEvents({
8424         /**
8425          * @event resize
8426          * Fires after the component is resized.
8427              * @param {Roo.Component} this
8428              * @param {Number} adjWidth The box-adjusted width that was set
8429              * @param {Number} adjHeight The box-adjusted height that was set
8430              * @param {Number} rawWidth The width that was originally specified
8431              * @param {Number} rawHeight The height that was originally specified
8432              */
8433         resize : true,
8434         /**
8435          * @event move
8436          * Fires after the component is moved.
8437              * @param {Roo.Component} this
8438              * @param {Number} x The new x position
8439              * @param {Number} y The new y position
8440              */
8441         move : true
8442     });
8443 };
8444
8445 Roo.extend(Roo.BoxComponent, Roo.Component, {
8446     // private, set in afterRender to signify that the component has been rendered
8447     boxReady : false,
8448     // private, used to defer height settings to subclasses
8449     deferHeight: false,
8450     /** @cfg {Number} width
8451      * width (optional) size of component
8452      */
8453      /** @cfg {Number} height
8454      * height (optional) size of component
8455      */
8456      
8457     /**
8458      * Sets the width and height of the component.  This method fires the resize event.  This method can accept
8459      * either width and height as separate numeric arguments, or you can pass a size object like {width:10, height:20}.
8460      * @param {Number/Object} width The new width to set, or a size object in the format {width, height}
8461      * @param {Number} height The new height to set (not required if a size object is passed as the first arg)
8462      * @return {Roo.BoxComponent} this
8463      */
8464     setSize : function(w, h){
8465         // support for standard size objects
8466         if(typeof w == 'object'){
8467             h = w.height;
8468             w = w.width;
8469         }
8470         // not rendered
8471         if(!this.boxReady){
8472             this.width = w;
8473             this.height = h;
8474             return this;
8475         }
8476
8477         // prevent recalcs when not needed
8478         if(this.lastSize && this.lastSize.width == w && this.lastSize.height == h){
8479             return this;
8480         }
8481         this.lastSize = {width: w, height: h};
8482
8483         var adj = this.adjustSize(w, h);
8484         var aw = adj.width, ah = adj.height;
8485         if(aw !== undefined || ah !== undefined){ // this code is nasty but performs better with floaters
8486             var rz = this.getResizeEl();
8487             if(!this.deferHeight && aw !== undefined && ah !== undefined){
8488                 rz.setSize(aw, ah);
8489             }else if(!this.deferHeight && ah !== undefined){
8490                 rz.setHeight(ah);
8491             }else if(aw !== undefined){
8492                 rz.setWidth(aw);
8493             }
8494             this.onResize(aw, ah, w, h);
8495             this.fireEvent('resize', this, aw, ah, w, h);
8496         }
8497         return this;
8498     },
8499
8500     /**
8501      * Gets the current size of the component's underlying element.
8502      * @return {Object} An object containing the element's size {width: (element width), height: (element height)}
8503      */
8504     getSize : function(){
8505         return this.el.getSize();
8506     },
8507
8508     /**
8509      * Gets the current XY position of the component's underlying element.
8510      * @param {Boolean} local (optional) If true the element's left and top are returned instead of page XY (defaults to false)
8511      * @return {Array} The XY position of the element (e.g., [100, 200])
8512      */
8513     getPosition : function(local){
8514         if(local === true){
8515             return [this.el.getLeft(true), this.el.getTop(true)];
8516         }
8517         return this.xy || this.el.getXY();
8518     },
8519
8520     /**
8521      * Gets the current box measurements of the component's underlying element.
8522      * @param {Boolean} local (optional) If true the element's left and top are returned instead of page XY (defaults to false)
8523      * @returns {Object} box An object in the format {x, y, width, height}
8524      */
8525     getBox : function(local){
8526         var s = this.el.getSize();
8527         if(local){
8528             s.x = this.el.getLeft(true);
8529             s.y = this.el.getTop(true);
8530         }else{
8531             var xy = this.xy || this.el.getXY();
8532             s.x = xy[0];
8533             s.y = xy[1];
8534         }
8535         return s;
8536     },
8537
8538     /**
8539      * Sets the current box measurements of the component's underlying element.
8540      * @param {Object} box An object in the format {x, y, width, height}
8541      * @returns {Roo.BoxComponent} this
8542      */
8543     updateBox : function(box){
8544         this.setSize(box.width, box.height);
8545         this.setPagePosition(box.x, box.y);
8546         return this;
8547     },
8548
8549     // protected
8550     getResizeEl : function(){
8551         return this.resizeEl || this.el;
8552     },
8553
8554     // protected
8555     getPositionEl : function(){
8556         return this.positionEl || this.el;
8557     },
8558
8559     /**
8560      * Sets the left and top of the component.  To set the page XY position instead, use {@link #setPagePosition}.
8561      * This method fires the move event.
8562      * @param {Number} left The new left
8563      * @param {Number} top The new top
8564      * @returns {Roo.BoxComponent} this
8565      */
8566     setPosition : function(x, y){
8567         this.x = x;
8568         this.y = y;
8569         if(!this.boxReady){
8570             return this;
8571         }
8572         var adj = this.adjustPosition(x, y);
8573         var ax = adj.x, ay = adj.y;
8574
8575         var el = this.getPositionEl();
8576         if(ax !== undefined || ay !== undefined){
8577             if(ax !== undefined && ay !== undefined){
8578                 el.setLeftTop(ax, ay);
8579             }else if(ax !== undefined){
8580                 el.setLeft(ax);
8581             }else if(ay !== undefined){
8582                 el.setTop(ay);
8583             }
8584             this.onPosition(ax, ay);
8585             this.fireEvent('move', this, ax, ay);
8586         }
8587         return this;
8588     },
8589
8590     /**
8591      * Sets the page XY position of the component.  To set the left and top instead, use {@link #setPosition}.
8592      * This method fires the move event.
8593      * @param {Number} x The new x position
8594      * @param {Number} y The new y position
8595      * @returns {Roo.BoxComponent} this
8596      */
8597     setPagePosition : function(x, y){
8598         this.pageX = x;
8599         this.pageY = y;
8600         if(!this.boxReady){
8601             return;
8602         }
8603         if(x === undefined || y === undefined){ // cannot translate undefined points
8604             return;
8605         }
8606         var p = this.el.translatePoints(x, y);
8607         this.setPosition(p.left, p.top);
8608         return this;
8609     },
8610
8611     // private
8612     onRender : function(ct, position){
8613         Roo.BoxComponent.superclass.onRender.call(this, ct, position);
8614         if(this.resizeEl){
8615             this.resizeEl = Roo.get(this.resizeEl);
8616         }
8617         if(this.positionEl){
8618             this.positionEl = Roo.get(this.positionEl);
8619         }
8620     },
8621
8622     // private
8623     afterRender : function(){
8624         Roo.BoxComponent.superclass.afterRender.call(this);
8625         this.boxReady = true;
8626         this.setSize(this.width, this.height);
8627         if(this.x || this.y){
8628             this.setPosition(this.x, this.y);
8629         }
8630         if(this.pageX || this.pageY){
8631             this.setPagePosition(this.pageX, this.pageY);
8632         }
8633     },
8634
8635     /**
8636      * Force the component's size to recalculate based on the underlying element's current height and width.
8637      * @returns {Roo.BoxComponent} this
8638      */
8639     syncSize : function(){
8640         delete this.lastSize;
8641         this.setSize(this.el.getWidth(), this.el.getHeight());
8642         return this;
8643     },
8644
8645     /**
8646      * Called after the component is resized, this method is empty by default but can be implemented by any
8647      * subclass that needs to perform custom logic after a resize occurs.
8648      * @param {Number} adjWidth The box-adjusted width that was set
8649      * @param {Number} adjHeight The box-adjusted height that was set
8650      * @param {Number} rawWidth The width that was originally specified
8651      * @param {Number} rawHeight The height that was originally specified
8652      */
8653     onResize : function(adjWidth, adjHeight, rawWidth, rawHeight){
8654
8655     },
8656
8657     /**
8658      * Called after the component is moved, this method is empty by default but can be implemented by any
8659      * subclass that needs to perform custom logic after a move occurs.
8660      * @param {Number} x The new x position
8661      * @param {Number} y The new y position
8662      */
8663     onPosition : function(x, y){
8664
8665     },
8666
8667     // private
8668     adjustSize : function(w, h){
8669         if(this.autoWidth){
8670             w = 'auto';
8671         }
8672         if(this.autoHeight){
8673             h = 'auto';
8674         }
8675         return {width : w, height: h};
8676     },
8677
8678     // private
8679     adjustPosition : function(x, y){
8680         return {x : x, y: y};
8681     }
8682 });/*
8683  * Based on:
8684  * Ext JS Library 1.1.1
8685  * Copyright(c) 2006-2007, Ext JS, LLC.
8686  *
8687  * Originally Released Under LGPL - original licence link has changed is not relivant.
8688  *
8689  * Fork - LGPL
8690  * <script type="text/javascript">
8691  */
8692
8693
8694 /**
8695  * @class Roo.SplitBar
8696  * @extends Roo.util.Observable
8697  * Creates draggable splitter bar functionality from two elements (element to be dragged and element to be resized).
8698  * <br><br>
8699  * Usage:
8700  * <pre><code>
8701 var split = new Roo.SplitBar("elementToDrag", "elementToSize",
8702                    Roo.SplitBar.HORIZONTAL, Roo.SplitBar.LEFT);
8703 split.setAdapter(new Roo.SplitBar.AbsoluteLayoutAdapter("container"));
8704 split.minSize = 100;
8705 split.maxSize = 600;
8706 split.animate = true;
8707 split.on('moved', splitterMoved);
8708 </code></pre>
8709  * @constructor
8710  * Create a new SplitBar
8711  * @param {String/HTMLElement/Roo.Element} dragElement The element to be dragged and act as the SplitBar. 
8712  * @param {String/HTMLElement/Roo.Element} resizingElement The element to be resized based on where the SplitBar element is dragged 
8713  * @param {Number} orientation (optional) Either Roo.SplitBar.HORIZONTAL or Roo.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
8714  * @param {Number} placement (optional) Either Roo.SplitBar.LEFT or Roo.SplitBar.RIGHT for horizontal or  
8715                         Roo.SplitBar.TOP or Roo.SplitBar.BOTTOM for vertical. (By default, this is determined automatically by the initial
8716                         position of the SplitBar).
8717  */
8718 Roo.SplitBar = function(dragElement, resizingElement, orientation, placement, existingProxy){
8719     
8720     /** @private */
8721     this.el = Roo.get(dragElement, true);
8722     this.el.dom.unselectable = "on";
8723     /** @private */
8724     this.resizingEl = Roo.get(resizingElement, true);
8725
8726     /**
8727      * @private
8728      * The orientation of the split. Either Roo.SplitBar.HORIZONTAL or Roo.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
8729      * Note: If this is changed after creating the SplitBar, the placement property must be manually updated
8730      * @type Number
8731      */
8732     this.orientation = orientation || Roo.SplitBar.HORIZONTAL;
8733     
8734     /**
8735      * The minimum size of the resizing element. (Defaults to 0)
8736      * @type Number
8737      */
8738     this.minSize = 0;
8739     
8740     /**
8741      * The maximum size of the resizing element. (Defaults to 2000)
8742      * @type Number
8743      */
8744     this.maxSize = 2000;
8745     
8746     /**
8747      * Whether to animate the transition to the new size
8748      * @type Boolean
8749      */
8750     this.animate = false;
8751     
8752     /**
8753      * Whether to create a transparent shim that overlays the page when dragging, enables dragging across iframes.
8754      * @type Boolean
8755      */
8756     this.useShim = false;
8757     
8758     /** @private */
8759     this.shim = null;
8760     
8761     if(!existingProxy){
8762         /** @private */
8763         this.proxy = Roo.SplitBar.createProxy(this.orientation);
8764     }else{
8765         this.proxy = Roo.get(existingProxy).dom;
8766     }
8767     /** @private */
8768     this.dd = new Roo.dd.DDProxy(this.el.dom.id, "XSplitBars", {dragElId : this.proxy.id});
8769     
8770     /** @private */
8771     this.dd.b4StartDrag = this.onStartProxyDrag.createDelegate(this);
8772     
8773     /** @private */
8774     this.dd.endDrag = this.onEndProxyDrag.createDelegate(this);
8775     
8776     /** @private */
8777     this.dragSpecs = {};
8778     
8779     /**
8780      * @private The adapter to use to positon and resize elements
8781      */
8782     this.adapter = new Roo.SplitBar.BasicLayoutAdapter();
8783     this.adapter.init(this);
8784     
8785     if(this.orientation == Roo.SplitBar.HORIZONTAL){
8786         /** @private */
8787         this.placement = placement || (this.el.getX() > this.resizingEl.getX() ? Roo.SplitBar.LEFT : Roo.SplitBar.RIGHT);
8788         this.el.addClass("x-splitbar-h");
8789     }else{
8790         /** @private */
8791         this.placement = placement || (this.el.getY() > this.resizingEl.getY() ? Roo.SplitBar.TOP : Roo.SplitBar.BOTTOM);
8792         this.el.addClass("x-splitbar-v");
8793     }
8794     
8795     this.addEvents({
8796         /**
8797          * @event resize
8798          * Fires when the splitter is moved (alias for {@link #event-moved})
8799          * @param {Roo.SplitBar} this
8800          * @param {Number} newSize the new width or height
8801          */
8802         "resize" : true,
8803         /**
8804          * @event moved
8805          * Fires when the splitter is moved
8806          * @param {Roo.SplitBar} this
8807          * @param {Number} newSize the new width or height
8808          */
8809         "moved" : true,
8810         /**
8811          * @event beforeresize
8812          * Fires before the splitter is dragged
8813          * @param {Roo.SplitBar} this
8814          */
8815         "beforeresize" : true,
8816
8817         "beforeapply" : true
8818     });
8819
8820     Roo.util.Observable.call(this);
8821 };
8822
8823 Roo.extend(Roo.SplitBar, Roo.util.Observable, {
8824     onStartProxyDrag : function(x, y){
8825         this.fireEvent("beforeresize", this);
8826         if(!this.overlay){
8827             var o = Roo.DomHelper.insertFirst(document.body,  {cls: "x-drag-overlay", html: "&#160;"}, true);
8828             o.unselectable();
8829             o.enableDisplayMode("block");
8830             // all splitbars share the same overlay
8831             Roo.SplitBar.prototype.overlay = o;
8832         }
8833         this.overlay.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
8834         this.overlay.show();
8835         Roo.get(this.proxy).setDisplayed("block");
8836         var size = this.adapter.getElementSize(this);
8837         this.activeMinSize = this.getMinimumSize();;
8838         this.activeMaxSize = this.getMaximumSize();;
8839         var c1 = size - this.activeMinSize;
8840         var c2 = Math.max(this.activeMaxSize - size, 0);
8841         if(this.orientation == Roo.SplitBar.HORIZONTAL){
8842             this.dd.resetConstraints();
8843             this.dd.setXConstraint(
8844                 this.placement == Roo.SplitBar.LEFT ? c1 : c2, 
8845                 this.placement == Roo.SplitBar.LEFT ? c2 : c1
8846             );
8847             this.dd.setYConstraint(0, 0);
8848         }else{
8849             this.dd.resetConstraints();
8850             this.dd.setXConstraint(0, 0);
8851             this.dd.setYConstraint(
8852                 this.placement == Roo.SplitBar.TOP ? c1 : c2, 
8853                 this.placement == Roo.SplitBar.TOP ? c2 : c1
8854             );
8855          }
8856         this.dragSpecs.startSize = size;
8857         this.dragSpecs.startPoint = [x, y];
8858         Roo.dd.DDProxy.prototype.b4StartDrag.call(this.dd, x, y);
8859     },
8860     
8861     /** 
8862      * @private Called after the drag operation by the DDProxy
8863      */
8864     onEndProxyDrag : function(e){
8865         Roo.get(this.proxy).setDisplayed(false);
8866         var endPoint = Roo.lib.Event.getXY(e);
8867         if(this.overlay){
8868             this.overlay.hide();
8869         }
8870         var newSize;
8871         if(this.orientation == Roo.SplitBar.HORIZONTAL){
8872             newSize = this.dragSpecs.startSize + 
8873                 (this.placement == Roo.SplitBar.LEFT ?
8874                     endPoint[0] - this.dragSpecs.startPoint[0] :
8875                     this.dragSpecs.startPoint[0] - endPoint[0]
8876                 );
8877         }else{
8878             newSize = this.dragSpecs.startSize + 
8879                 (this.placement == Roo.SplitBar.TOP ?
8880                     endPoint[1] - this.dragSpecs.startPoint[1] :
8881                     this.dragSpecs.startPoint[1] - endPoint[1]
8882                 );
8883         }
8884         newSize = Math.min(Math.max(newSize, this.activeMinSize), this.activeMaxSize);
8885         if(newSize != this.dragSpecs.startSize){
8886             if(this.fireEvent('beforeapply', this, newSize) !== false){
8887                 this.adapter.setElementSize(this, newSize);
8888                 this.fireEvent("moved", this, newSize);
8889                 this.fireEvent("resize", this, newSize);
8890             }
8891         }
8892     },
8893     
8894     /**
8895      * Get the adapter this SplitBar uses
8896      * @return The adapter object
8897      */
8898     getAdapter : function(){
8899         return this.adapter;
8900     },
8901     
8902     /**
8903      * Set the adapter this SplitBar uses
8904      * @param {Object} adapter A SplitBar adapter object
8905      */
8906     setAdapter : function(adapter){
8907         this.adapter = adapter;
8908         this.adapter.init(this);
8909     },
8910     
8911     /**
8912      * Gets the minimum size for the resizing element
8913      * @return {Number} The minimum size
8914      */
8915     getMinimumSize : function(){
8916         return this.minSize;
8917     },
8918     
8919     /**
8920      * Sets the minimum size for the resizing element
8921      * @param {Number} minSize The minimum size
8922      */
8923     setMinimumSize : function(minSize){
8924         this.minSize = minSize;
8925     },
8926     
8927     /**
8928      * Gets the maximum size for the resizing element
8929      * @return {Number} The maximum size
8930      */
8931     getMaximumSize : function(){
8932         return this.maxSize;
8933     },
8934     
8935     /**
8936      * Sets the maximum size for the resizing element
8937      * @param {Number} maxSize The maximum size
8938      */
8939     setMaximumSize : function(maxSize){
8940         this.maxSize = maxSize;
8941     },
8942     
8943     /**
8944      * Sets the initialize size for the resizing element
8945      * @param {Number} size The initial size
8946      */
8947     setCurrentSize : function(size){
8948         var oldAnimate = this.animate;
8949         this.animate = false;
8950         this.adapter.setElementSize(this, size);
8951         this.animate = oldAnimate;
8952     },
8953     
8954     /**
8955      * Destroy this splitbar. 
8956      * @param {Boolean} removeEl True to remove the element
8957      */
8958     destroy : function(removeEl){
8959         if(this.shim){
8960             this.shim.remove();
8961         }
8962         this.dd.unreg();
8963         this.proxy.parentNode.removeChild(this.proxy);
8964         if(removeEl){
8965             this.el.remove();
8966         }
8967     }
8968 });
8969
8970 /**
8971  * @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.
8972  */
8973 Roo.SplitBar.createProxy = function(dir){
8974     var proxy = new Roo.Element(document.createElement("div"));
8975     proxy.unselectable();
8976     var cls = 'x-splitbar-proxy';
8977     proxy.addClass(cls + ' ' + (dir == Roo.SplitBar.HORIZONTAL ? cls +'-h' : cls + '-v'));
8978     document.body.appendChild(proxy.dom);
8979     return proxy.dom;
8980 };
8981
8982 /** 
8983  * @class Roo.SplitBar.BasicLayoutAdapter
8984  * Default Adapter. It assumes the splitter and resizing element are not positioned
8985  * elements and only gets/sets the width of the element. Generally used for table based layouts.
8986  */
8987 Roo.SplitBar.BasicLayoutAdapter = function(){
8988 };
8989
8990 Roo.SplitBar.BasicLayoutAdapter.prototype = {
8991     // do nothing for now
8992     init : function(s){
8993     
8994     },
8995     /**
8996      * Called before drag operations to get the current size of the resizing element. 
8997      * @param {Roo.SplitBar} s The SplitBar using this adapter
8998      */
8999      getElementSize : function(s){
9000         if(s.orientation == Roo.SplitBar.HORIZONTAL){
9001             return s.resizingEl.getWidth();
9002         }else{
9003             return s.resizingEl.getHeight();
9004         }
9005     },
9006     
9007     /**
9008      * Called after drag operations to set the size of the resizing element.
9009      * @param {Roo.SplitBar} s The SplitBar using this adapter
9010      * @param {Number} newSize The new size to set
9011      * @param {Function} onComplete A function to be invoked when resizing is complete
9012      */
9013     setElementSize : function(s, newSize, onComplete){
9014         if(s.orientation == Roo.SplitBar.HORIZONTAL){
9015             if(!s.animate){
9016                 s.resizingEl.setWidth(newSize);
9017                 if(onComplete){
9018                     onComplete(s, newSize);
9019                 }
9020             }else{
9021                 s.resizingEl.setWidth(newSize, true, .1, onComplete, 'easeOut');
9022             }
9023         }else{
9024             
9025             if(!s.animate){
9026                 s.resizingEl.setHeight(newSize);
9027                 if(onComplete){
9028                     onComplete(s, newSize);
9029                 }
9030             }else{
9031                 s.resizingEl.setHeight(newSize, true, .1, onComplete, 'easeOut');
9032             }
9033         }
9034     }
9035 };
9036
9037 /** 
9038  *@class Roo.SplitBar.AbsoluteLayoutAdapter
9039  * @extends Roo.SplitBar.BasicLayoutAdapter
9040  * Adapter that  moves the splitter element to align with the resized sizing element. 
9041  * Used with an absolute positioned SplitBar.
9042  * @param {String/HTMLElement/Roo.Element} container The container that wraps around the absolute positioned content. If it's
9043  * document.body, make sure you assign an id to the body element.
9044  */
9045 Roo.SplitBar.AbsoluteLayoutAdapter = function(container){
9046     this.basic = new Roo.SplitBar.BasicLayoutAdapter();
9047     this.container = Roo.get(container);
9048 };
9049
9050 Roo.SplitBar.AbsoluteLayoutAdapter.prototype = {
9051     init : function(s){
9052         this.basic.init(s);
9053     },
9054     
9055     getElementSize : function(s){
9056         return this.basic.getElementSize(s);
9057     },
9058     
9059     setElementSize : function(s, newSize, onComplete){
9060         this.basic.setElementSize(s, newSize, this.moveSplitter.createDelegate(this, [s]));
9061     },
9062     
9063     moveSplitter : function(s){
9064         var yes = Roo.SplitBar;
9065         switch(s.placement){
9066             case yes.LEFT:
9067                 s.el.setX(s.resizingEl.getRight());
9068                 break;
9069             case yes.RIGHT:
9070                 s.el.setStyle("right", (this.container.getWidth() - s.resizingEl.getLeft()) + "px");
9071                 break;
9072             case yes.TOP:
9073                 s.el.setY(s.resizingEl.getBottom());
9074                 break;
9075             case yes.BOTTOM:
9076                 s.el.setY(s.resizingEl.getTop() - s.el.getHeight());
9077                 break;
9078         }
9079     }
9080 };
9081
9082 /**
9083  * Orientation constant - Create a vertical SplitBar
9084  * @static
9085  * @type Number
9086  */
9087 Roo.SplitBar.VERTICAL = 1;
9088
9089 /**
9090  * Orientation constant - Create a horizontal SplitBar
9091  * @static
9092  * @type Number
9093  */
9094 Roo.SplitBar.HORIZONTAL = 2;
9095
9096 /**
9097  * Placement constant - The resizing element is to the left of the splitter element
9098  * @static
9099  * @type Number
9100  */
9101 Roo.SplitBar.LEFT = 1;
9102
9103 /**
9104  * Placement constant - The resizing element is to the right of the splitter element
9105  * @static
9106  * @type Number
9107  */
9108 Roo.SplitBar.RIGHT = 2;
9109
9110 /**
9111  * Placement constant - The resizing element is positioned above the splitter element
9112  * @static
9113  * @type Number
9114  */
9115 Roo.SplitBar.TOP = 3;
9116
9117 /**
9118  * Placement constant - The resizing element is positioned under splitter element
9119  * @static
9120  * @type Number
9121  */
9122 Roo.SplitBar.BOTTOM = 4;
9123 /*
9124  * Based on:
9125  * Ext JS Library 1.1.1
9126  * Copyright(c) 2006-2007, Ext JS, LLC.
9127  *
9128  * Originally Released Under LGPL - original licence link has changed is not relivant.
9129  *
9130  * Fork - LGPL
9131  * <script type="text/javascript">
9132  */
9133
9134 /**
9135  * @class Roo.View
9136  * @extends Roo.util.Observable
9137  * Create a "View" for an element based on a data model or UpdateManager and the supplied DomHelper template. 
9138  * This class also supports single and multi selection modes. <br>
9139  * Create a data model bound view:
9140  <pre><code>
9141  var store = new Roo.data.Store(...);
9142
9143  var view = new Roo.View({
9144     el : "my-element",
9145     tpl : '&lt;div id="{0}"&gt;{2} - {1}&lt;/div&gt;', // auto create template
9146  
9147     singleSelect: true,
9148     selectedClass: "ydataview-selected",
9149     store: store
9150  });
9151
9152  // listen for node click?
9153  view.on("click", function(vw, index, node, e){
9154  alert('Node "' + node.id + '" at index: ' + index + " was clicked.");
9155  });
9156
9157  // load XML data
9158  dataModel.load("foobar.xml");
9159  </code></pre>
9160  For an example of creating a JSON/UpdateManager view, see {@link Roo.JsonView}.
9161  * <br><br>
9162  * <b>Note: The root of your template must be a single node. Table/row implementations may work but are not supported due to
9163  * IE"s limited insertion support with tables and Opera"s faulty event bubbling.</b>
9164  * 
9165  * Note: old style constructor is still suported (container, template, config)
9166  * 
9167  * @constructor
9168  * Create a new View
9169  * @param {Object} config The config object
9170  * 
9171  */
9172 Roo.View = function(config, depreciated_tpl, depreciated_config){
9173     
9174     if (typeof(depreciated_tpl) == 'undefined') {
9175         // new way.. - universal constructor.
9176         Roo.apply(this, config);
9177         this.el  = Roo.get(this.el);
9178     } else {
9179         // old format..
9180         this.el  = Roo.get(config);
9181         this.tpl = depreciated_tpl;
9182         Roo.apply(this, depreciated_config);
9183     }
9184     this.wrapEl  = this.el.wrap().wrap();
9185     ///this.el = this.wrapEla.appendChild(document.createElement("div"));
9186     
9187     
9188     if(typeof(this.tpl) == "string"){
9189         this.tpl = new Roo.Template(this.tpl);
9190     } else {
9191         // support xtype ctors..
9192         this.tpl = new Roo.factory(this.tpl, Roo);
9193     }
9194     
9195     
9196     this.tpl.compile();
9197    
9198   
9199     
9200      
9201     /** @private */
9202     this.addEvents({
9203         /**
9204          * @event beforeclick
9205          * Fires before a click is processed. Returns false to cancel the default action.
9206          * @param {Roo.View} this
9207          * @param {Number} index The index of the target node
9208          * @param {HTMLElement} node The target node
9209          * @param {Roo.EventObject} e The raw event object
9210          */
9211             "beforeclick" : true,
9212         /**
9213          * @event click
9214          * Fires when a template node is clicked.
9215          * @param {Roo.View} this
9216          * @param {Number} index The index of the target node
9217          * @param {HTMLElement} node The target node
9218          * @param {Roo.EventObject} e The raw event object
9219          */
9220             "click" : true,
9221         /**
9222          * @event dblclick
9223          * Fires when a template node is double clicked.
9224          * @param {Roo.View} this
9225          * @param {Number} index The index of the target node
9226          * @param {HTMLElement} node The target node
9227          * @param {Roo.EventObject} e The raw event object
9228          */
9229             "dblclick" : true,
9230         /**
9231          * @event contextmenu
9232          * Fires when a template node is right clicked.
9233          * @param {Roo.View} this
9234          * @param {Number} index The index of the target node
9235          * @param {HTMLElement} node The target node
9236          * @param {Roo.EventObject} e The raw event object
9237          */
9238             "contextmenu" : true,
9239         /**
9240          * @event selectionchange
9241          * Fires when the selected nodes change.
9242          * @param {Roo.View} this
9243          * @param {Array} selections Array of the selected nodes
9244          */
9245             "selectionchange" : true,
9246     
9247         /**
9248          * @event beforeselect
9249          * Fires before a selection is made. If any handlers return false, the selection is cancelled.
9250          * @param {Roo.View} this
9251          * @param {HTMLElement} node The node to be selected
9252          * @param {Array} selections Array of currently selected nodes
9253          */
9254             "beforeselect" : true,
9255         /**
9256          * @event preparedata
9257          * Fires on every row to render, to allow you to change the data.
9258          * @param {Roo.View} this
9259          * @param {Object} data to be rendered (change this)
9260          */
9261           "preparedata" : true
9262           
9263           
9264         });
9265
9266
9267
9268     this.el.on({
9269         "click": this.onClick,
9270         "dblclick": this.onDblClick,
9271         "contextmenu": this.onContextMenu,
9272         scope:this
9273     });
9274
9275     this.selections = [];
9276     this.nodes = [];
9277     this.cmp = new Roo.CompositeElementLite([]);
9278     if(this.store){
9279         this.store = Roo.factory(this.store, Roo.data);
9280         this.setStore(this.store, true);
9281     }
9282     
9283     if ( this.footer && this.footer.xtype) {
9284            
9285          var fctr = this.wrapEl.appendChild(document.createElement("div"));
9286         
9287         this.footer.dataSource = this.store
9288         this.footer.container = fctr;
9289         this.footer = Roo.factory(this.footer, Roo);
9290         fctr.insertFirst(this.el);
9291         
9292         // this is a bit insane - as the paging toolbar seems to detach the el..
9293 //        dom.parentNode.parentNode.parentNode
9294          // they get detached?
9295     }
9296     
9297     
9298     Roo.View.superclass.constructor.call(this);
9299     
9300     
9301 };
9302
9303 Roo.extend(Roo.View, Roo.util.Observable, {
9304     
9305      /**
9306      * @cfg {Roo.data.Store} store Data store to load data from.
9307      */
9308     store : false,
9309     
9310     /**
9311      * @cfg {String|Roo.Element} el The container element.
9312      */
9313     el : '',
9314     
9315     /**
9316      * @cfg {String|Roo.Template} tpl The template used by this View 
9317      */
9318     tpl : false,
9319     /**
9320      * @cfg {String} dataName the named area of the template to use as the data area
9321      *                          Works with domtemplates roo-name="name"
9322      */
9323     dataName: false,
9324     /**
9325      * @cfg {String} selectedClass The css class to add to selected nodes
9326      */
9327     selectedClass : "x-view-selected",
9328      /**
9329      * @cfg {String} emptyText The empty text to show when nothing is loaded.
9330      */
9331     emptyText : "",
9332     
9333     /**
9334      * @cfg {String} text to display on mask (default Loading)
9335      */
9336     mask : false,
9337     /**
9338      * @cfg {Boolean} multiSelect Allow multiple selection
9339      */
9340     multiSelect : false,
9341     /**
9342      * @cfg {Boolean} singleSelect Allow single selection
9343      */
9344     singleSelect:  false,
9345     
9346     /**
9347      * @cfg {Boolean} toggleSelect - selecting 
9348      */
9349     toggleSelect : false,
9350     
9351     /**
9352      * Returns the element this view is bound to.
9353      * @return {Roo.Element}
9354      */
9355     getEl : function(){
9356         return this.wrapEl;
9357     },
9358     
9359     
9360
9361     /**
9362      * Refreshes the view. - called by datachanged on the store. - do not call directly.
9363      */
9364     refresh : function(){
9365         var t = this.tpl;
9366         
9367         // if we are using something like 'domtemplate', then
9368         // the what gets used is:
9369         // t.applySubtemplate(NAME, data, wrapping data..)
9370         // the outer template then get' applied with
9371         //     the store 'extra data'
9372         // and the body get's added to the
9373         //      roo-name="data" node?
9374         //      <span class='roo-tpl-{name}'></span> ?????
9375         
9376         
9377         
9378         this.clearSelections();
9379         this.el.update("");
9380         var html = [];
9381         var records = this.store.getRange();
9382         if(records.length < 1) {
9383             
9384             // is this valid??  = should it render a template??
9385             
9386             this.el.update(this.emptyText);
9387             return;
9388         }
9389         var el = this.el;
9390         if (this.dataName) {
9391             this.el.update(t.apply(this.store.meta)); //????
9392             el = this.el.child('.roo-tpl-' + this.dataName);
9393         }
9394         
9395         for(var i = 0, len = records.length; i < len; i++){
9396             var data = this.prepareData(records[i].data, i, records[i]);
9397             this.fireEvent("preparedata", this, data, i, records[i]);
9398             html[html.length] = Roo.util.Format.trim(
9399                 this.dataName ?
9400                     t.applySubtemplate(this.dataName, data, this.store.meta) :
9401                     t.apply(data)
9402             );
9403         }
9404         
9405         
9406         
9407         el.update(html.join(""));
9408         this.nodes = el.dom.childNodes;
9409         this.updateIndexes(0);
9410     },
9411
9412     /**
9413      * Function to override to reformat the data that is sent to
9414      * the template for each node.
9415      * DEPRICATED - use the preparedata event handler.
9416      * @param {Array/Object} data The raw data (array of colData for a data model bound view or
9417      * a JSON object for an UpdateManager bound view).
9418      */
9419     prepareData : function(data, index, record)
9420     {
9421         this.fireEvent("preparedata", this, data, index, record);
9422         return data;
9423     },
9424
9425     onUpdate : function(ds, record){
9426         this.clearSelections();
9427         var index = this.store.indexOf(record);
9428         var n = this.nodes[index];
9429         this.tpl.insertBefore(n, this.prepareData(record.data, index, record));
9430         n.parentNode.removeChild(n);
9431         this.updateIndexes(index, index);
9432     },
9433
9434     
9435     
9436 // --------- FIXME     
9437     onAdd : function(ds, records, index)
9438     {
9439         this.clearSelections();
9440         if(this.nodes.length == 0){
9441             this.refresh();
9442             return;
9443         }
9444         var n = this.nodes[index];
9445         for(var i = 0, len = records.length; i < len; i++){
9446             var d = this.prepareData(records[i].data, i, records[i]);
9447             if(n){
9448                 this.tpl.insertBefore(n, d);
9449             }else{
9450                 
9451                 this.tpl.append(this.el, d);
9452             }
9453         }
9454         this.updateIndexes(index);
9455     },
9456
9457     onRemove : function(ds, record, index){
9458         this.clearSelections();
9459         var el = this.dataName  ?
9460             this.el.child('.roo-tpl-' + this.dataName) :
9461             this.el; 
9462         el.dom.removeChild(this.nodes[index]);
9463         this.updateIndexes(index);
9464     },
9465
9466     /**
9467      * Refresh an individual node.
9468      * @param {Number} index
9469      */
9470     refreshNode : function(index){
9471         this.onUpdate(this.store, this.store.getAt(index));
9472     },
9473
9474     updateIndexes : function(startIndex, endIndex){
9475         var ns = this.nodes;
9476         startIndex = startIndex || 0;
9477         endIndex = endIndex || ns.length - 1;
9478         for(var i = startIndex; i <= endIndex; i++){
9479             ns[i].nodeIndex = i;
9480         }
9481     },
9482
9483     /**
9484      * Changes the data store this view uses and refresh the view.
9485      * @param {Store} store
9486      */
9487     setStore : function(store, initial){
9488         if(!initial && this.store){
9489             this.store.un("datachanged", this.refresh);
9490             this.store.un("add", this.onAdd);
9491             this.store.un("remove", this.onRemove);
9492             this.store.un("update", this.onUpdate);
9493             this.store.un("clear", this.refresh);
9494             this.store.un("beforeload", this.onBeforeLoad);
9495             this.store.un("load", this.onLoad);
9496             this.store.un("loadexception", this.onLoad);
9497         }
9498         if(store){
9499           
9500             store.on("datachanged", this.refresh, this);
9501             store.on("add", this.onAdd, this);
9502             store.on("remove", this.onRemove, this);
9503             store.on("update", this.onUpdate, this);
9504             store.on("clear", this.refresh, this);
9505             store.on("beforeload", this.onBeforeLoad, this);
9506             store.on("load", this.onLoad, this);
9507             store.on("loadexception", this.onLoad, this);
9508         }
9509         
9510         if(store){
9511             this.refresh();
9512         }
9513     },
9514     /**
9515      * onbeforeLoad - masks the loading area.
9516      *
9517      */
9518     onBeforeLoad : function()
9519     {
9520         this.el.update("");
9521         this.el.mask(this.mask ? this.mask : "Loading" ); 
9522     },
9523     onLoad : function ()
9524     {
9525         this.el.unmask();
9526     },
9527     
9528
9529     /**
9530      * Returns the template node the passed child belongs to or null if it doesn't belong to one.
9531      * @param {HTMLElement} node
9532      * @return {HTMLElement} The template node
9533      */
9534     findItemFromChild : function(node){
9535         var el = this.dataName  ?
9536             this.el.child('.roo-tpl-' + this.dataName,true) :
9537             this.el.dom; 
9538         
9539         if(!node || node.parentNode == el){
9540                     return node;
9541             }
9542             var p = node.parentNode;
9543             while(p && p != el){
9544             if(p.parentNode == el){
9545                 return p;
9546             }
9547             p = p.parentNode;
9548         }
9549             return null;
9550     },
9551
9552     /** @ignore */
9553     onClick : function(e){
9554         var item = this.findItemFromChild(e.getTarget());
9555         if(item){
9556             var index = this.indexOf(item);
9557             if(this.onItemClick(item, index, e) !== false){
9558                 this.fireEvent("click", this, index, item, e);
9559             }
9560         }else{
9561             this.clearSelections();
9562         }
9563     },
9564
9565     /** @ignore */
9566     onContextMenu : function(e){
9567         var item = this.findItemFromChild(e.getTarget());
9568         if(item){
9569             this.fireEvent("contextmenu", this, this.indexOf(item), item, e);
9570         }
9571     },
9572
9573     /** @ignore */
9574     onDblClick : function(e){
9575         var item = this.findItemFromChild(e.getTarget());
9576         if(item){
9577             this.fireEvent("dblclick", this, this.indexOf(item), item, e);
9578         }
9579     },
9580
9581     onItemClick : function(item, index, e)
9582     {
9583         if(this.fireEvent("beforeclick", this, index, item, e) === false){
9584             return false;
9585         }
9586         if (this.toggleSelect) {
9587             var m = this.isSelected(item) ? 'unselect' : 'select';
9588             Roo.log(m);
9589             var _t = this;
9590             _t[m](item, true, false);
9591             return true;
9592         }
9593         if(this.multiSelect || this.singleSelect){
9594             if(this.multiSelect && e.shiftKey && this.lastSelection){
9595                 this.select(this.getNodes(this.indexOf(this.lastSelection), index), false);
9596             }else{
9597                 this.select(item, this.multiSelect && e.ctrlKey);
9598                 this.lastSelection = item;
9599             }
9600             e.preventDefault();
9601         }
9602         return true;
9603     },
9604
9605     /**
9606      * Get the number of selected nodes.
9607      * @return {Number}
9608      */
9609     getSelectionCount : function(){
9610         return this.selections.length;
9611     },
9612
9613     /**
9614      * Get the currently selected nodes.
9615      * @return {Array} An array of HTMLElements
9616      */
9617     getSelectedNodes : function(){
9618         return this.selections;
9619     },
9620
9621     /**
9622      * Get the indexes of the selected nodes.
9623      * @return {Array}
9624      */
9625     getSelectedIndexes : function(){
9626         var indexes = [], s = this.selections;
9627         for(var i = 0, len = s.length; i < len; i++){
9628             indexes.push(s[i].nodeIndex);
9629         }
9630         return indexes;
9631     },
9632
9633     /**
9634      * Clear all selections
9635      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange event
9636      */
9637     clearSelections : function(suppressEvent){
9638         if(this.nodes && (this.multiSelect || this.singleSelect) && this.selections.length > 0){
9639             this.cmp.elements = this.selections;
9640             this.cmp.removeClass(this.selectedClass);
9641             this.selections = [];
9642             if(!suppressEvent){
9643                 this.fireEvent("selectionchange", this, this.selections);
9644             }
9645         }
9646     },
9647
9648     /**
9649      * Returns true if the passed node is selected
9650      * @param {HTMLElement/Number} node The node or node index
9651      * @return {Boolean}
9652      */
9653     isSelected : function(node){
9654         var s = this.selections;
9655         if(s.length < 1){
9656             return false;
9657         }
9658         node = this.getNode(node);
9659         return s.indexOf(node) !== -1;
9660     },
9661
9662     /**
9663      * Selects nodes.
9664      * @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
9665      * @param {Boolean} keepExisting (optional) true to keep existing selections
9666      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
9667      */
9668     select : function(nodeInfo, keepExisting, suppressEvent){
9669         if(nodeInfo instanceof Array){
9670             if(!keepExisting){
9671                 this.clearSelections(true);
9672             }
9673             for(var i = 0, len = nodeInfo.length; i < len; i++){
9674                 this.select(nodeInfo[i], true, true);
9675             }
9676             return;
9677         } 
9678         var node = this.getNode(nodeInfo);
9679         if(!node || this.isSelected(node)){
9680             return; // already selected.
9681         }
9682         if(!keepExisting){
9683             this.clearSelections(true);
9684         }
9685         if(this.fireEvent("beforeselect", this, node, this.selections) !== false){
9686             Roo.fly(node).addClass(this.selectedClass);
9687             this.selections.push(node);
9688             if(!suppressEvent){
9689                 this.fireEvent("selectionchange", this, this.selections);
9690             }
9691         }
9692         
9693         
9694     },
9695       /**
9696      * Unselects nodes.
9697      * @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
9698      * @param {Boolean} keepExisting (optional) true IGNORED (for campatibility with select)
9699      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
9700      */
9701     unselect : function(nodeInfo, keepExisting, suppressEvent)
9702     {
9703         if(nodeInfo instanceof Array){
9704             Roo.each(this.selections, function(s) {
9705                 this.unselect(s, nodeInfo);
9706             }, this);
9707             return;
9708         }
9709         var node = this.getNode(nodeInfo);
9710         if(!node || !this.isSelected(node)){
9711             Roo.log("not selected");
9712             return; // not selected.
9713         }
9714         // fireevent???
9715         var ns = [];
9716         Roo.each(this.selections, function(s) {
9717             if (s == node ) {
9718                 Roo.fly(node).removeClass(this.selectedClass);
9719
9720                 return;
9721             }
9722             ns.push(s);
9723         },this);
9724         
9725         this.selections= ns;
9726         this.fireEvent("selectionchange", this, this.selections);
9727     },
9728
9729     /**
9730      * Gets a template node.
9731      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
9732      * @return {HTMLElement} The node or null if it wasn't found
9733      */
9734     getNode : function(nodeInfo){
9735         if(typeof nodeInfo == "string"){
9736             return document.getElementById(nodeInfo);
9737         }else if(typeof nodeInfo == "number"){
9738             return this.nodes[nodeInfo];
9739         }
9740         return nodeInfo;
9741     },
9742
9743     /**
9744      * Gets a range template nodes.
9745      * @param {Number} startIndex
9746      * @param {Number} endIndex
9747      * @return {Array} An array of nodes
9748      */
9749     getNodes : function(start, end){
9750         var ns = this.nodes;
9751         start = start || 0;
9752         end = typeof end == "undefined" ? ns.length - 1 : end;
9753         var nodes = [];
9754         if(start <= end){
9755             for(var i = start; i <= end; i++){
9756                 nodes.push(ns[i]);
9757             }
9758         } else{
9759             for(var i = start; i >= end; i--){
9760                 nodes.push(ns[i]);
9761             }
9762         }
9763         return nodes;
9764     },
9765
9766     /**
9767      * Finds the index of the passed node
9768      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
9769      * @return {Number} The index of the node or -1
9770      */
9771     indexOf : function(node){
9772         node = this.getNode(node);
9773         if(typeof node.nodeIndex == "number"){
9774             return node.nodeIndex;
9775         }
9776         var ns = this.nodes;
9777         for(var i = 0, len = ns.length; i < len; i++){
9778             if(ns[i] == node){
9779                 return i;
9780             }
9781         }
9782         return -1;
9783     }
9784 });
9785 /*
9786  * Based on:
9787  * Ext JS Library 1.1.1
9788  * Copyright(c) 2006-2007, Ext JS, LLC.
9789  *
9790  * Originally Released Under LGPL - original licence link has changed is not relivant.
9791  *
9792  * Fork - LGPL
9793  * <script type="text/javascript">
9794  */
9795
9796 /**
9797  * @class Roo.JsonView
9798  * @extends Roo.View
9799  * Shortcut class to create a JSON + {@link Roo.UpdateManager} template view. Usage:
9800 <pre><code>
9801 var view = new Roo.JsonView({
9802     container: "my-element",
9803     tpl: '&lt;div id="{id}"&gt;{foo} - {bar}&lt;/div&gt;', // auto create template
9804     multiSelect: true, 
9805     jsonRoot: "data" 
9806 });
9807
9808 // listen for node click?
9809 view.on("click", function(vw, index, node, e){
9810     alert('Node "' + node.id + '" at index: ' + index + " was clicked.");
9811 });
9812
9813 // direct load of JSON data
9814 view.load("foobar.php");
9815
9816 // Example from my blog list
9817 var tpl = new Roo.Template(
9818     '&lt;div class="entry"&gt;' +
9819     '&lt;a class="entry-title" href="{link}"&gt;{title}&lt;/a&gt;' +
9820     "&lt;h4&gt;{date} by {author} | {comments} Comments&lt;/h4&gt;{description}" +
9821     "&lt;/div&gt;&lt;hr /&gt;"
9822 );
9823
9824 var moreView = new Roo.JsonView({
9825     container :  "entry-list", 
9826     template : tpl,
9827     jsonRoot: "posts"
9828 });
9829 moreView.on("beforerender", this.sortEntries, this);
9830 moreView.load({
9831     url: "/blog/get-posts.php",
9832     params: "allposts=true",
9833     text: "Loading Blog Entries..."
9834 });
9835 </code></pre>
9836
9837 * Note: old code is supported with arguments : (container, template, config)
9838
9839
9840  * @constructor
9841  * Create a new JsonView
9842  * 
9843  * @param {Object} config The config object
9844  * 
9845  */
9846 Roo.JsonView = function(config, depreciated_tpl, depreciated_config){
9847     
9848     
9849     Roo.JsonView.superclass.constructor.call(this, config, depreciated_tpl, depreciated_config);
9850
9851     var um = this.el.getUpdateManager();
9852     um.setRenderer(this);
9853     um.on("update", this.onLoad, this);
9854     um.on("failure", this.onLoadException, this);
9855
9856     /**
9857      * @event beforerender
9858      * Fires before rendering of the downloaded JSON data.
9859      * @param {Roo.JsonView} this
9860      * @param {Object} data The JSON data loaded
9861      */
9862     /**
9863      * @event load
9864      * Fires when data is loaded.
9865      * @param {Roo.JsonView} this
9866      * @param {Object} data The JSON data loaded
9867      * @param {Object} response The raw Connect response object
9868      */
9869     /**
9870      * @event loadexception
9871      * Fires when loading fails.
9872      * @param {Roo.JsonView} this
9873      * @param {Object} response The raw Connect response object
9874      */
9875     this.addEvents({
9876         'beforerender' : true,
9877         'load' : true,
9878         'loadexception' : true
9879     });
9880 };
9881 Roo.extend(Roo.JsonView, Roo.View, {
9882     /**
9883      * @type {String} The root property in the loaded JSON object that contains the data
9884      */
9885     jsonRoot : "",
9886
9887     /**
9888      * Refreshes the view.
9889      */
9890     refresh : function(){
9891         this.clearSelections();
9892         this.el.update("");
9893         var html = [];
9894         var o = this.jsonData;
9895         if(o && o.length > 0){
9896             for(var i = 0, len = o.length; i < len; i++){
9897                 var data = this.prepareData(o[i], i, o);
9898                 html[html.length] = this.tpl.apply(data);
9899             }
9900         }else{
9901             html.push(this.emptyText);
9902         }
9903         this.el.update(html.join(""));
9904         this.nodes = this.el.dom.childNodes;
9905         this.updateIndexes(0);
9906     },
9907
9908     /**
9909      * 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.
9910      * @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:
9911      <pre><code>
9912      view.load({
9913          url: "your-url.php",
9914          params: {param1: "foo", param2: "bar"}, // or a URL encoded string
9915          callback: yourFunction,
9916          scope: yourObject, //(optional scope)
9917          discardUrl: false,
9918          nocache: false,
9919          text: "Loading...",
9920          timeout: 30,
9921          scripts: false
9922      });
9923      </code></pre>
9924      * The only required property is <i>url</i>. The optional properties <i>nocache</i>, <i>text</i> and <i>scripts</i>
9925      * 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.
9926      * @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}
9927      * @param {Function} callback (optional) Callback when transaction is complete - called with signature (oElement, bSuccess)
9928      * @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.
9929      */
9930     load : function(){
9931         var um = this.el.getUpdateManager();
9932         um.update.apply(um, arguments);
9933     },
9934
9935     render : function(el, response){
9936         this.clearSelections();
9937         this.el.update("");
9938         var o;
9939         try{
9940             o = Roo.util.JSON.decode(response.responseText);
9941             if(this.jsonRoot){
9942                 
9943                 o = o[this.jsonRoot];
9944             }
9945         } catch(e){
9946         }
9947         /**
9948          * The current JSON data or null
9949          */
9950         this.jsonData = o;
9951         this.beforeRender();
9952         this.refresh();
9953     },
9954
9955 /**
9956  * Get the number of records in the current JSON dataset
9957  * @return {Number}
9958  */
9959     getCount : function(){
9960         return this.jsonData ? this.jsonData.length : 0;
9961     },
9962
9963 /**
9964  * Returns the JSON object for the specified node(s)
9965  * @param {HTMLElement/Array} node The node or an array of nodes
9966  * @return {Object/Array} If you pass in an array, you get an array back, otherwise
9967  * you get the JSON object for the node
9968  */
9969     getNodeData : function(node){
9970         if(node instanceof Array){
9971             var data = [];
9972             for(var i = 0, len = node.length; i < len; i++){
9973                 data.push(this.getNodeData(node[i]));
9974             }
9975             return data;
9976         }
9977         return this.jsonData[this.indexOf(node)] || null;
9978     },
9979
9980     beforeRender : function(){
9981         this.snapshot = this.jsonData;
9982         if(this.sortInfo){
9983             this.sort.apply(this, this.sortInfo);
9984         }
9985         this.fireEvent("beforerender", this, this.jsonData);
9986     },
9987
9988     onLoad : function(el, o){
9989         this.fireEvent("load", this, this.jsonData, o);
9990     },
9991
9992     onLoadException : function(el, o){
9993         this.fireEvent("loadexception", this, o);
9994     },
9995
9996 /**
9997  * Filter the data by a specific property.
9998  * @param {String} property A property on your JSON objects
9999  * @param {String/RegExp} value Either string that the property values
10000  * should start with, or a RegExp to test against the property
10001  */
10002     filter : function(property, value){
10003         if(this.jsonData){
10004             var data = [];
10005             var ss = this.snapshot;
10006             if(typeof value == "string"){
10007                 var vlen = value.length;
10008                 if(vlen == 0){
10009                     this.clearFilter();
10010                     return;
10011                 }
10012                 value = value.toLowerCase();
10013                 for(var i = 0, len = ss.length; i < len; i++){
10014                     var o = ss[i];
10015                     if(o[property].substr(0, vlen).toLowerCase() == value){
10016                         data.push(o);
10017                     }
10018                 }
10019             } else if(value.exec){ // regex?
10020                 for(var i = 0, len = ss.length; i < len; i++){
10021                     var o = ss[i];
10022                     if(value.test(o[property])){
10023                         data.push(o);
10024                     }
10025                 }
10026             } else{
10027                 return;
10028             }
10029             this.jsonData = data;
10030             this.refresh();
10031         }
10032     },
10033
10034 /**
10035  * Filter by a function. The passed function will be called with each
10036  * object in the current dataset. If the function returns true the value is kept,
10037  * otherwise it is filtered.
10038  * @param {Function} fn
10039  * @param {Object} scope (optional) The scope of the function (defaults to this JsonView)
10040  */
10041     filterBy : function(fn, scope){
10042         if(this.jsonData){
10043             var data = [];
10044             var ss = this.snapshot;
10045             for(var i = 0, len = ss.length; i < len; i++){
10046                 var o = ss[i];
10047                 if(fn.call(scope || this, o)){
10048                     data.push(o);
10049                 }
10050             }
10051             this.jsonData = data;
10052             this.refresh();
10053         }
10054     },
10055
10056 /**
10057  * Clears the current filter.
10058  */
10059     clearFilter : function(){
10060         if(this.snapshot && this.jsonData != this.snapshot){
10061             this.jsonData = this.snapshot;
10062             this.refresh();
10063         }
10064     },
10065
10066
10067 /**
10068  * Sorts the data for this view and refreshes it.
10069  * @param {String} property A property on your JSON objects to sort on
10070  * @param {String} direction (optional) "desc" or "asc" (defaults to "asc")
10071  * @param {Function} sortType (optional) A function to call to convert the data to a sortable value.
10072  */
10073     sort : function(property, dir, sortType){
10074         this.sortInfo = Array.prototype.slice.call(arguments, 0);
10075         if(this.jsonData){
10076             var p = property;
10077             var dsc = dir && dir.toLowerCase() == "desc";
10078             var f = function(o1, o2){
10079                 var v1 = sortType ? sortType(o1[p]) : o1[p];
10080                 var v2 = sortType ? sortType(o2[p]) : o2[p];
10081                 ;
10082                 if(v1 < v2){
10083                     return dsc ? +1 : -1;
10084                 } else if(v1 > v2){
10085                     return dsc ? -1 : +1;
10086                 } else{
10087                     return 0;
10088                 }
10089             };
10090             this.jsonData.sort(f);
10091             this.refresh();
10092             if(this.jsonData != this.snapshot){
10093                 this.snapshot.sort(f);
10094             }
10095         }
10096     }
10097 });/*
10098  * Based on:
10099  * Ext JS Library 1.1.1
10100  * Copyright(c) 2006-2007, Ext JS, LLC.
10101  *
10102  * Originally Released Under LGPL - original licence link has changed is not relivant.
10103  *
10104  * Fork - LGPL
10105  * <script type="text/javascript">
10106  */
10107  
10108
10109 /**
10110  * @class Roo.ColorPalette
10111  * @extends Roo.Component
10112  * Simple color palette class for choosing colors.  The palette can be rendered to any container.<br />
10113  * Here's an example of typical usage:
10114  * <pre><code>
10115 var cp = new Roo.ColorPalette({value:'993300'});  // initial selected color
10116 cp.render('my-div');
10117
10118 cp.on('select', function(palette, selColor){
10119     // do something with selColor
10120 });
10121 </code></pre>
10122  * @constructor
10123  * Create a new ColorPalette
10124  * @param {Object} config The config object
10125  */
10126 Roo.ColorPalette = function(config){
10127     Roo.ColorPalette.superclass.constructor.call(this, config);
10128     this.addEvents({
10129         /**
10130              * @event select
10131              * Fires when a color is selected
10132              * @param {ColorPalette} this
10133              * @param {String} color The 6-digit color hex code (without the # symbol)
10134              */
10135         select: true
10136     });
10137
10138     if(this.handler){
10139         this.on("select", this.handler, this.scope, true);
10140     }
10141 };
10142 Roo.extend(Roo.ColorPalette, Roo.Component, {
10143     /**
10144      * @cfg {String} itemCls
10145      * The CSS class to apply to the containing element (defaults to "x-color-palette")
10146      */
10147     itemCls : "x-color-palette",
10148     /**
10149      * @cfg {String} value
10150      * The initial color to highlight (should be a valid 6-digit color hex code without the # symbol).  Note that
10151      * the hex codes are case-sensitive.
10152      */
10153     value : null,
10154     clickEvent:'click',
10155     // private
10156     ctype: "Roo.ColorPalette",
10157
10158     /**
10159      * @cfg {Boolean} allowReselect If set to true then reselecting a color that is already selected fires the selection event
10160      */
10161     allowReselect : false,
10162
10163     /**
10164      * <p>An array of 6-digit color hex code strings (without the # symbol).  This array can contain any number
10165      * of colors, and each hex code should be unique.  The width of the palette is controlled via CSS by adjusting
10166      * the width property of the 'x-color-palette' class (or assigning a custom class), so you can balance the number
10167      * of colors with the width setting until the box is symmetrical.</p>
10168      * <p>You can override individual colors if needed:</p>
10169      * <pre><code>
10170 var cp = new Roo.ColorPalette();
10171 cp.colors[0] = "FF0000";  // change the first box to red
10172 </code></pre>
10173
10174 Or you can provide a custom array of your own for complete control:
10175 <pre><code>
10176 var cp = new Roo.ColorPalette();
10177 cp.colors = ["000000", "993300", "333300"];
10178 </code></pre>
10179      * @type Array
10180      */
10181     colors : [
10182         "000000", "993300", "333300", "003300", "003366", "000080", "333399", "333333",
10183         "800000", "FF6600", "808000", "008000", "008080", "0000FF", "666699", "808080",
10184         "FF0000", "FF9900", "99CC00", "339966", "33CCCC", "3366FF", "800080", "969696",
10185         "FF00FF", "FFCC00", "FFFF00", "00FF00", "00FFFF", "00CCFF", "993366", "C0C0C0",
10186         "FF99CC", "FFCC99", "FFFF99", "CCFFCC", "CCFFFF", "99CCFF", "CC99FF", "FFFFFF"
10187     ],
10188
10189     // private
10190     onRender : function(container, position){
10191         var t = new Roo.MasterTemplate(
10192             '<tpl><a href="#" class="color-{0}" hidefocus="on"><em><span style="background:#{0}" unselectable="on">&#160;</span></em></a></tpl>'
10193         );
10194         var c = this.colors;
10195         for(var i = 0, len = c.length; i < len; i++){
10196             t.add([c[i]]);
10197         }
10198         var el = document.createElement("div");
10199         el.className = this.itemCls;
10200         t.overwrite(el);
10201         container.dom.insertBefore(el, position);
10202         this.el = Roo.get(el);
10203         this.el.on(this.clickEvent, this.handleClick,  this, {delegate: "a"});
10204         if(this.clickEvent != 'click'){
10205             this.el.on('click', Roo.emptyFn,  this, {delegate: "a", preventDefault:true});
10206         }
10207     },
10208
10209     // private
10210     afterRender : function(){
10211         Roo.ColorPalette.superclass.afterRender.call(this);
10212         if(this.value){
10213             var s = this.value;
10214             this.value = null;
10215             this.select(s);
10216         }
10217     },
10218
10219     // private
10220     handleClick : function(e, t){
10221         e.preventDefault();
10222         if(!this.disabled){
10223             var c = t.className.match(/(?:^|\s)color-(.{6})(?:\s|$)/)[1];
10224             this.select(c.toUpperCase());
10225         }
10226     },
10227
10228     /**
10229      * Selects the specified color in the palette (fires the select event)
10230      * @param {String} color A valid 6-digit color hex code (# will be stripped if included)
10231      */
10232     select : function(color){
10233         color = color.replace("#", "");
10234         if(color != this.value || this.allowReselect){
10235             var el = this.el;
10236             if(this.value){
10237                 el.child("a.color-"+this.value).removeClass("x-color-palette-sel");
10238             }
10239             el.child("a.color-"+color).addClass("x-color-palette-sel");
10240             this.value = color;
10241             this.fireEvent("select", this, color);
10242         }
10243     }
10244 });/*
10245  * Based on:
10246  * Ext JS Library 1.1.1
10247  * Copyright(c) 2006-2007, Ext JS, LLC.
10248  *
10249  * Originally Released Under LGPL - original licence link has changed is not relivant.
10250  *
10251  * Fork - LGPL
10252  * <script type="text/javascript">
10253  */
10254  
10255 /**
10256  * @class Roo.DatePicker
10257  * @extends Roo.Component
10258  * Simple date picker class.
10259  * @constructor
10260  * Create a new DatePicker
10261  * @param {Object} config The config object
10262  */
10263 Roo.DatePicker = function(config){
10264     Roo.DatePicker.superclass.constructor.call(this, config);
10265
10266     this.value = config && config.value ?
10267                  config.value.clearTime() : new Date().clearTime();
10268
10269     this.addEvents({
10270         /**
10271              * @event select
10272              * Fires when a date is selected
10273              * @param {DatePicker} this
10274              * @param {Date} date The selected date
10275              */
10276         'select': true,
10277         /**
10278              * @event monthchange
10279              * Fires when the displayed month changes 
10280              * @param {DatePicker} this
10281              * @param {Date} date The selected month
10282              */
10283         'monthchange': true
10284     });
10285
10286     if(this.handler){
10287         this.on("select", this.handler,  this.scope || this);
10288     }
10289     // build the disabledDatesRE
10290     if(!this.disabledDatesRE && this.disabledDates){
10291         var dd = this.disabledDates;
10292         var re = "(?:";
10293         for(var i = 0; i < dd.length; i++){
10294             re += dd[i];
10295             if(i != dd.length-1) re += "|";
10296         }
10297         this.disabledDatesRE = new RegExp(re + ")");
10298     }
10299 };
10300
10301 Roo.extend(Roo.DatePicker, Roo.Component, {
10302     /**
10303      * @cfg {String} todayText
10304      * The text to display on the button that selects the current date (defaults to "Today")
10305      */
10306     todayText : "Today",
10307     /**
10308      * @cfg {String} okText
10309      * The text to display on the ok button
10310      */
10311     okText : "&#160;OK&#160;", // &#160; to give the user extra clicking room
10312     /**
10313      * @cfg {String} cancelText
10314      * The text to display on the cancel button
10315      */
10316     cancelText : "Cancel",
10317     /**
10318      * @cfg {String} todayTip
10319      * The tooltip to display for the button that selects the current date (defaults to "{current date} (Spacebar)")
10320      */
10321     todayTip : "{0} (Spacebar)",
10322     /**
10323      * @cfg {Date} minDate
10324      * Minimum allowable date (JavaScript date object, defaults to null)
10325      */
10326     minDate : null,
10327     /**
10328      * @cfg {Date} maxDate
10329      * Maximum allowable date (JavaScript date object, defaults to null)
10330      */
10331     maxDate : null,
10332     /**
10333      * @cfg {String} minText
10334      * The error text to display if the minDate validation fails (defaults to "This date is before the minimum date")
10335      */
10336     minText : "This date is before the minimum date",
10337     /**
10338      * @cfg {String} maxText
10339      * The error text to display if the maxDate validation fails (defaults to "This date is after the maximum date")
10340      */
10341     maxText : "This date is after the maximum date",
10342     /**
10343      * @cfg {String} format
10344      * The default date format string which can be overriden for localization support.  The format must be
10345      * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
10346      */
10347     format : "m/d/y",
10348     /**
10349      * @cfg {Array} disabledDays
10350      * An array of days to disable, 0-based. For example, [0, 6] disables Sunday and Saturday (defaults to null).
10351      */
10352     disabledDays : null,
10353     /**
10354      * @cfg {String} disabledDaysText
10355      * The tooltip to display when the date falls on a disabled day (defaults to "")
10356      */
10357     disabledDaysText : "",
10358     /**
10359      * @cfg {RegExp} disabledDatesRE
10360      * JavaScript regular expression used to disable a pattern of dates (defaults to null)
10361      */
10362     disabledDatesRE : null,
10363     /**
10364      * @cfg {String} disabledDatesText
10365      * The tooltip text to display when the date falls on a disabled date (defaults to "")
10366      */
10367     disabledDatesText : "",
10368     /**
10369      * @cfg {Boolean} constrainToViewport
10370      * True to constrain the date picker to the viewport (defaults to true)
10371      */
10372     constrainToViewport : true,
10373     /**
10374      * @cfg {Array} monthNames
10375      * An array of textual month names which can be overriden for localization support (defaults to Date.monthNames)
10376      */
10377     monthNames : Date.monthNames,
10378     /**
10379      * @cfg {Array} dayNames
10380      * An array of textual day names which can be overriden for localization support (defaults to Date.dayNames)
10381      */
10382     dayNames : Date.dayNames,
10383     /**
10384      * @cfg {String} nextText
10385      * The next month navigation button tooltip (defaults to 'Next Month (Control+Right)')
10386      */
10387     nextText: 'Next Month (Control+Right)',
10388     /**
10389      * @cfg {String} prevText
10390      * The previous month navigation button tooltip (defaults to 'Previous Month (Control+Left)')
10391      */
10392     prevText: 'Previous Month (Control+Left)',
10393     /**
10394      * @cfg {String} monthYearText
10395      * The header month selector tooltip (defaults to 'Choose a month (Control+Up/Down to move years)')
10396      */
10397     monthYearText: 'Choose a month (Control+Up/Down to move years)',
10398     /**
10399      * @cfg {Number} startDay
10400      * Day index at which the week should begin, 0-based (defaults to 0, which is Sunday)
10401      */
10402     startDay : 0,
10403     /**
10404      * @cfg {Bool} showClear
10405      * Show a clear button (usefull for date form elements that can be blank.)
10406      */
10407     
10408     showClear: false,
10409     
10410     /**
10411      * Sets the value of the date field
10412      * @param {Date} value The date to set
10413      */
10414     setValue : function(value){
10415         var old = this.value;
10416         
10417         if (typeof(value) == 'string') {
10418          
10419             value = Date.parseDate(value, this.format);
10420         }
10421         if (!value) {
10422             value = new Date();
10423         }
10424         
10425         this.value = value.clearTime(true);
10426         if(this.el){
10427             this.update(this.value);
10428         }
10429     },
10430
10431     /**
10432      * Gets the current selected value of the date field
10433      * @return {Date} The selected date
10434      */
10435     getValue : function(){
10436         return this.value;
10437     },
10438
10439     // private
10440     focus : function(){
10441         if(this.el){
10442             this.update(this.activeDate);
10443         }
10444     },
10445
10446     // privateval
10447     onRender : function(container, position){
10448         
10449         var m = [
10450              '<table cellspacing="0">',
10451                 '<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>',
10452                 '<tr><td colspan="3"><table class="x-date-inner" cellspacing="0"><thead><tr>'];
10453         var dn = this.dayNames;
10454         for(var i = 0; i < 7; i++){
10455             var d = this.startDay+i;
10456             if(d > 6){
10457                 d = d-7;
10458             }
10459             m.push("<th><span>", dn[d].substr(0,1), "</span></th>");
10460         }
10461         m[m.length] = "</tr></thead><tbody><tr>";
10462         for(var i = 0; i < 42; i++) {
10463             if(i % 7 == 0 && i != 0){
10464                 m[m.length] = "</tr><tr>";
10465             }
10466             m[m.length] = '<td><a href="#" hidefocus="on" class="x-date-date" tabIndex="1"><em><span></span></em></a></td>';
10467         }
10468         m[m.length] = '</tr></tbody></table></td></tr><tr>'+
10469             '<td colspan="3" class="x-date-bottom" align="center"></td></tr></table><div class="x-date-mp"></div>';
10470
10471         var el = document.createElement("div");
10472         el.className = "x-date-picker";
10473         el.innerHTML = m.join("");
10474
10475         container.dom.insertBefore(el, position);
10476
10477         this.el = Roo.get(el);
10478         this.eventEl = Roo.get(el.firstChild);
10479
10480         new Roo.util.ClickRepeater(this.el.child("td.x-date-left a"), {
10481             handler: this.showPrevMonth,
10482             scope: this,
10483             preventDefault:true,
10484             stopDefault:true
10485         });
10486
10487         new Roo.util.ClickRepeater(this.el.child("td.x-date-right a"), {
10488             handler: this.showNextMonth,
10489             scope: this,
10490             preventDefault:true,
10491             stopDefault:true
10492         });
10493
10494         this.eventEl.on("mousewheel", this.handleMouseWheel,  this);
10495
10496         this.monthPicker = this.el.down('div.x-date-mp');
10497         this.monthPicker.enableDisplayMode('block');
10498         
10499         var kn = new Roo.KeyNav(this.eventEl, {
10500             "left" : function(e){
10501                 e.ctrlKey ?
10502                     this.showPrevMonth() :
10503                     this.update(this.activeDate.add("d", -1));
10504             },
10505
10506             "right" : function(e){
10507                 e.ctrlKey ?
10508                     this.showNextMonth() :
10509                     this.update(this.activeDate.add("d", 1));
10510             },
10511
10512             "up" : function(e){
10513                 e.ctrlKey ?
10514                     this.showNextYear() :
10515                     this.update(this.activeDate.add("d", -7));
10516             },
10517
10518             "down" : function(e){
10519                 e.ctrlKey ?
10520                     this.showPrevYear() :
10521                     this.update(this.activeDate.add("d", 7));
10522             },
10523
10524             "pageUp" : function(e){
10525                 this.showNextMonth();
10526             },
10527
10528             "pageDown" : function(e){
10529                 this.showPrevMonth();
10530             },
10531
10532             "enter" : function(e){
10533                 e.stopPropagation();
10534                 return true;
10535             },
10536
10537             scope : this
10538         });
10539
10540         this.eventEl.on("click", this.handleDateClick,  this, {delegate: "a.x-date-date"});
10541
10542         this.eventEl.addKeyListener(Roo.EventObject.SPACE, this.selectToday,  this);
10543
10544         this.el.unselectable();
10545         
10546         this.cells = this.el.select("table.x-date-inner tbody td");
10547         this.textNodes = this.el.query("table.x-date-inner tbody span");
10548
10549         this.mbtn = new Roo.Button(this.el.child("td.x-date-middle", true), {
10550             text: "&#160;",
10551             tooltip: this.monthYearText
10552         });
10553
10554         this.mbtn.on('click', this.showMonthPicker, this);
10555         this.mbtn.el.child(this.mbtn.menuClassTarget).addClass("x-btn-with-menu");
10556
10557
10558         var today = (new Date()).dateFormat(this.format);
10559         
10560         var baseTb = new Roo.Toolbar(this.el.child("td.x-date-bottom", true));
10561         if (this.showClear) {
10562             baseTb.add( new Roo.Toolbar.Fill());
10563         }
10564         baseTb.add({
10565             text: String.format(this.todayText, today),
10566             tooltip: String.format(this.todayTip, today),
10567             handler: this.selectToday,
10568             scope: this
10569         });
10570         
10571         //var todayBtn = new Roo.Button(this.el.child("td.x-date-bottom", true), {
10572             
10573         //});
10574         if (this.showClear) {
10575             
10576             baseTb.add( new Roo.Toolbar.Fill());
10577             baseTb.add({
10578                 text: '&#160;',
10579                 cls: 'x-btn-icon x-btn-clear',
10580                 handler: function() {
10581                     //this.value = '';
10582                     this.fireEvent("select", this, '');
10583                 },
10584                 scope: this
10585             });
10586         }
10587         
10588         
10589         if(Roo.isIE){
10590             this.el.repaint();
10591         }
10592         this.update(this.value);
10593     },
10594
10595     createMonthPicker : function(){
10596         if(!this.monthPicker.dom.firstChild){
10597             var buf = ['<table border="0" cellspacing="0">'];
10598             for(var i = 0; i < 6; i++){
10599                 buf.push(
10600                     '<tr><td class="x-date-mp-month"><a href="#">', this.monthNames[i].substr(0, 3), '</a></td>',
10601                     '<td class="x-date-mp-month x-date-mp-sep"><a href="#">', this.monthNames[i+6].substr(0, 3), '</a></td>',
10602                     i == 0 ?
10603                     '<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>' :
10604                     '<td class="x-date-mp-year"><a href="#"></a></td><td class="x-date-mp-year"><a href="#"></a></td></tr>'
10605                 );
10606             }
10607             buf.push(
10608                 '<tr class="x-date-mp-btns"><td colspan="4"><button type="button" class="x-date-mp-ok">',
10609                     this.okText,
10610                     '</button><button type="button" class="x-date-mp-cancel">',
10611                     this.cancelText,
10612                     '</button></td></tr>',
10613                 '</table>'
10614             );
10615             this.monthPicker.update(buf.join(''));
10616             this.monthPicker.on('click', this.onMonthClick, this);
10617             this.monthPicker.on('dblclick', this.onMonthDblClick, this);
10618
10619             this.mpMonths = this.monthPicker.select('td.x-date-mp-month');
10620             this.mpYears = this.monthPicker.select('td.x-date-mp-year');
10621
10622             this.mpMonths.each(function(m, a, i){
10623                 i += 1;
10624                 if((i%2) == 0){
10625                     m.dom.xmonth = 5 + Math.round(i * .5);
10626                 }else{
10627                     m.dom.xmonth = Math.round((i-1) * .5);
10628                 }
10629             });
10630         }
10631     },
10632
10633     showMonthPicker : function(){
10634         this.createMonthPicker();
10635         var size = this.el.getSize();
10636         this.monthPicker.setSize(size);
10637         this.monthPicker.child('table').setSize(size);
10638
10639         this.mpSelMonth = (this.activeDate || this.value).getMonth();
10640         this.updateMPMonth(this.mpSelMonth);
10641         this.mpSelYear = (this.activeDate || this.value).getFullYear();
10642         this.updateMPYear(this.mpSelYear);
10643
10644         this.monthPicker.slideIn('t', {duration:.2});
10645     },
10646
10647     updateMPYear : function(y){
10648         this.mpyear = y;
10649         var ys = this.mpYears.elements;
10650         for(var i = 1; i <= 10; i++){
10651             var td = ys[i-1], y2;
10652             if((i%2) == 0){
10653                 y2 = y + Math.round(i * .5);
10654                 td.firstChild.innerHTML = y2;
10655                 td.xyear = y2;
10656             }else{
10657                 y2 = y - (5-Math.round(i * .5));
10658                 td.firstChild.innerHTML = y2;
10659                 td.xyear = y2;
10660             }
10661             this.mpYears.item(i-1)[y2 == this.mpSelYear ? 'addClass' : 'removeClass']('x-date-mp-sel');
10662         }
10663     },
10664
10665     updateMPMonth : function(sm){
10666         this.mpMonths.each(function(m, a, i){
10667             m[m.dom.xmonth == sm ? 'addClass' : 'removeClass']('x-date-mp-sel');
10668         });
10669     },
10670
10671     selectMPMonth: function(m){
10672         
10673     },
10674
10675     onMonthClick : function(e, t){
10676         e.stopEvent();
10677         var el = new Roo.Element(t), pn;
10678         if(el.is('button.x-date-mp-cancel')){
10679             this.hideMonthPicker();
10680         }
10681         else if(el.is('button.x-date-mp-ok')){
10682             this.update(new Date(this.mpSelYear, this.mpSelMonth, (this.activeDate || this.value).getDate()));
10683             this.hideMonthPicker();
10684         }
10685         else if(pn = el.up('td.x-date-mp-month', 2)){
10686             this.mpMonths.removeClass('x-date-mp-sel');
10687             pn.addClass('x-date-mp-sel');
10688             this.mpSelMonth = pn.dom.xmonth;
10689         }
10690         else if(pn = el.up('td.x-date-mp-year', 2)){
10691             this.mpYears.removeClass('x-date-mp-sel');
10692             pn.addClass('x-date-mp-sel');
10693             this.mpSelYear = pn.dom.xyear;
10694         }
10695         else if(el.is('a.x-date-mp-prev')){
10696             this.updateMPYear(this.mpyear-10);
10697         }
10698         else if(el.is('a.x-date-mp-next')){
10699             this.updateMPYear(this.mpyear+10);
10700         }
10701     },
10702
10703     onMonthDblClick : function(e, t){
10704         e.stopEvent();
10705         var el = new Roo.Element(t), pn;
10706         if(pn = el.up('td.x-date-mp-month', 2)){
10707             this.update(new Date(this.mpSelYear, pn.dom.xmonth, (this.activeDate || this.value).getDate()));
10708             this.hideMonthPicker();
10709         }
10710         else if(pn = el.up('td.x-date-mp-year', 2)){
10711             this.update(new Date(pn.dom.xyear, this.mpSelMonth, (this.activeDate || this.value).getDate()));
10712             this.hideMonthPicker();
10713         }
10714     },
10715
10716     hideMonthPicker : function(disableAnim){
10717         if(this.monthPicker){
10718             if(disableAnim === true){
10719                 this.monthPicker.hide();
10720             }else{
10721                 this.monthPicker.slideOut('t', {duration:.2});
10722             }
10723         }
10724     },
10725
10726     // private
10727     showPrevMonth : function(e){
10728         this.update(this.activeDate.add("mo", -1));
10729     },
10730
10731     // private
10732     showNextMonth : function(e){
10733         this.update(this.activeDate.add("mo", 1));
10734     },
10735
10736     // private
10737     showPrevYear : function(){
10738         this.update(this.activeDate.add("y", -1));
10739     },
10740
10741     // private
10742     showNextYear : function(){
10743         this.update(this.activeDate.add("y", 1));
10744     },
10745
10746     // private
10747     handleMouseWheel : function(e){
10748         var delta = e.getWheelDelta();
10749         if(delta > 0){
10750             this.showPrevMonth();
10751             e.stopEvent();
10752         } else if(delta < 0){
10753             this.showNextMonth();
10754             e.stopEvent();
10755         }
10756     },
10757
10758     // private
10759     handleDateClick : function(e, t){
10760         e.stopEvent();
10761         if(t.dateValue && !Roo.fly(t.parentNode).hasClass("x-date-disabled")){
10762             this.setValue(new Date(t.dateValue));
10763             this.fireEvent("select", this, this.value);
10764         }
10765     },
10766
10767     // private
10768     selectToday : function(){
10769         this.setValue(new Date().clearTime());
10770         this.fireEvent("select", this, this.value);
10771     },
10772
10773     // private
10774     update : function(date)
10775     {
10776         var vd = this.activeDate;
10777         this.activeDate = date;
10778         if(vd && this.el){
10779             var t = date.getTime();
10780             if(vd.getMonth() == date.getMonth() && vd.getFullYear() == date.getFullYear()){
10781                 this.cells.removeClass("x-date-selected");
10782                 this.cells.each(function(c){
10783                    if(c.dom.firstChild.dateValue == t){
10784                        c.addClass("x-date-selected");
10785                        setTimeout(function(){
10786                             try{c.dom.firstChild.focus();}catch(e){}
10787                        }, 50);
10788                        return false;
10789                    }
10790                 });
10791                 return;
10792             }
10793         }
10794         
10795         var days = date.getDaysInMonth();
10796         var firstOfMonth = date.getFirstDateOfMonth();
10797         var startingPos = firstOfMonth.getDay()-this.startDay;
10798
10799         if(startingPos <= this.startDay){
10800             startingPos += 7;
10801         }
10802
10803         var pm = date.add("mo", -1);
10804         var prevStart = pm.getDaysInMonth()-startingPos;
10805
10806         var cells = this.cells.elements;
10807         var textEls = this.textNodes;
10808         days += startingPos;
10809
10810         // convert everything to numbers so it's fast
10811         var day = 86400000;
10812         var d = (new Date(pm.getFullYear(), pm.getMonth(), prevStart)).clearTime();
10813         var today = new Date().clearTime().getTime();
10814         var sel = date.clearTime().getTime();
10815         var min = this.minDate ? this.minDate.clearTime() : Number.NEGATIVE_INFINITY;
10816         var max = this.maxDate ? this.maxDate.clearTime() : Number.POSITIVE_INFINITY;
10817         var ddMatch = this.disabledDatesRE;
10818         var ddText = this.disabledDatesText;
10819         var ddays = this.disabledDays ? this.disabledDays.join("") : false;
10820         var ddaysText = this.disabledDaysText;
10821         var format = this.format;
10822
10823         var setCellClass = function(cal, cell){
10824             cell.title = "";
10825             var t = d.getTime();
10826             cell.firstChild.dateValue = t;
10827             if(t == today){
10828                 cell.className += " x-date-today";
10829                 cell.title = cal.todayText;
10830             }
10831             if(t == sel){
10832                 cell.className += " x-date-selected";
10833                 setTimeout(function(){
10834                     try{cell.firstChild.focus();}catch(e){}
10835                 }, 50);
10836             }
10837             // disabling
10838             if(t < min) {
10839                 cell.className = " x-date-disabled";
10840                 cell.title = cal.minText;
10841                 return;
10842             }
10843             if(t > max) {
10844                 cell.className = " x-date-disabled";
10845                 cell.title = cal.maxText;
10846                 return;
10847             }
10848             if(ddays){
10849                 if(ddays.indexOf(d.getDay()) != -1){
10850                     cell.title = ddaysText;
10851                     cell.className = " x-date-disabled";
10852                 }
10853             }
10854             if(ddMatch && format){
10855                 var fvalue = d.dateFormat(format);
10856                 if(ddMatch.test(fvalue)){
10857                     cell.title = ddText.replace("%0", fvalue);
10858                     cell.className = " x-date-disabled";
10859                 }
10860             }
10861         };
10862
10863         var i = 0;
10864         for(; i < startingPos; i++) {
10865             textEls[i].innerHTML = (++prevStart);
10866             d.setDate(d.getDate()+1);
10867             cells[i].className = "x-date-prevday";
10868             setCellClass(this, cells[i]);
10869         }
10870         for(; i < days; i++){
10871             intDay = i - startingPos + 1;
10872             textEls[i].innerHTML = (intDay);
10873             d.setDate(d.getDate()+1);
10874             cells[i].className = "x-date-active";
10875             setCellClass(this, cells[i]);
10876         }
10877         var extraDays = 0;
10878         for(; i < 42; i++) {
10879              textEls[i].innerHTML = (++extraDays);
10880              d.setDate(d.getDate()+1);
10881              cells[i].className = "x-date-nextday";
10882              setCellClass(this, cells[i]);
10883         }
10884
10885         this.mbtn.setText(this.monthNames[date.getMonth()] + " " + date.getFullYear());
10886         this.fireEvent('monthchange', this, date);
10887         
10888         if(!this.internalRender){
10889             var main = this.el.dom.firstChild;
10890             var w = main.offsetWidth;
10891             this.el.setWidth(w + this.el.getBorderWidth("lr"));
10892             Roo.fly(main).setWidth(w);
10893             this.internalRender = true;
10894             // opera does not respect the auto grow header center column
10895             // then, after it gets a width opera refuses to recalculate
10896             // without a second pass
10897             if(Roo.isOpera && !this.secondPass){
10898                 main.rows[0].cells[1].style.width = (w - (main.rows[0].cells[0].offsetWidth+main.rows[0].cells[2].offsetWidth)) + "px";
10899                 this.secondPass = true;
10900                 this.update.defer(10, this, [date]);
10901             }
10902         }
10903         
10904         
10905     }
10906 });        /*
10907  * Based on:
10908  * Ext JS Library 1.1.1
10909  * Copyright(c) 2006-2007, Ext JS, LLC.
10910  *
10911  * Originally Released Under LGPL - original licence link has changed is not relivant.
10912  *
10913  * Fork - LGPL
10914  * <script type="text/javascript">
10915  */
10916 /**
10917  * @class Roo.TabPanel
10918  * @extends Roo.util.Observable
10919  * A lightweight tab container.
10920  * <br><br>
10921  * Usage:
10922  * <pre><code>
10923 // basic tabs 1, built from existing content
10924 var tabs = new Roo.TabPanel("tabs1");
10925 tabs.addTab("script", "View Script");
10926 tabs.addTab("markup", "View Markup");
10927 tabs.activate("script");
10928
10929 // more advanced tabs, built from javascript
10930 var jtabs = new Roo.TabPanel("jtabs");
10931 jtabs.addTab("jtabs-1", "Normal Tab", "My content was added during construction.");
10932
10933 // set up the UpdateManager
10934 var tab2 = jtabs.addTab("jtabs-2", "Ajax Tab 1");
10935 var updater = tab2.getUpdateManager();
10936 updater.setDefaultUrl("ajax1.htm");
10937 tab2.on('activate', updater.refresh, updater, true);
10938
10939 // Use setUrl for Ajax loading
10940 var tab3 = jtabs.addTab("jtabs-3", "Ajax Tab 2");
10941 tab3.setUrl("ajax2.htm", null, true);
10942
10943 // Disabled tab
10944 var tab4 = jtabs.addTab("tabs1-5", "Disabled Tab", "Can't see me cause I'm disabled");
10945 tab4.disable();
10946
10947 jtabs.activate("jtabs-1");
10948  * </code></pre>
10949  * @constructor
10950  * Create a new TabPanel.
10951  * @param {String/HTMLElement/Roo.Element} container The id, DOM element or Roo.Element container where this TabPanel is to be rendered.
10952  * @param {Object/Boolean} config Config object to set any properties for this TabPanel, or true to render the tabs on the bottom.
10953  */
10954 Roo.TabPanel = function(container, config){
10955     /**
10956     * The container element for this TabPanel.
10957     * @type Roo.Element
10958     */
10959     this.el = Roo.get(container, true);
10960     if(config){
10961         if(typeof config == "boolean"){
10962             this.tabPosition = config ? "bottom" : "top";
10963         }else{
10964             Roo.apply(this, config);
10965         }
10966     }
10967     if(this.tabPosition == "bottom"){
10968         this.bodyEl = Roo.get(this.createBody(this.el.dom));
10969         this.el.addClass("x-tabs-bottom");
10970     }
10971     this.stripWrap = Roo.get(this.createStrip(this.el.dom), true);
10972     this.stripEl = Roo.get(this.createStripList(this.stripWrap.dom), true);
10973     this.stripBody = Roo.get(this.stripWrap.dom.firstChild.firstChild, true);
10974     if(Roo.isIE){
10975         Roo.fly(this.stripWrap.dom.firstChild).setStyle("overflow-x", "hidden");
10976     }
10977     if(this.tabPosition != "bottom"){
10978         /** The body element that contains {@link Roo.TabPanelItem} bodies. +
10979          * @type Roo.Element
10980          */
10981         this.bodyEl = Roo.get(this.createBody(this.el.dom));
10982         this.el.addClass("x-tabs-top");
10983     }
10984     this.items = [];
10985
10986     this.bodyEl.setStyle("position", "relative");
10987
10988     this.active = null;
10989     this.activateDelegate = this.activate.createDelegate(this);
10990
10991     this.addEvents({
10992         /**
10993          * @event tabchange
10994          * Fires when the active tab changes
10995          * @param {Roo.TabPanel} this
10996          * @param {Roo.TabPanelItem} activePanel The new active tab
10997          */
10998         "tabchange": true,
10999         /**
11000          * @event beforetabchange
11001          * Fires before the active tab changes, set cancel to true on the "e" parameter to cancel the change
11002          * @param {Roo.TabPanel} this
11003          * @param {Object} e Set cancel to true on this object to cancel the tab change
11004          * @param {Roo.TabPanelItem} tab The tab being changed to
11005          */
11006         "beforetabchange" : true
11007     });
11008
11009     Roo.EventManager.onWindowResize(this.onResize, this);
11010     this.cpad = this.el.getPadding("lr");
11011     this.hiddenCount = 0;
11012
11013
11014     // toolbar on the tabbar support...
11015     if (this.toolbar) {
11016         var tcfg = this.toolbar;
11017         tcfg.container = this.stripEl.child('td.x-tab-strip-toolbar');  
11018         this.toolbar = new Roo.Toolbar(tcfg);
11019         if (Roo.isSafari) {
11020             var tbl = tcfg.container.child('table', true);
11021             tbl.setAttribute('width', '100%');
11022         }
11023         
11024     }
11025    
11026
11027
11028     Roo.TabPanel.superclass.constructor.call(this);
11029 };
11030
11031 Roo.extend(Roo.TabPanel, Roo.util.Observable, {
11032     /*
11033      *@cfg {String} tabPosition "top" or "bottom" (defaults to "top")
11034      */
11035     tabPosition : "top",
11036     /*
11037      *@cfg {Number} currentTabWidth The width of the current tab (defaults to 0)
11038      */
11039     currentTabWidth : 0,
11040     /*
11041      *@cfg {Number} minTabWidth The minimum width of a tab (defaults to 40) (ignored if {@link #resizeTabs} is not true)
11042      */
11043     minTabWidth : 40,
11044     /*
11045      *@cfg {Number} maxTabWidth The maximum width of a tab (defaults to 250) (ignored if {@link #resizeTabs} is not true)
11046      */
11047     maxTabWidth : 250,
11048     /*
11049      *@cfg {Number} preferredTabWidth The preferred (default) width of a tab (defaults to 175) (ignored if {@link #resizeTabs} is not true)
11050      */
11051     preferredTabWidth : 175,
11052     /*
11053      *@cfg {Boolean} resizeTabs True to enable dynamic tab resizing (defaults to false)
11054      */
11055     resizeTabs : false,
11056     /*
11057      *@cfg {Boolean} monitorResize Set this to true to turn on window resize monitoring (ignored if {@link #resizeTabs} is not true) (defaults to true)
11058      */
11059     monitorResize : true,
11060     /*
11061      *@cfg {Object} toolbar xtype description of toolbar to show at the right of the tab bar. 
11062      */
11063     toolbar : false,
11064
11065     /**
11066      * Creates a new {@link Roo.TabPanelItem} by looking for an existing element with the provided id -- if it's not found it creates one.
11067      * @param {String} id The id of the div to use <b>or create</b>
11068      * @param {String} text The text for the tab
11069      * @param {String} content (optional) Content to put in the TabPanelItem body
11070      * @param {Boolean} closable (optional) True to create a close icon on the tab
11071      * @return {Roo.TabPanelItem} The created TabPanelItem
11072      */
11073     addTab : function(id, text, content, closable){
11074         var item = new Roo.TabPanelItem(this, id, text, closable);
11075         this.addTabItem(item);
11076         if(content){
11077             item.setContent(content);
11078         }
11079         return item;
11080     },
11081
11082     /**
11083      * Returns the {@link Roo.TabPanelItem} with the specified id/index
11084      * @param {String/Number} id The id or index of the TabPanelItem to fetch.
11085      * @return {Roo.TabPanelItem}
11086      */
11087     getTab : function(id){
11088         return this.items[id];
11089     },
11090
11091     /**
11092      * Hides the {@link Roo.TabPanelItem} with the specified id/index
11093      * @param {String/Number} id The id or index of the TabPanelItem to hide.
11094      */
11095     hideTab : function(id){
11096         var t = this.items[id];
11097         if(!t.isHidden()){
11098            t.setHidden(true);
11099            this.hiddenCount++;
11100            this.autoSizeTabs();
11101         }
11102     },
11103
11104     /**
11105      * "Unhides" the {@link Roo.TabPanelItem} with the specified id/index.
11106      * @param {String/Number} id The id or index of the TabPanelItem to unhide.
11107      */
11108     unhideTab : function(id){
11109         var t = this.items[id];
11110         if(t.isHidden()){
11111            t.setHidden(false);
11112            this.hiddenCount--;
11113            this.autoSizeTabs();
11114         }
11115     },
11116
11117     /**
11118      * Adds an existing {@link Roo.TabPanelItem}.
11119      * @param {Roo.TabPanelItem} item The TabPanelItem to add
11120      */
11121     addTabItem : function(item){
11122         this.items[item.id] = item;
11123         this.items.push(item);
11124         if(this.resizeTabs){
11125            item.setWidth(this.currentTabWidth || this.preferredTabWidth);
11126            this.autoSizeTabs();
11127         }else{
11128             item.autoSize();
11129         }
11130     },
11131
11132     /**
11133      * Removes a {@link Roo.TabPanelItem}.
11134      * @param {String/Number} id The id or index of the TabPanelItem to remove.
11135      */
11136     removeTab : function(id){
11137         var items = this.items;
11138         var tab = items[id];
11139         if(!tab) { return; }
11140         var index = items.indexOf(tab);
11141         if(this.active == tab && items.length > 1){
11142             var newTab = this.getNextAvailable(index);
11143             if(newTab) {
11144                 newTab.activate();
11145             }
11146         }
11147         this.stripEl.dom.removeChild(tab.pnode.dom);
11148         if(tab.bodyEl.dom.parentNode == this.bodyEl.dom){ // if it was moved already prevent error
11149             this.bodyEl.dom.removeChild(tab.bodyEl.dom);
11150         }
11151         items.splice(index, 1);
11152         delete this.items[tab.id];
11153         tab.fireEvent("close", tab);
11154         tab.purgeListeners();
11155         this.autoSizeTabs();
11156     },
11157
11158     getNextAvailable : function(start){
11159         var items = this.items;
11160         var index = start;
11161         // look for a next tab that will slide over to
11162         // replace the one being removed
11163         while(index < items.length){
11164             var item = items[++index];
11165             if(item && !item.isHidden()){
11166                 return item;
11167             }
11168         }
11169         // if one isn't found select the previous tab (on the left)
11170         index = start;
11171         while(index >= 0){
11172             var item = items[--index];
11173             if(item && !item.isHidden()){
11174                 return item;
11175             }
11176         }
11177         return null;
11178     },
11179
11180     /**
11181      * Disables a {@link Roo.TabPanelItem}. It cannot be the active tab, if it is this call is ignored.
11182      * @param {String/Number} id The id or index of the TabPanelItem to disable.
11183      */
11184     disableTab : function(id){
11185         var tab = this.items[id];
11186         if(tab && this.active != tab){
11187             tab.disable();
11188         }
11189     },
11190
11191     /**
11192      * Enables a {@link Roo.TabPanelItem} that is disabled.
11193      * @param {String/Number} id The id or index of the TabPanelItem to enable.
11194      */
11195     enableTab : function(id){
11196         var tab = this.items[id];
11197         tab.enable();
11198     },
11199
11200     /**
11201      * Activates a {@link Roo.TabPanelItem}. The currently active one will be deactivated.
11202      * @param {String/Number} id The id or index of the TabPanelItem to activate.
11203      * @return {Roo.TabPanelItem} The TabPanelItem.
11204      */
11205     activate : function(id){
11206         var tab = this.items[id];
11207         if(!tab){
11208             return null;
11209         }
11210         if(tab == this.active || tab.disabled){
11211             return tab;
11212         }
11213         var e = {};
11214         this.fireEvent("beforetabchange", this, e, tab);
11215         if(e.cancel !== true && !tab.disabled){
11216             if(this.active){
11217                 this.active.hide();
11218             }
11219             this.active = this.items[id];
11220             this.active.show();
11221             this.fireEvent("tabchange", this, this.active);
11222         }
11223         return tab;
11224     },
11225
11226     /**
11227      * Gets the active {@link Roo.TabPanelItem}.
11228      * @return {Roo.TabPanelItem} The active TabPanelItem or null if none are active.
11229      */
11230     getActiveTab : function(){
11231         return this.active;
11232     },
11233
11234     /**
11235      * Updates the tab body element to fit the height of the container element
11236      * for overflow scrolling
11237      * @param {Number} targetHeight (optional) Override the starting height from the elements height
11238      */
11239     syncHeight : function(targetHeight){
11240         var height = (targetHeight || this.el.getHeight())-this.el.getBorderWidth("tb")-this.el.getPadding("tb");
11241         var bm = this.bodyEl.getMargins();
11242         var newHeight = height-(this.stripWrap.getHeight()||0)-(bm.top+bm.bottom);
11243         this.bodyEl.setHeight(newHeight);
11244         return newHeight;
11245     },
11246
11247     onResize : function(){
11248         if(this.monitorResize){
11249             this.autoSizeTabs();
11250         }
11251     },
11252
11253     /**
11254      * Disables tab resizing while tabs are being added (if {@link #resizeTabs} is false this does nothing)
11255      */
11256     beginUpdate : function(){
11257         this.updating = true;
11258     },
11259
11260     /**
11261      * Stops an update and resizes the tabs (if {@link #resizeTabs} is false this does nothing)
11262      */
11263     endUpdate : function(){
11264         this.updating = false;
11265         this.autoSizeTabs();
11266     },
11267
11268     /**
11269      * Manual call to resize the tabs (if {@link #resizeTabs} is false this does nothing)
11270      */
11271     autoSizeTabs : function(){
11272         var count = this.items.length;
11273         var vcount = count - this.hiddenCount;
11274         if(!this.resizeTabs || count < 1 || vcount < 1 || this.updating) return;
11275         var w = Math.max(this.el.getWidth() - this.cpad, 10);
11276         var availWidth = Math.floor(w / vcount);
11277         var b = this.stripBody;
11278         if(b.getWidth() > w){
11279             var tabs = this.items;
11280             this.setTabWidth(Math.max(availWidth, this.minTabWidth)-2);
11281             if(availWidth < this.minTabWidth){
11282                 /*if(!this.sleft){    // incomplete scrolling code
11283                     this.createScrollButtons();
11284                 }
11285                 this.showScroll();
11286                 this.stripClip.setWidth(w - (this.sleft.getWidth()+this.sright.getWidth()));*/
11287             }
11288         }else{
11289             if(this.currentTabWidth < this.preferredTabWidth){
11290                 this.setTabWidth(Math.min(availWidth, this.preferredTabWidth)-2);
11291             }
11292         }
11293     },
11294
11295     /**
11296      * Returns the number of tabs in this TabPanel.
11297      * @return {Number}
11298      */
11299      getCount : function(){
11300          return this.items.length;
11301      },
11302
11303     /**
11304      * Resizes all the tabs to the passed width
11305      * @param {Number} The new width
11306      */
11307     setTabWidth : function(width){
11308         this.currentTabWidth = width;
11309         for(var i = 0, len = this.items.length; i < len; i++) {
11310                 if(!this.items[i].isHidden())this.items[i].setWidth(width);
11311         }
11312     },
11313
11314     /**
11315      * Destroys this TabPanel
11316      * @param {Boolean} removeEl (optional) True to remove the element from the DOM as well (defaults to undefined)
11317      */
11318     destroy : function(removeEl){
11319         Roo.EventManager.removeResizeListener(this.onResize, this);
11320         for(var i = 0, len = this.items.length; i < len; i++){
11321             this.items[i].purgeListeners();
11322         }
11323         if(removeEl === true){
11324             this.el.update("");
11325             this.el.remove();
11326         }
11327     }
11328 });
11329
11330 /**
11331  * @class Roo.TabPanelItem
11332  * @extends Roo.util.Observable
11333  * Represents an individual item (tab plus body) in a TabPanel.
11334  * @param {Roo.TabPanel} tabPanel The {@link Roo.TabPanel} this TabPanelItem belongs to
11335  * @param {String} id The id of this TabPanelItem
11336  * @param {String} text The text for the tab of this TabPanelItem
11337  * @param {Boolean} closable True to allow this TabPanelItem to be closable (defaults to false)
11338  */
11339 Roo.TabPanelItem = function(tabPanel, id, text, closable){
11340     /**
11341      * The {@link Roo.TabPanel} this TabPanelItem belongs to
11342      * @type Roo.TabPanel
11343      */
11344     this.tabPanel = tabPanel;
11345     /**
11346      * The id for this TabPanelItem
11347      * @type String
11348      */
11349     this.id = id;
11350     /** @private */
11351     this.disabled = false;
11352     /** @private */
11353     this.text = text;
11354     /** @private */
11355     this.loaded = false;
11356     this.closable = closable;
11357
11358     /**
11359      * The body element for this TabPanelItem.
11360      * @type Roo.Element
11361      */
11362     this.bodyEl = Roo.get(tabPanel.createItemBody(tabPanel.bodyEl.dom, id));
11363     this.bodyEl.setVisibilityMode(Roo.Element.VISIBILITY);
11364     this.bodyEl.setStyle("display", "block");
11365     this.bodyEl.setStyle("zoom", "1");
11366     this.hideAction();
11367
11368     var els = tabPanel.createStripElements(tabPanel.stripEl.dom, text, closable);
11369     /** @private */
11370     this.el = Roo.get(els.el, true);
11371     this.inner = Roo.get(els.inner, true);
11372     this.textEl = Roo.get(this.el.dom.firstChild.firstChild.firstChild, true);
11373     this.pnode = Roo.get(els.el.parentNode, true);
11374     this.el.on("mousedown", this.onTabMouseDown, this);
11375     this.el.on("click", this.onTabClick, this);
11376     /** @private */
11377     if(closable){
11378         var c = Roo.get(els.close, true);
11379         c.dom.title = this.closeText;
11380         c.addClassOnOver("close-over");
11381         c.on("click", this.closeClick, this);
11382      }
11383
11384     this.addEvents({
11385          /**
11386          * @event activate
11387          * Fires when this tab becomes the active tab.
11388          * @param {Roo.TabPanel} tabPanel The parent TabPanel
11389          * @param {Roo.TabPanelItem} this
11390          */
11391         "activate": true,
11392         /**
11393          * @event beforeclose
11394          * Fires before this tab is closed. To cancel the close, set cancel to true on e (e.cancel = true).
11395          * @param {Roo.TabPanelItem} this
11396          * @param {Object} e Set cancel to true on this object to cancel the close.
11397          */
11398         "beforeclose": true,
11399         /**
11400          * @event close
11401          * Fires when this tab is closed.
11402          * @param {Roo.TabPanelItem} this
11403          */
11404          "close": true,
11405         /**
11406          * @event deactivate
11407          * Fires when this tab is no longer the active tab.
11408          * @param {Roo.TabPanel} tabPanel The parent TabPanel
11409          * @param {Roo.TabPanelItem} this
11410          */
11411          "deactivate" : true
11412     });
11413     this.hidden = false;
11414
11415     Roo.TabPanelItem.superclass.constructor.call(this);
11416 };
11417
11418 Roo.extend(Roo.TabPanelItem, Roo.util.Observable, {
11419     purgeListeners : function(){
11420        Roo.util.Observable.prototype.purgeListeners.call(this);
11421        this.el.removeAllListeners();
11422     },
11423     /**
11424      * Shows this TabPanelItem -- this <b>does not</b> deactivate the currently active TabPanelItem.
11425      */
11426     show : function(){
11427         this.pnode.addClass("on");
11428         this.showAction();
11429         if(Roo.isOpera){
11430             this.tabPanel.stripWrap.repaint();
11431         }
11432         this.fireEvent("activate", this.tabPanel, this);
11433     },
11434
11435     /**
11436      * Returns true if this tab is the active tab.
11437      * @return {Boolean}
11438      */
11439     isActive : function(){
11440         return this.tabPanel.getActiveTab() == this;
11441     },
11442
11443     /**
11444      * Hides this TabPanelItem -- if you don't activate another TabPanelItem this could look odd.
11445      */
11446     hide : function(){
11447         this.pnode.removeClass("on");
11448         this.hideAction();
11449         this.fireEvent("deactivate", this.tabPanel, this);
11450     },
11451
11452     hideAction : function(){
11453         this.bodyEl.hide();
11454         this.bodyEl.setStyle("position", "absolute");
11455         this.bodyEl.setLeft("-20000px");
11456         this.bodyEl.setTop("-20000px");
11457     },
11458
11459     showAction : function(){
11460         this.bodyEl.setStyle("position", "relative");
11461         this.bodyEl.setTop("");
11462         this.bodyEl.setLeft("");
11463         this.bodyEl.show();
11464     },
11465
11466     /**
11467      * Set the tooltip for the tab.
11468      * @param {String} tooltip The tab's tooltip
11469      */
11470     setTooltip : function(text){
11471         if(Roo.QuickTips && Roo.QuickTips.isEnabled()){
11472             this.textEl.dom.qtip = text;
11473             this.textEl.dom.removeAttribute('title');
11474         }else{
11475             this.textEl.dom.title = text;
11476         }
11477     },
11478
11479     onTabClick : function(e){
11480         e.preventDefault();
11481         this.tabPanel.activate(this.id);
11482     },
11483
11484     onTabMouseDown : function(e){
11485         e.preventDefault();
11486         this.tabPanel.activate(this.id);
11487     },
11488
11489     getWidth : function(){
11490         return this.inner.getWidth();
11491     },
11492
11493     setWidth : function(width){
11494         var iwidth = width - this.pnode.getPadding("lr");
11495         this.inner.setWidth(iwidth);
11496         this.textEl.setWidth(iwidth-this.inner.getPadding("lr"));
11497         this.pnode.setWidth(width);
11498     },
11499
11500     /**
11501      * Show or hide the tab
11502      * @param {Boolean} hidden True to hide or false to show.
11503      */
11504     setHidden : function(hidden){
11505         this.hidden = hidden;
11506         this.pnode.setStyle("display", hidden ? "none" : "");
11507     },
11508
11509     /**
11510      * Returns true if this tab is "hidden"
11511      * @return {Boolean}
11512      */
11513     isHidden : function(){
11514         return this.hidden;
11515     },
11516
11517     /**
11518      * Returns the text for this tab
11519      * @return {String}
11520      */
11521     getText : function(){
11522         return this.text;
11523     },
11524
11525     autoSize : function(){
11526         //this.el.beginMeasure();
11527         this.textEl.setWidth(1);
11528         this.setWidth(this.textEl.dom.scrollWidth+this.pnode.getPadding("lr")+this.inner.getPadding("lr"));
11529         //this.el.endMeasure();
11530     },
11531
11532     /**
11533      * Sets the text for the tab (Note: this also sets the tooltip text)
11534      * @param {String} text The tab's text and tooltip
11535      */
11536     setText : function(text){
11537         this.text = text;
11538         this.textEl.update(text);
11539         this.setTooltip(text);
11540         if(!this.tabPanel.resizeTabs){
11541             this.autoSize();
11542         }
11543     },
11544     /**
11545      * Activates this TabPanelItem -- this <b>does</b> deactivate the currently active TabPanelItem.
11546      */
11547     activate : function(){
11548         this.tabPanel.activate(this.id);
11549     },
11550
11551     /**
11552      * Disables this TabPanelItem -- this does nothing if this is the active TabPanelItem.
11553      */
11554     disable : function(){
11555         if(this.tabPanel.active != this){
11556             this.disabled = true;
11557             this.pnode.addClass("disabled");
11558         }
11559     },
11560
11561     /**
11562      * Enables this TabPanelItem if it was previously disabled.
11563      */
11564     enable : function(){
11565         this.disabled = false;
11566         this.pnode.removeClass("disabled");
11567     },
11568
11569     /**
11570      * Sets the content for this TabPanelItem.
11571      * @param {String} content The content
11572      * @param {Boolean} loadScripts true to look for and load scripts
11573      */
11574     setContent : function(content, loadScripts){
11575         this.bodyEl.update(content, loadScripts);
11576     },
11577
11578     /**
11579      * Gets the {@link Roo.UpdateManager} for the body of this TabPanelItem. Enables you to perform Ajax updates.
11580      * @return {Roo.UpdateManager} The UpdateManager
11581      */
11582     getUpdateManager : function(){
11583         return this.bodyEl.getUpdateManager();
11584     },
11585
11586     /**
11587      * Set a URL to be used to load the content for this TabPanelItem.
11588      * @param {String/Function} url The URL to load the content from, or a function to call to get the URL
11589      * @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)
11590      * @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)
11591      * @return {Roo.UpdateManager} The UpdateManager
11592      */
11593     setUrl : function(url, params, loadOnce){
11594         if(this.refreshDelegate){
11595             this.un('activate', this.refreshDelegate);
11596         }
11597         this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
11598         this.on("activate", this.refreshDelegate);
11599         return this.bodyEl.getUpdateManager();
11600     },
11601
11602     /** @private */
11603     _handleRefresh : function(url, params, loadOnce){
11604         if(!loadOnce || !this.loaded){
11605             var updater = this.bodyEl.getUpdateManager();
11606             updater.update(url, params, this._setLoaded.createDelegate(this));
11607         }
11608     },
11609
11610     /**
11611      *   Forces a content refresh from the URL specified in the {@link #setUrl} method.
11612      *   Will fail silently if the setUrl method has not been called.
11613      *   This does not activate the panel, just updates its content.
11614      */
11615     refresh : function(){
11616         if(this.refreshDelegate){
11617            this.loaded = false;
11618            this.refreshDelegate();
11619         }
11620     },
11621
11622     /** @private */
11623     _setLoaded : function(){
11624         this.loaded = true;
11625     },
11626
11627     /** @private */
11628     closeClick : function(e){
11629         var o = {};
11630         e.stopEvent();
11631         this.fireEvent("beforeclose", this, o);
11632         if(o.cancel !== true){
11633             this.tabPanel.removeTab(this.id);
11634         }
11635     },
11636     /**
11637      * The text displayed in the tooltip for the close icon.
11638      * @type String
11639      */
11640     closeText : "Close this tab"
11641 });
11642
11643 /** @private */
11644 Roo.TabPanel.prototype.createStrip = function(container){
11645     var strip = document.createElement("div");
11646     strip.className = "x-tabs-wrap";
11647     container.appendChild(strip);
11648     return strip;
11649 };
11650 /** @private */
11651 Roo.TabPanel.prototype.createStripList = function(strip){
11652     // div wrapper for retard IE
11653     // returns the "tr" element.
11654     strip.innerHTML = '<div class="x-tabs-strip-wrap">'+
11655         '<table class="x-tabs-strip" cellspacing="0" cellpadding="0" border="0"><tbody><tr>'+
11656         '<td class="x-tab-strip-toolbar"></td></tr></tbody></table></div>';
11657     return strip.firstChild.firstChild.firstChild.firstChild;
11658 };
11659 /** @private */
11660 Roo.TabPanel.prototype.createBody = function(container){
11661     var body = document.createElement("div");
11662     Roo.id(body, "tab-body");
11663     Roo.fly(body).addClass("x-tabs-body");
11664     container.appendChild(body);
11665     return body;
11666 };
11667 /** @private */
11668 Roo.TabPanel.prototype.createItemBody = function(bodyEl, id){
11669     var body = Roo.getDom(id);
11670     if(!body){
11671         body = document.createElement("div");
11672         body.id = id;
11673     }
11674     Roo.fly(body).addClass("x-tabs-item-body");
11675     bodyEl.insertBefore(body, bodyEl.firstChild);
11676     return body;
11677 };
11678 /** @private */
11679 Roo.TabPanel.prototype.createStripElements = function(stripEl, text, closable){
11680     var td = document.createElement("td");
11681     stripEl.insertBefore(td, stripEl.childNodes[stripEl.childNodes.length-1]);
11682     //stripEl.appendChild(td);
11683     if(closable){
11684         td.className = "x-tabs-closable";
11685         if(!this.closeTpl){
11686             this.closeTpl = new Roo.Template(
11687                '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
11688                '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span>' +
11689                '<div unselectable="on" class="close-icon">&#160;</div></em></span></a>'
11690             );
11691         }
11692         var el = this.closeTpl.overwrite(td, {"text": text});
11693         var close = el.getElementsByTagName("div")[0];
11694         var inner = el.getElementsByTagName("em")[0];
11695         return {"el": el, "close": close, "inner": inner};
11696     } else {
11697         if(!this.tabTpl){
11698             this.tabTpl = new Roo.Template(
11699                '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
11700                '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span></em></span></a>'
11701             );
11702         }
11703         var el = this.tabTpl.overwrite(td, {"text": text});
11704         var inner = el.getElementsByTagName("em")[0];
11705         return {"el": el, "inner": inner};
11706     }
11707 };/*
11708  * Based on:
11709  * Ext JS Library 1.1.1
11710  * Copyright(c) 2006-2007, Ext JS, LLC.
11711  *
11712  * Originally Released Under LGPL - original licence link has changed is not relivant.
11713  *
11714  * Fork - LGPL
11715  * <script type="text/javascript">
11716  */
11717
11718 /**
11719  * @class Roo.Button
11720  * @extends Roo.util.Observable
11721  * Simple Button class
11722  * @cfg {String} text The button text
11723  * @cfg {String} icon The path to an image to display in the button (the image will be set as the background-image
11724  * CSS property of the button by default, so if you want a mixed icon/text button, set cls:"x-btn-text-icon")
11725  * @cfg {Function} handler A function called when the button is clicked (can be used instead of click event)
11726  * @cfg {Object} scope The scope of the handler
11727  * @cfg {Number} minWidth The minimum width for this button (used to give a set of buttons a common width)
11728  * @cfg {String/Object} tooltip The tooltip for the button - can be a string or QuickTips config object
11729  * @cfg {Boolean} hidden True to start hidden (defaults to false)
11730  * @cfg {Boolean} disabled True to start disabled (defaults to false)
11731  * @cfg {Boolean} pressed True to start pressed (only if enableToggle = true)
11732  * @cfg {String} toggleGroup The group this toggle button is a member of (only 1 per group can be pressed, only
11733    applies if enableToggle = true)
11734  * @cfg {String/HTMLElement/Element} renderTo The element to append the button to
11735  * @cfg {Boolean/Object} repeat True to repeat fire the click event while the mouse is down. This can also be
11736   an {@link Roo.util.ClickRepeater} config object (defaults to false).
11737  * @constructor
11738  * Create a new button
11739  * @param {Object} config The config object
11740  */
11741 Roo.Button = function(renderTo, config)
11742 {
11743     if (!config) {
11744         config = renderTo;
11745         renderTo = config.renderTo || false;
11746     }
11747     
11748     Roo.apply(this, config);
11749     this.addEvents({
11750         /**
11751              * @event click
11752              * Fires when this button is clicked
11753              * @param {Button} this
11754              * @param {EventObject} e The click event
11755              */
11756             "click" : true,
11757         /**
11758              * @event toggle
11759              * Fires when the "pressed" state of this button changes (only if enableToggle = true)
11760              * @param {Button} this
11761              * @param {Boolean} pressed
11762              */
11763             "toggle" : true,
11764         /**
11765              * @event mouseover
11766              * Fires when the mouse hovers over the button
11767              * @param {Button} this
11768              * @param {Event} e The event object
11769              */
11770         'mouseover' : true,
11771         /**
11772              * @event mouseout
11773              * Fires when the mouse exits the button
11774              * @param {Button} this
11775              * @param {Event} e The event object
11776              */
11777         'mouseout': true,
11778          /**
11779              * @event render
11780              * Fires when the button is rendered
11781              * @param {Button} this
11782              */
11783         'render': true
11784     });
11785     if(this.menu){
11786         this.menu = Roo.menu.MenuMgr.get(this.menu);
11787     }
11788     // register listeners first!!  - so render can be captured..
11789     Roo.util.Observable.call(this);
11790     if(renderTo){
11791         this.render(renderTo);
11792     }
11793     
11794   
11795 };
11796
11797 Roo.extend(Roo.Button, Roo.util.Observable, {
11798     /**
11799      * 
11800      */
11801     
11802     /**
11803      * Read-only. True if this button is hidden
11804      * @type Boolean
11805      */
11806     hidden : false,
11807     /**
11808      * Read-only. True if this button is disabled
11809      * @type Boolean
11810      */
11811     disabled : false,
11812     /**
11813      * Read-only. True if this button is pressed (only if enableToggle = true)
11814      * @type Boolean
11815      */
11816     pressed : false,
11817
11818     /**
11819      * @cfg {Number} tabIndex 
11820      * The DOM tabIndex for this button (defaults to undefined)
11821      */
11822     tabIndex : undefined,
11823
11824     /**
11825      * @cfg {Boolean} enableToggle
11826      * True to enable pressed/not pressed toggling (defaults to false)
11827      */
11828     enableToggle: false,
11829     /**
11830      * @cfg {Mixed} menu
11831      * Standard menu attribute consisting of a reference to a menu object, a menu id or a menu config blob (defaults to undefined).
11832      */
11833     menu : undefined,
11834     /**
11835      * @cfg {String} menuAlign
11836      * The position to align the menu to (see {@link Roo.Element#alignTo} for more details, defaults to 'tl-bl?').
11837      */
11838     menuAlign : "tl-bl?",
11839
11840     /**
11841      * @cfg {String} iconCls
11842      * A css class which sets a background image to be used as the icon for this button (defaults to undefined).
11843      */
11844     iconCls : undefined,
11845     /**
11846      * @cfg {String} type
11847      * The button's type, corresponding to the DOM input element type attribute.  Either "submit," "reset" or "button" (default).
11848      */
11849     type : 'button',
11850
11851     // private
11852     menuClassTarget: 'tr',
11853
11854     /**
11855      * @cfg {String} clickEvent
11856      * The type of event to map to the button's event handler (defaults to 'click')
11857      */
11858     clickEvent : 'click',
11859
11860     /**
11861      * @cfg {Boolean} handleMouseEvents
11862      * False to disable visual cues on mouseover, mouseout and mousedown (defaults to true)
11863      */
11864     handleMouseEvents : true,
11865
11866     /**
11867      * @cfg {String} tooltipType
11868      * The type of tooltip to use. Either "qtip" (default) for QuickTips or "title" for title attribute.
11869      */
11870     tooltipType : 'qtip',
11871
11872     /**
11873      * @cfg {String} cls
11874      * A CSS class to apply to the button's main element.
11875      */
11876     
11877     /**
11878      * @cfg {Roo.Template} template (Optional)
11879      * An {@link Roo.Template} with which to create the Button's main element. This Template must
11880      * contain numeric substitution parameter 0 if it is to display the tRoo property. Changing the template could
11881      * require code modifications if required elements (e.g. a button) aren't present.
11882      */
11883
11884     // private
11885     render : function(renderTo){
11886         var btn;
11887         if(this.hideParent){
11888             this.parentEl = Roo.get(renderTo);
11889         }
11890         if(!this.dhconfig){
11891             if(!this.template){
11892                 if(!Roo.Button.buttonTemplate){
11893                     // hideous table template
11894                     Roo.Button.buttonTemplate = new Roo.Template(
11895                         '<table border="0" cellpadding="0" cellspacing="0" class="x-btn-wrap"><tbody><tr>',
11896                         '<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>',
11897                         "</tr></tbody></table>");
11898                 }
11899                 this.template = Roo.Button.buttonTemplate;
11900             }
11901             btn = this.template.append(renderTo, [this.text || '&#160;', this.type], true);
11902             var btnEl = btn.child("button:first");
11903             btnEl.on('focus', this.onFocus, this);
11904             btnEl.on('blur', this.onBlur, this);
11905             if(this.cls){
11906                 btn.addClass(this.cls);
11907             }
11908             if(this.icon){
11909                 btnEl.setStyle('background-image', 'url(' +this.icon +')');
11910             }
11911             if(this.iconCls){
11912                 btnEl.addClass(this.iconCls);
11913                 if(!this.cls){
11914                     btn.addClass(this.text ? 'x-btn-text-icon' : 'x-btn-icon');
11915                 }
11916             }
11917             if(this.tabIndex !== undefined){
11918                 btnEl.dom.tabIndex = this.tabIndex;
11919             }
11920             if(this.tooltip){
11921                 if(typeof this.tooltip == 'object'){
11922                     Roo.QuickTips.tips(Roo.apply({
11923                           target: btnEl.id
11924                     }, this.tooltip));
11925                 } else {
11926                     btnEl.dom[this.tooltipType] = this.tooltip;
11927                 }
11928             }
11929         }else{
11930             btn = Roo.DomHelper.append(Roo.get(renderTo).dom, this.dhconfig, true);
11931         }
11932         this.el = btn;
11933         if(this.id){
11934             this.el.dom.id = this.el.id = this.id;
11935         }
11936         if(this.menu){
11937             this.el.child(this.menuClassTarget).addClass("x-btn-with-menu");
11938             this.menu.on("show", this.onMenuShow, this);
11939             this.menu.on("hide", this.onMenuHide, this);
11940         }
11941         btn.addClass("x-btn");
11942         if(Roo.isIE && !Roo.isIE7){
11943             this.autoWidth.defer(1, this);
11944         }else{
11945             this.autoWidth();
11946         }
11947         if(this.handleMouseEvents){
11948             btn.on("mouseover", this.onMouseOver, this);
11949             btn.on("mouseout", this.onMouseOut, this);
11950             btn.on("mousedown", this.onMouseDown, this);
11951         }
11952         btn.on(this.clickEvent, this.onClick, this);
11953         //btn.on("mouseup", this.onMouseUp, this);
11954         if(this.hidden){
11955             this.hide();
11956         }
11957         if(this.disabled){
11958             this.disable();
11959         }
11960         Roo.ButtonToggleMgr.register(this);
11961         if(this.pressed){
11962             this.el.addClass("x-btn-pressed");
11963         }
11964         if(this.repeat){
11965             var repeater = new Roo.util.ClickRepeater(btn,
11966                 typeof this.repeat == "object" ? this.repeat : {}
11967             );
11968             repeater.on("click", this.onClick,  this);
11969         }
11970         
11971         this.fireEvent('render', this);
11972         
11973     },
11974     /**
11975      * Returns the button's underlying element
11976      * @return {Roo.Element} The element
11977      */
11978     getEl : function(){
11979         return this.el;  
11980     },
11981     
11982     /**
11983      * Destroys this Button and removes any listeners.
11984      */
11985     destroy : function(){
11986         Roo.ButtonToggleMgr.unregister(this);
11987         this.el.removeAllListeners();
11988         this.purgeListeners();
11989         this.el.remove();
11990     },
11991
11992     // private
11993     autoWidth : function(){
11994         if(this.el){
11995             this.el.setWidth("auto");
11996             if(Roo.isIE7 && Roo.isStrict){
11997                 var ib = this.el.child('button');
11998                 if(ib && ib.getWidth() > 20){
11999                     ib.clip();
12000                     ib.setWidth(Roo.util.TextMetrics.measure(ib, this.text).width+ib.getFrameWidth('lr'));
12001                 }
12002             }
12003             if(this.minWidth){
12004                 if(this.hidden){
12005                     this.el.beginMeasure();
12006                 }
12007                 if(this.el.getWidth() < this.minWidth){
12008                     this.el.setWidth(this.minWidth);
12009                 }
12010                 if(this.hidden){
12011                     this.el.endMeasure();
12012                 }
12013             }
12014         }
12015     },
12016
12017     /**
12018      * Assigns this button's click handler
12019      * @param {Function} handler The function to call when the button is clicked
12020      * @param {Object} scope (optional) Scope for the function passed in
12021      */
12022     setHandler : function(handler, scope){
12023         this.handler = handler;
12024         this.scope = scope;  
12025     },
12026     
12027     /**
12028      * Sets this button's text
12029      * @param {String} text The button text
12030      */
12031     setText : function(text){
12032         this.text = text;
12033         if(this.el){
12034             this.el.child("td.x-btn-center button.x-btn-text").update(text);
12035         }
12036         this.autoWidth();
12037     },
12038     
12039     /**
12040      * Gets the text for this button
12041      * @return {String} The button text
12042      */
12043     getText : function(){
12044         return this.text;  
12045     },
12046     
12047     /**
12048      * Show this button
12049      */
12050     show: function(){
12051         this.hidden = false;
12052         if(this.el){
12053             this[this.hideParent? 'parentEl' : 'el'].setStyle("display", "");
12054         }
12055     },
12056     
12057     /**
12058      * Hide this button
12059      */
12060     hide: function(){
12061         this.hidden = true;
12062         if(this.el){
12063             this[this.hideParent? 'parentEl' : 'el'].setStyle("display", "none");
12064         }
12065     },
12066     
12067     /**
12068      * Convenience function for boolean show/hide
12069      * @param {Boolean} visible True to show, false to hide
12070      */
12071     setVisible: function(visible){
12072         if(visible) {
12073             this.show();
12074         }else{
12075             this.hide();
12076         }
12077     },
12078     
12079     /**
12080      * If a state it passed, it becomes the pressed state otherwise the current state is toggled.
12081      * @param {Boolean} state (optional) Force a particular state
12082      */
12083     toggle : function(state){
12084         state = state === undefined ? !this.pressed : state;
12085         if(state != this.pressed){
12086             if(state){
12087                 this.el.addClass("x-btn-pressed");
12088                 this.pressed = true;
12089                 this.fireEvent("toggle", this, true);
12090             }else{
12091                 this.el.removeClass("x-btn-pressed");
12092                 this.pressed = false;
12093                 this.fireEvent("toggle", this, false);
12094             }
12095             if(this.toggleHandler){
12096                 this.toggleHandler.call(this.scope || this, this, state);
12097             }
12098         }
12099     },
12100     
12101     /**
12102      * Focus the button
12103      */
12104     focus : function(){
12105         this.el.child('button:first').focus();
12106     },
12107     
12108     /**
12109      * Disable this button
12110      */
12111     disable : function(){
12112         if(this.el){
12113             this.el.addClass("x-btn-disabled");
12114         }
12115         this.disabled = true;
12116     },
12117     
12118     /**
12119      * Enable this button
12120      */
12121     enable : function(){
12122         if(this.el){
12123             this.el.removeClass("x-btn-disabled");
12124         }
12125         this.disabled = false;
12126     },
12127
12128     /**
12129      * Convenience function for boolean enable/disable
12130      * @param {Boolean} enabled True to enable, false to disable
12131      */
12132     setDisabled : function(v){
12133         this[v !== true ? "enable" : "disable"]();
12134     },
12135
12136     // private
12137     onClick : function(e){
12138         if(e){
12139             e.preventDefault();
12140         }
12141         if(e.button != 0){
12142             return;
12143         }
12144         if(!this.disabled){
12145             if(this.enableToggle){
12146                 this.toggle();
12147             }
12148             if(this.menu && !this.menu.isVisible()){
12149                 this.menu.show(this.el, this.menuAlign);
12150             }
12151             this.fireEvent("click", this, e);
12152             if(this.handler){
12153                 this.el.removeClass("x-btn-over");
12154                 this.handler.call(this.scope || this, this, e);
12155             }
12156         }
12157     },
12158     // private
12159     onMouseOver : function(e){
12160         if(!this.disabled){
12161             this.el.addClass("x-btn-over");
12162             this.fireEvent('mouseover', this, e);
12163         }
12164     },
12165     // private
12166     onMouseOut : function(e){
12167         if(!e.within(this.el,  true)){
12168             this.el.removeClass("x-btn-over");
12169             this.fireEvent('mouseout', this, e);
12170         }
12171     },
12172     // private
12173     onFocus : function(e){
12174         if(!this.disabled){
12175             this.el.addClass("x-btn-focus");
12176         }
12177     },
12178     // private
12179     onBlur : function(e){
12180         this.el.removeClass("x-btn-focus");
12181     },
12182     // private
12183     onMouseDown : function(e){
12184         if(!this.disabled && e.button == 0){
12185             this.el.addClass("x-btn-click");
12186             Roo.get(document).on('mouseup', this.onMouseUp, this);
12187         }
12188     },
12189     // private
12190     onMouseUp : function(e){
12191         if(e.button == 0){
12192             this.el.removeClass("x-btn-click");
12193             Roo.get(document).un('mouseup', this.onMouseUp, this);
12194         }
12195     },
12196     // private
12197     onMenuShow : function(e){
12198         this.el.addClass("x-btn-menu-active");
12199     },
12200     // private
12201     onMenuHide : function(e){
12202         this.el.removeClass("x-btn-menu-active");
12203     }   
12204 });
12205
12206 // Private utility class used by Button
12207 Roo.ButtonToggleMgr = function(){
12208    var groups = {};
12209    
12210    function toggleGroup(btn, state){
12211        if(state){
12212            var g = groups[btn.toggleGroup];
12213            for(var i = 0, l = g.length; i < l; i++){
12214                if(g[i] != btn){
12215                    g[i].toggle(false);
12216                }
12217            }
12218        }
12219    }
12220    
12221    return {
12222        register : function(btn){
12223            if(!btn.toggleGroup){
12224                return;
12225            }
12226            var g = groups[btn.toggleGroup];
12227            if(!g){
12228                g = groups[btn.toggleGroup] = [];
12229            }
12230            g.push(btn);
12231            btn.on("toggle", toggleGroup);
12232        },
12233        
12234        unregister : function(btn){
12235            if(!btn.toggleGroup){
12236                return;
12237            }
12238            var g = groups[btn.toggleGroup];
12239            if(g){
12240                g.remove(btn);
12241                btn.un("toggle", toggleGroup);
12242            }
12243        }
12244    };
12245 }();/*
12246  * Based on:
12247  * Ext JS Library 1.1.1
12248  * Copyright(c) 2006-2007, Ext JS, LLC.
12249  *
12250  * Originally Released Under LGPL - original licence link has changed is not relivant.
12251  *
12252  * Fork - LGPL
12253  * <script type="text/javascript">
12254  */
12255  
12256 /**
12257  * @class Roo.SplitButton
12258  * @extends Roo.Button
12259  * A split button that provides a built-in dropdown arrow that can fire an event separately from the default
12260  * click event of the button.  Typically this would be used to display a dropdown menu that provides additional
12261  * options to the primary button action, but any custom handler can provide the arrowclick implementation.
12262  * @cfg {Function} arrowHandler A function called when the arrow button is clicked (can be used instead of click event)
12263  * @cfg {String} arrowTooltip The title attribute of the arrow
12264  * @constructor
12265  * Create a new menu button
12266  * @param {String/HTMLElement/Element} renderTo The element to append the button to
12267  * @param {Object} config The config object
12268  */
12269 Roo.SplitButton = function(renderTo, config){
12270     Roo.SplitButton.superclass.constructor.call(this, renderTo, config);
12271     /**
12272      * @event arrowclick
12273      * Fires when this button's arrow is clicked
12274      * @param {SplitButton} this
12275      * @param {EventObject} e The click event
12276      */
12277     this.addEvents({"arrowclick":true});
12278 };
12279
12280 Roo.extend(Roo.SplitButton, Roo.Button, {
12281     render : function(renderTo){
12282         // this is one sweet looking template!
12283         var tpl = new Roo.Template(
12284             '<table cellspacing="0" class="x-btn-menu-wrap x-btn"><tr><td>',
12285             '<table cellspacing="0" class="x-btn-wrap x-btn-menu-text-wrap"><tbody>',
12286             '<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>',
12287             "</tbody></table></td><td>",
12288             '<table cellspacing="0" class="x-btn-wrap x-btn-menu-arrow-wrap"><tbody>',
12289             '<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>',
12290             "</tbody></table></td></tr></table>"
12291         );
12292         var btn = tpl.append(renderTo, [this.text, this.type], true);
12293         var btnEl = btn.child("button");
12294         if(this.cls){
12295             btn.addClass(this.cls);
12296         }
12297         if(this.icon){
12298             btnEl.setStyle('background-image', 'url(' +this.icon +')');
12299         }
12300         if(this.iconCls){
12301             btnEl.addClass(this.iconCls);
12302             if(!this.cls){
12303                 btn.addClass(this.text ? 'x-btn-text-icon' : 'x-btn-icon');
12304             }
12305         }
12306         this.el = btn;
12307         if(this.handleMouseEvents){
12308             btn.on("mouseover", this.onMouseOver, this);
12309             btn.on("mouseout", this.onMouseOut, this);
12310             btn.on("mousedown", this.onMouseDown, this);
12311             btn.on("mouseup", this.onMouseUp, this);
12312         }
12313         btn.on(this.clickEvent, this.onClick, this);
12314         if(this.tooltip){
12315             if(typeof this.tooltip == 'object'){
12316                 Roo.QuickTips.tips(Roo.apply({
12317                       target: btnEl.id
12318                 }, this.tooltip));
12319             } else {
12320                 btnEl.dom[this.tooltipType] = this.tooltip;
12321             }
12322         }
12323         if(this.arrowTooltip){
12324             btn.child("button:nth(2)").dom[this.tooltipType] = this.arrowTooltip;
12325         }
12326         if(this.hidden){
12327             this.hide();
12328         }
12329         if(this.disabled){
12330             this.disable();
12331         }
12332         if(this.pressed){
12333             this.el.addClass("x-btn-pressed");
12334         }
12335         if(Roo.isIE && !Roo.isIE7){
12336             this.autoWidth.defer(1, this);
12337         }else{
12338             this.autoWidth();
12339         }
12340         if(this.menu){
12341             this.menu.on("show", this.onMenuShow, this);
12342             this.menu.on("hide", this.onMenuHide, this);
12343         }
12344         this.fireEvent('render', this);
12345     },
12346
12347     // private
12348     autoWidth : function(){
12349         if(this.el){
12350             var tbl = this.el.child("table:first");
12351             var tbl2 = this.el.child("table:last");
12352             this.el.setWidth("auto");
12353             tbl.setWidth("auto");
12354             if(Roo.isIE7 && Roo.isStrict){
12355                 var ib = this.el.child('button:first');
12356                 if(ib && ib.getWidth() > 20){
12357                     ib.clip();
12358                     ib.setWidth(Roo.util.TextMetrics.measure(ib, this.text).width+ib.getFrameWidth('lr'));
12359                 }
12360             }
12361             if(this.minWidth){
12362                 if(this.hidden){
12363                     this.el.beginMeasure();
12364                 }
12365                 if((tbl.getWidth()+tbl2.getWidth()) < this.minWidth){
12366                     tbl.setWidth(this.minWidth-tbl2.getWidth());
12367                 }
12368                 if(this.hidden){
12369                     this.el.endMeasure();
12370                 }
12371             }
12372             this.el.setWidth(tbl.getWidth()+tbl2.getWidth());
12373         } 
12374     },
12375     /**
12376      * Sets this button's click handler
12377      * @param {Function} handler The function to call when the button is clicked
12378      * @param {Object} scope (optional) Scope for the function passed above
12379      */
12380     setHandler : function(handler, scope){
12381         this.handler = handler;
12382         this.scope = scope;  
12383     },
12384     
12385     /**
12386      * Sets this button's arrow click handler
12387      * @param {Function} handler The function to call when the arrow is clicked
12388      * @param {Object} scope (optional) Scope for the function passed above
12389      */
12390     setArrowHandler : function(handler, scope){
12391         this.arrowHandler = handler;
12392         this.scope = scope;  
12393     },
12394     
12395     /**
12396      * Focus the button
12397      */
12398     focus : function(){
12399         if(this.el){
12400             this.el.child("button:first").focus();
12401         }
12402     },
12403
12404     // private
12405     onClick : function(e){
12406         e.preventDefault();
12407         if(!this.disabled){
12408             if(e.getTarget(".x-btn-menu-arrow-wrap")){
12409                 if(this.menu && !this.menu.isVisible()){
12410                     this.menu.show(this.el, this.menuAlign);
12411                 }
12412                 this.fireEvent("arrowclick", this, e);
12413                 if(this.arrowHandler){
12414                     this.arrowHandler.call(this.scope || this, this, e);
12415                 }
12416             }else{
12417                 this.fireEvent("click", this, e);
12418                 if(this.handler){
12419                     this.handler.call(this.scope || this, this, e);
12420                 }
12421             }
12422         }
12423     },
12424     // private
12425     onMouseDown : function(e){
12426         if(!this.disabled){
12427             Roo.fly(e.getTarget("table")).addClass("x-btn-click");
12428         }
12429     },
12430     // private
12431     onMouseUp : function(e){
12432         Roo.fly(e.getTarget("table")).removeClass("x-btn-click");
12433     }   
12434 });
12435
12436
12437 // backwards compat
12438 Roo.MenuButton = Roo.SplitButton;/*
12439  * Based on:
12440  * Ext JS Library 1.1.1
12441  * Copyright(c) 2006-2007, Ext JS, LLC.
12442  *
12443  * Originally Released Under LGPL - original licence link has changed is not relivant.
12444  *
12445  * Fork - LGPL
12446  * <script type="text/javascript">
12447  */
12448
12449 /**
12450  * @class Roo.Toolbar
12451  * Basic Toolbar class.
12452  * @constructor
12453  * Creates a new Toolbar
12454  * @param {Object} container The config object
12455  */ 
12456 Roo.Toolbar = function(container, buttons, config)
12457 {
12458     /// old consturctor format still supported..
12459     if(container instanceof Array){ // omit the container for later rendering
12460         buttons = container;
12461         config = buttons;
12462         container = null;
12463     }
12464     if (typeof(container) == 'object' && container.xtype) {
12465         config = container;
12466         container = config.container;
12467         buttons = config.buttons || []; // not really - use items!!
12468     }
12469     var xitems = [];
12470     if (config && config.items) {
12471         xitems = config.items;
12472         delete config.items;
12473     }
12474     Roo.apply(this, config);
12475     this.buttons = buttons;
12476     
12477     if(container){
12478         this.render(container);
12479     }
12480     this.xitems = xitems;
12481     Roo.each(xitems, function(b) {
12482         this.add(b);
12483     }, this);
12484     
12485 };
12486
12487 Roo.Toolbar.prototype = {
12488     /**
12489      * @cfg {Array} items
12490      * array of button configs or elements to add (will be converted to a MixedCollection)
12491      */
12492     
12493     /**
12494      * @cfg {String/HTMLElement/Element} container
12495      * The id or element that will contain the toolbar
12496      */
12497     // private
12498     render : function(ct){
12499         this.el = Roo.get(ct);
12500         if(this.cls){
12501             this.el.addClass(this.cls);
12502         }
12503         // using a table allows for vertical alignment
12504         // 100% width is needed by Safari...
12505         this.el.update('<div class="x-toolbar x-small-editor"><table cellspacing="0"><tr></tr></table></div>');
12506         this.tr = this.el.child("tr", true);
12507         var autoId = 0;
12508         this.items = new Roo.util.MixedCollection(false, function(o){
12509             return o.id || ("item" + (++autoId));
12510         });
12511         if(this.buttons){
12512             this.add.apply(this, this.buttons);
12513             delete this.buttons;
12514         }
12515     },
12516
12517     /**
12518      * Adds element(s) to the toolbar -- this function takes a variable number of 
12519      * arguments of mixed type and adds them to the toolbar.
12520      * @param {Mixed} arg1 The following types of arguments are all valid:<br />
12521      * <ul>
12522      * <li>{@link Roo.Toolbar.Button} config: A valid button config object (equivalent to {@link #addButton})</li>
12523      * <li>HtmlElement: Any standard HTML element (equivalent to {@link #addElement})</li>
12524      * <li>Field: Any form field (equivalent to {@link #addField})</li>
12525      * <li>Item: Any subclass of {@link Roo.Toolbar.Item} (equivalent to {@link #addItem})</li>
12526      * <li>String: Any generic string (gets wrapped in a {@link Roo.Toolbar.TextItem}, equivalent to {@link #addText}).
12527      * Note that there are a few special strings that are treated differently as explained nRoo.</li>
12528      * <li>'separator' or '-': Creates a separator element (equivalent to {@link #addSeparator})</li>
12529      * <li>' ': Creates a spacer element (equivalent to {@link #addSpacer})</li>
12530      * <li>'->': Creates a fill element (equivalent to {@link #addFill})</li>
12531      * </ul>
12532      * @param {Mixed} arg2
12533      * @param {Mixed} etc.
12534      */
12535     add : function(){
12536         var a = arguments, l = a.length;
12537         for(var i = 0; i < l; i++){
12538             this._add(a[i]);
12539         }
12540     },
12541     // private..
12542     _add : function(el) {
12543         
12544         if (el.xtype) {
12545             el = Roo.factory(el, typeof(Roo.Toolbar[el.xtype]) == 'undefined' ? Roo.form : Roo.Toolbar);
12546         }
12547         
12548         if (el.applyTo){ // some kind of form field
12549             return this.addField(el);
12550         } 
12551         if (el.render){ // some kind of Toolbar.Item
12552             return this.addItem(el);
12553         }
12554         if (typeof el == "string"){ // string
12555             if(el == "separator" || el == "-"){
12556                 return this.addSeparator();
12557             }
12558             if (el == " "){
12559                 return this.addSpacer();
12560             }
12561             if(el == "->"){
12562                 return this.addFill();
12563             }
12564             return this.addText(el);
12565             
12566         }
12567         if(el.tagName){ // element
12568             return this.addElement(el);
12569         }
12570         if(typeof el == "object"){ // must be button config?
12571             return this.addButton(el);
12572         }
12573         // and now what?!?!
12574         return false;
12575         
12576     },
12577     
12578     /**
12579      * Add an Xtype element
12580      * @param {Object} xtype Xtype Object
12581      * @return {Object} created Object
12582      */
12583     addxtype : function(e){
12584         return this.add(e);  
12585     },
12586     
12587     /**
12588      * Returns the Element for this toolbar.
12589      * @return {Roo.Element}
12590      */
12591     getEl : function(){
12592         return this.el;  
12593     },
12594     
12595     /**
12596      * Adds a separator
12597      * @return {Roo.Toolbar.Item} The separator item
12598      */
12599     addSeparator : function(){
12600         return this.addItem(new Roo.Toolbar.Separator());
12601     },
12602
12603     /**
12604      * Adds a spacer element
12605      * @return {Roo.Toolbar.Spacer} The spacer item
12606      */
12607     addSpacer : function(){
12608         return this.addItem(new Roo.Toolbar.Spacer());
12609     },
12610
12611     /**
12612      * Adds a fill element that forces subsequent additions to the right side of the toolbar
12613      * @return {Roo.Toolbar.Fill} The fill item
12614      */
12615     addFill : function(){
12616         return this.addItem(new Roo.Toolbar.Fill());
12617     },
12618
12619     /**
12620      * Adds any standard HTML element to the toolbar
12621      * @param {String/HTMLElement/Element} el The element or id of the element to add
12622      * @return {Roo.Toolbar.Item} The element's item
12623      */
12624     addElement : function(el){
12625         return this.addItem(new Roo.Toolbar.Item(el));
12626     },
12627     /**
12628      * Collection of items on the toolbar.. (only Toolbar Items, so use fields to retrieve fields)
12629      * @type Roo.util.MixedCollection  
12630      */
12631     items : false,
12632      
12633     /**
12634      * Adds any Toolbar.Item or subclass
12635      * @param {Roo.Toolbar.Item} item
12636      * @return {Roo.Toolbar.Item} The item
12637      */
12638     addItem : function(item){
12639         var td = this.nextBlock();
12640         item.render(td);
12641         this.items.add(item);
12642         return item;
12643     },
12644     
12645     /**
12646      * Adds a button (or buttons). See {@link Roo.Toolbar.Button} for more info on the config.
12647      * @param {Object/Array} config A button config or array of configs
12648      * @return {Roo.Toolbar.Button/Array}
12649      */
12650     addButton : function(config){
12651         if(config instanceof Array){
12652             var buttons = [];
12653             for(var i = 0, len = config.length; i < len; i++) {
12654                 buttons.push(this.addButton(config[i]));
12655             }
12656             return buttons;
12657         }
12658         var b = config;
12659         if(!(config instanceof Roo.Toolbar.Button)){
12660             b = config.split ?
12661                 new Roo.Toolbar.SplitButton(config) :
12662                 new Roo.Toolbar.Button(config);
12663         }
12664         var td = this.nextBlock();
12665         b.render(td);
12666         this.items.add(b);
12667         return b;
12668     },
12669     
12670     /**
12671      * Adds text to the toolbar
12672      * @param {String} text The text to add
12673      * @return {Roo.Toolbar.Item} The element's item
12674      */
12675     addText : function(text){
12676         return this.addItem(new Roo.Toolbar.TextItem(text));
12677     },
12678     
12679     /**
12680      * Inserts any {@link Roo.Toolbar.Item}/{@link Roo.Toolbar.Button} at the specified index.
12681      * @param {Number} index The index where the item is to be inserted
12682      * @param {Object/Roo.Toolbar.Item/Roo.Toolbar.Button (may be Array)} item The button, or button config object to be inserted.
12683      * @return {Roo.Toolbar.Button/Item}
12684      */
12685     insertButton : function(index, item){
12686         if(item instanceof Array){
12687             var buttons = [];
12688             for(var i = 0, len = item.length; i < len; i++) {
12689                buttons.push(this.insertButton(index + i, item[i]));
12690             }
12691             return buttons;
12692         }
12693         if (!(item instanceof Roo.Toolbar.Button)){
12694            item = new Roo.Toolbar.Button(item);
12695         }
12696         var td = document.createElement("td");
12697         this.tr.insertBefore(td, this.tr.childNodes[index]);
12698         item.render(td);
12699         this.items.insert(index, item);
12700         return item;
12701     },
12702     
12703     /**
12704      * Adds a new element to the toolbar from the passed {@link Roo.DomHelper} config.
12705      * @param {Object} config
12706      * @return {Roo.Toolbar.Item} The element's item
12707      */
12708     addDom : function(config, returnEl){
12709         var td = this.nextBlock();
12710         Roo.DomHelper.overwrite(td, config);
12711         var ti = new Roo.Toolbar.Item(td.firstChild);
12712         ti.render(td);
12713         this.items.add(ti);
12714         return ti;
12715     },
12716
12717     /**
12718      * Collection of fields on the toolbar.. usefull for quering (value is false if there are no fields)
12719      * @type Roo.util.MixedCollection  
12720      */
12721     fields : false,
12722     
12723     /**
12724      * Adds a dynamically rendered Roo.form field (TextField, ComboBox, etc).
12725      * Note: the field should not have been rendered yet. For a field that has already been
12726      * rendered, use {@link #addElement}.
12727      * @param {Roo.form.Field} field
12728      * @return {Roo.ToolbarItem}
12729      */
12730      
12731       
12732     addField : function(field) {
12733         if (!this.fields) {
12734             var autoId = 0;
12735             this.fields = new Roo.util.MixedCollection(false, function(o){
12736                 return o.id || ("item" + (++autoId));
12737             });
12738
12739         }
12740         
12741         var td = this.nextBlock();
12742         field.render(td);
12743         var ti = new Roo.Toolbar.Item(td.firstChild);
12744         ti.render(td);
12745         this.items.add(ti);
12746         this.fields.add(field);
12747         return ti;
12748     },
12749     /**
12750      * Hide the toolbar
12751      * @method hide
12752      */
12753      
12754       
12755     hide : function()
12756     {
12757         this.el.child('div').setVisibilityMode(Roo.Element.DISPLAY);
12758         this.el.child('div').hide();
12759     },
12760     /**
12761      * Show the toolbar
12762      * @method show
12763      */
12764     show : function()
12765     {
12766         this.el.child('div').show();
12767     },
12768       
12769     // private
12770     nextBlock : function(){
12771         var td = document.createElement("td");
12772         this.tr.appendChild(td);
12773         return td;
12774     },
12775
12776     // private
12777     destroy : function(){
12778         if(this.items){ // rendered?
12779             Roo.destroy.apply(Roo, this.items.items);
12780         }
12781         if(this.fields){ // rendered?
12782             Roo.destroy.apply(Roo, this.fields.items);
12783         }
12784         Roo.Element.uncache(this.el, this.tr);
12785     }
12786 };
12787
12788 /**
12789  * @class Roo.Toolbar.Item
12790  * The base class that other classes should extend in order to get some basic common toolbar item functionality.
12791  * @constructor
12792  * Creates a new Item
12793  * @param {HTMLElement} el 
12794  */
12795 Roo.Toolbar.Item = function(el){
12796     this.el = Roo.getDom(el);
12797     this.id = Roo.id(this.el);
12798     this.hidden = false;
12799 };
12800
12801 Roo.Toolbar.Item.prototype = {
12802     
12803     /**
12804      * Get this item's HTML Element
12805      * @return {HTMLElement}
12806      */
12807     getEl : function(){
12808        return this.el;  
12809     },
12810
12811     // private
12812     render : function(td){
12813         this.td = td;
12814         td.appendChild(this.el);
12815     },
12816     
12817     /**
12818      * Removes and destroys this item.
12819      */
12820     destroy : function(){
12821         this.td.parentNode.removeChild(this.td);
12822     },
12823     
12824     /**
12825      * Shows this item.
12826      */
12827     show: function(){
12828         this.hidden = false;
12829         this.td.style.display = "";
12830     },
12831     
12832     /**
12833      * Hides this item.
12834      */
12835     hide: function(){
12836         this.hidden = true;
12837         this.td.style.display = "none";
12838     },
12839     
12840     /**
12841      * Convenience function for boolean show/hide.
12842      * @param {Boolean} visible true to show/false to hide
12843      */
12844     setVisible: function(visible){
12845         if(visible) {
12846             this.show();
12847         }else{
12848             this.hide();
12849         }
12850     },
12851     
12852     /**
12853      * Try to focus this item.
12854      */
12855     focus : function(){
12856         Roo.fly(this.el).focus();
12857     },
12858     
12859     /**
12860      * Disables this item.
12861      */
12862     disable : function(){
12863         Roo.fly(this.td).addClass("x-item-disabled");
12864         this.disabled = true;
12865         this.el.disabled = true;
12866     },
12867     
12868     /**
12869      * Enables this item.
12870      */
12871     enable : function(){
12872         Roo.fly(this.td).removeClass("x-item-disabled");
12873         this.disabled = false;
12874         this.el.disabled = false;
12875     }
12876 };
12877
12878
12879 /**
12880  * @class Roo.Toolbar.Separator
12881  * @extends Roo.Toolbar.Item
12882  * A simple toolbar separator class
12883  * @constructor
12884  * Creates a new Separator
12885  */
12886 Roo.Toolbar.Separator = function(){
12887     var s = document.createElement("span");
12888     s.className = "ytb-sep";
12889     Roo.Toolbar.Separator.superclass.constructor.call(this, s);
12890 };
12891 Roo.extend(Roo.Toolbar.Separator, Roo.Toolbar.Item, {
12892     enable:Roo.emptyFn,
12893     disable:Roo.emptyFn,
12894     focus:Roo.emptyFn
12895 });
12896
12897 /**
12898  * @class Roo.Toolbar.Spacer
12899  * @extends Roo.Toolbar.Item
12900  * A simple element that adds extra horizontal space to a toolbar.
12901  * @constructor
12902  * Creates a new Spacer
12903  */
12904 Roo.Toolbar.Spacer = function(){
12905     var s = document.createElement("div");
12906     s.className = "ytb-spacer";
12907     Roo.Toolbar.Spacer.superclass.constructor.call(this, s);
12908 };
12909 Roo.extend(Roo.Toolbar.Spacer, Roo.Toolbar.Item, {
12910     enable:Roo.emptyFn,
12911     disable:Roo.emptyFn,
12912     focus:Roo.emptyFn
12913 });
12914
12915 /**
12916  * @class Roo.Toolbar.Fill
12917  * @extends Roo.Toolbar.Spacer
12918  * A simple element that adds a greedy (100% width) horizontal space to a toolbar.
12919  * @constructor
12920  * Creates a new Spacer
12921  */
12922 Roo.Toolbar.Fill = Roo.extend(Roo.Toolbar.Spacer, {
12923     // private
12924     render : function(td){
12925         td.style.width = '100%';
12926         Roo.Toolbar.Fill.superclass.render.call(this, td);
12927     }
12928 });
12929
12930 /**
12931  * @class Roo.Toolbar.TextItem
12932  * @extends Roo.Toolbar.Item
12933  * A simple class that renders text directly into a toolbar.
12934  * @constructor
12935  * Creates a new TextItem
12936  * @param {String} text
12937  */
12938 Roo.Toolbar.TextItem = function(text){
12939     if (typeof(text) == 'object') {
12940         text = text.text;
12941     }
12942     var s = document.createElement("span");
12943     s.className = "ytb-text";
12944     s.innerHTML = text;
12945     Roo.Toolbar.TextItem.superclass.constructor.call(this, s);
12946 };
12947 Roo.extend(Roo.Toolbar.TextItem, Roo.Toolbar.Item, {
12948     enable:Roo.emptyFn,
12949     disable:Roo.emptyFn,
12950     focus:Roo.emptyFn
12951 });
12952
12953 /**
12954  * @class Roo.Toolbar.Button
12955  * @extends Roo.Button
12956  * A button that renders into a toolbar.
12957  * @constructor
12958  * Creates a new Button
12959  * @param {Object} config A standard {@link Roo.Button} config object
12960  */
12961 Roo.Toolbar.Button = function(config){
12962     Roo.Toolbar.Button.superclass.constructor.call(this, null, config);
12963 };
12964 Roo.extend(Roo.Toolbar.Button, Roo.Button, {
12965     render : function(td){
12966         this.td = td;
12967         Roo.Toolbar.Button.superclass.render.call(this, td);
12968     },
12969     
12970     /**
12971      * Removes and destroys this button
12972      */
12973     destroy : function(){
12974         Roo.Toolbar.Button.superclass.destroy.call(this);
12975         this.td.parentNode.removeChild(this.td);
12976     },
12977     
12978     /**
12979      * Shows this button
12980      */
12981     show: function(){
12982         this.hidden = false;
12983         this.td.style.display = "";
12984     },
12985     
12986     /**
12987      * Hides this button
12988      */
12989     hide: function(){
12990         this.hidden = true;
12991         this.td.style.display = "none";
12992     },
12993
12994     /**
12995      * Disables this item
12996      */
12997     disable : function(){
12998         Roo.fly(this.td).addClass("x-item-disabled");
12999         this.disabled = true;
13000     },
13001
13002     /**
13003      * Enables this item
13004      */
13005     enable : function(){
13006         Roo.fly(this.td).removeClass("x-item-disabled");
13007         this.disabled = false;
13008     }
13009 });
13010 // backwards compat
13011 Roo.ToolbarButton = Roo.Toolbar.Button;
13012
13013 /**
13014  * @class Roo.Toolbar.SplitButton
13015  * @extends Roo.SplitButton
13016  * A menu button that renders into a toolbar.
13017  * @constructor
13018  * Creates a new SplitButton
13019  * @param {Object} config A standard {@link Roo.SplitButton} config object
13020  */
13021 Roo.Toolbar.SplitButton = function(config){
13022     Roo.Toolbar.SplitButton.superclass.constructor.call(this, null, config);
13023 };
13024 Roo.extend(Roo.Toolbar.SplitButton, Roo.SplitButton, {
13025     render : function(td){
13026         this.td = td;
13027         Roo.Toolbar.SplitButton.superclass.render.call(this, td);
13028     },
13029     
13030     /**
13031      * Removes and destroys this button
13032      */
13033     destroy : function(){
13034         Roo.Toolbar.SplitButton.superclass.destroy.call(this);
13035         this.td.parentNode.removeChild(this.td);
13036     },
13037     
13038     /**
13039      * Shows this button
13040      */
13041     show: function(){
13042         this.hidden = false;
13043         this.td.style.display = "";
13044     },
13045     
13046     /**
13047      * Hides this button
13048      */
13049     hide: function(){
13050         this.hidden = true;
13051         this.td.style.display = "none";
13052     }
13053 });
13054
13055 // backwards compat
13056 Roo.Toolbar.MenuButton = Roo.Toolbar.SplitButton;/*
13057  * Based on:
13058  * Ext JS Library 1.1.1
13059  * Copyright(c) 2006-2007, Ext JS, LLC.
13060  *
13061  * Originally Released Under LGPL - original licence link has changed is not relivant.
13062  *
13063  * Fork - LGPL
13064  * <script type="text/javascript">
13065  */
13066  
13067 /**
13068  * @class Roo.PagingToolbar
13069  * @extends Roo.Toolbar
13070  * A specialized toolbar that is bound to a {@link Roo.data.Store} and provides automatic paging controls.
13071  * @constructor
13072  * Create a new PagingToolbar
13073  * @param {Object} config The config object
13074  */
13075 Roo.PagingToolbar = function(el, ds, config)
13076 {
13077     // old args format still supported... - xtype is prefered..
13078     if (typeof(el) == 'object' && el.xtype) {
13079         // created from xtype...
13080         config = el;
13081         ds = el.dataSource;
13082         el = config.container;
13083     }
13084     var items = [];
13085     if (config.items) {
13086         items = config.items;
13087         config.items = [];
13088     }
13089     
13090     Roo.PagingToolbar.superclass.constructor.call(this, el, null, config);
13091     this.ds = ds;
13092     this.cursor = 0;
13093     this.renderButtons(this.el);
13094     this.bind(ds);
13095     
13096     // supprot items array.
13097    
13098     Roo.each(items, function(e) {
13099         this.add(Roo.factory(e));
13100     },this);
13101     
13102 };
13103
13104 Roo.extend(Roo.PagingToolbar, Roo.Toolbar, {
13105     /**
13106      * @cfg {Roo.data.Store} dataSource
13107      * The underlying data store providing the paged data
13108      */
13109     /**
13110      * @cfg {String/HTMLElement/Element} container
13111      * container The id or element that will contain the toolbar
13112      */
13113     /**
13114      * @cfg {Boolean} displayInfo
13115      * True to display the displayMsg (defaults to false)
13116      */
13117     /**
13118      * @cfg {Number} pageSize
13119      * The number of records to display per page (defaults to 20)
13120      */
13121     pageSize: 20,
13122     /**
13123      * @cfg {String} displayMsg
13124      * The paging status message to display (defaults to "Displaying {start} - {end} of {total}")
13125      */
13126     displayMsg : 'Displaying {0} - {1} of {2}',
13127     /**
13128      * @cfg {String} emptyMsg
13129      * The message to display when no records are found (defaults to "No data to display")
13130      */
13131     emptyMsg : 'No data to display',
13132     /**
13133      * Customizable piece of the default paging text (defaults to "Page")
13134      * @type String
13135      */
13136     beforePageText : "Page",
13137     /**
13138      * Customizable piece of the default paging text (defaults to "of %0")
13139      * @type String
13140      */
13141     afterPageText : "of {0}",
13142     /**
13143      * Customizable piece of the default paging text (defaults to "First Page")
13144      * @type String
13145      */
13146     firstText : "First Page",
13147     /**
13148      * Customizable piece of the default paging text (defaults to "Previous Page")
13149      * @type String
13150      */
13151     prevText : "Previous Page",
13152     /**
13153      * Customizable piece of the default paging text (defaults to "Next Page")
13154      * @type String
13155      */
13156     nextText : "Next Page",
13157     /**
13158      * Customizable piece of the default paging text (defaults to "Last Page")
13159      * @type String
13160      */
13161     lastText : "Last Page",
13162     /**
13163      * Customizable piece of the default paging text (defaults to "Refresh")
13164      * @type String
13165      */
13166     refreshText : "Refresh",
13167
13168     // private
13169     renderButtons : function(el){
13170         Roo.PagingToolbar.superclass.render.call(this, el);
13171         this.first = this.addButton({
13172             tooltip: this.firstText,
13173             cls: "x-btn-icon x-grid-page-first",
13174             disabled: true,
13175             handler: this.onClick.createDelegate(this, ["first"])
13176         });
13177         this.prev = this.addButton({
13178             tooltip: this.prevText,
13179             cls: "x-btn-icon x-grid-page-prev",
13180             disabled: true,
13181             handler: this.onClick.createDelegate(this, ["prev"])
13182         });
13183         //this.addSeparator();
13184         this.add(this.beforePageText);
13185         this.field = Roo.get(this.addDom({
13186            tag: "input",
13187            type: "text",
13188            size: "3",
13189            value: "1",
13190            cls: "x-grid-page-number"
13191         }).el);
13192         this.field.on("keydown", this.onPagingKeydown, this);
13193         this.field.on("focus", function(){this.dom.select();});
13194         this.afterTextEl = this.addText(String.format(this.afterPageText, 1));
13195         this.field.setHeight(18);
13196         //this.addSeparator();
13197         this.next = this.addButton({
13198             tooltip: this.nextText,
13199             cls: "x-btn-icon x-grid-page-next",
13200             disabled: true,
13201             handler: this.onClick.createDelegate(this, ["next"])
13202         });
13203         this.last = this.addButton({
13204             tooltip: this.lastText,
13205             cls: "x-btn-icon x-grid-page-last",
13206             disabled: true,
13207             handler: this.onClick.createDelegate(this, ["last"])
13208         });
13209         //this.addSeparator();
13210         this.loading = this.addButton({
13211             tooltip: this.refreshText,
13212             cls: "x-btn-icon x-grid-loading",
13213             handler: this.onClick.createDelegate(this, ["refresh"])
13214         });
13215
13216         if(this.displayInfo){
13217             this.displayEl = Roo.fly(this.el.dom.firstChild).createChild({cls:'x-paging-info'});
13218         }
13219     },
13220
13221     // private
13222     updateInfo : function(){
13223         if(this.displayEl){
13224             var count = this.ds.getCount();
13225             var msg = count == 0 ?
13226                 this.emptyMsg :
13227                 String.format(
13228                     this.displayMsg,
13229                     this.cursor+1, this.cursor+count, this.ds.getTotalCount()    
13230                 );
13231             this.displayEl.update(msg);
13232         }
13233     },
13234
13235     // private
13236     onLoad : function(ds, r, o){
13237        this.cursor = o.params ? o.params.start : 0;
13238        var d = this.getPageData(), ap = d.activePage, ps = d.pages;
13239
13240        this.afterTextEl.el.innerHTML = String.format(this.afterPageText, d.pages);
13241        this.field.dom.value = ap;
13242        this.first.setDisabled(ap == 1);
13243        this.prev.setDisabled(ap == 1);
13244        this.next.setDisabled(ap == ps);
13245        this.last.setDisabled(ap == ps);
13246        this.loading.enable();
13247        this.updateInfo();
13248     },
13249
13250     // private
13251     getPageData : function(){
13252         var total = this.ds.getTotalCount();
13253         return {
13254             total : total,
13255             activePage : Math.ceil((this.cursor+this.pageSize)/this.pageSize),
13256             pages :  total < this.pageSize ? 1 : Math.ceil(total/this.pageSize)
13257         };
13258     },
13259
13260     // private
13261     onLoadError : function(){
13262         this.loading.enable();
13263     },
13264
13265     // private
13266     onPagingKeydown : function(e){
13267         var k = e.getKey();
13268         var d = this.getPageData();
13269         if(k == e.RETURN){
13270             var v = this.field.dom.value, pageNum;
13271             if(!v || isNaN(pageNum = parseInt(v, 10))){
13272                 this.field.dom.value = d.activePage;
13273                 return;
13274             }
13275             pageNum = Math.min(Math.max(1, pageNum), d.pages) - 1;
13276             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
13277             e.stopEvent();
13278         }
13279         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))
13280         {
13281           var pageNum = (k == e.HOME || (k == e.DOWN && e.ctrlKey) || (k == e.LEFT && e.ctrlKey) || (k == e.PAGEDOWN && e.ctrlKey)) ? 1 : d.pages;
13282           this.field.dom.value = pageNum;
13283           this.ds.load({params:{start: (pageNum - 1) * this.pageSize, limit: this.pageSize}});
13284           e.stopEvent();
13285         }
13286         else if(k == e.UP || k == e.RIGHT || k == e.PAGEUP || k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN)
13287         {
13288           var v = this.field.dom.value, pageNum; 
13289           var increment = (e.shiftKey) ? 10 : 1;
13290           if(k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN)
13291             increment *= -1;
13292           if(!v || isNaN(pageNum = parseInt(v, 10))) {
13293             this.field.dom.value = d.activePage;
13294             return;
13295           }
13296           else if(parseInt(v, 10) + increment >= 1 & parseInt(v, 10) + increment <= d.pages)
13297           {
13298             this.field.dom.value = parseInt(v, 10) + increment;
13299             pageNum = Math.min(Math.max(1, pageNum + increment), d.pages) - 1;
13300             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
13301           }
13302           e.stopEvent();
13303         }
13304     },
13305
13306     // private
13307     beforeLoad : function(){
13308         if(this.loading){
13309             this.loading.disable();
13310         }
13311     },
13312
13313     // private
13314     onClick : function(which){
13315         var ds = this.ds;
13316         switch(which){
13317             case "first":
13318                 ds.load({params:{start: 0, limit: this.pageSize}});
13319             break;
13320             case "prev":
13321                 ds.load({params:{start: Math.max(0, this.cursor-this.pageSize), limit: this.pageSize}});
13322             break;
13323             case "next":
13324                 ds.load({params:{start: this.cursor+this.pageSize, limit: this.pageSize}});
13325             break;
13326             case "last":
13327                 var total = ds.getTotalCount();
13328                 var extra = total % this.pageSize;
13329                 var lastStart = extra ? (total - extra) : total-this.pageSize;
13330                 ds.load({params:{start: lastStart, limit: this.pageSize}});
13331             break;
13332             case "refresh":
13333                 ds.load({params:{start: this.cursor, limit: this.pageSize}});
13334             break;
13335         }
13336     },
13337
13338     /**
13339      * Unbinds the paging toolbar from the specified {@link Roo.data.Store}
13340      * @param {Roo.data.Store} store The data store to unbind
13341      */
13342     unbind : function(ds){
13343         ds.un("beforeload", this.beforeLoad, this);
13344         ds.un("load", this.onLoad, this);
13345         ds.un("loadexception", this.onLoadError, this);
13346         ds.un("remove", this.updateInfo, this);
13347         ds.un("add", this.updateInfo, this);
13348         this.ds = undefined;
13349     },
13350
13351     /**
13352      * Binds the paging toolbar to the specified {@link Roo.data.Store}
13353      * @param {Roo.data.Store} store The data store to bind
13354      */
13355     bind : function(ds){
13356         ds.on("beforeload", this.beforeLoad, this);
13357         ds.on("load", this.onLoad, this);
13358         ds.on("loadexception", this.onLoadError, this);
13359         ds.on("remove", this.updateInfo, this);
13360         ds.on("add", this.updateInfo, this);
13361         this.ds = ds;
13362     }
13363 });/*
13364  * Based on:
13365  * Ext JS Library 1.1.1
13366  * Copyright(c) 2006-2007, Ext JS, LLC.
13367  *
13368  * Originally Released Under LGPL - original licence link has changed is not relivant.
13369  *
13370  * Fork - LGPL
13371  * <script type="text/javascript">
13372  */
13373
13374 /**
13375  * @class Roo.Resizable
13376  * @extends Roo.util.Observable
13377  * <p>Applies drag handles to an element to make it resizable. The drag handles are inserted into the element
13378  * and positioned absolute. Some elements, such as a textarea or image, don't support this. To overcome that, you can wrap
13379  * 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
13380  * the element will be wrapped for you automatically.</p>
13381  * <p>Here is the list of valid resize handles:</p>
13382  * <pre>
13383 Value   Description
13384 ------  -------------------
13385  'n'     north
13386  's'     south
13387  'e'     east
13388  'w'     west
13389  'nw'    northwest
13390  'sw'    southwest
13391  'se'    southeast
13392  'ne'    northeast
13393  'hd'    horizontal drag
13394  'all'   all
13395 </pre>
13396  * <p>Here's an example showing the creation of a typical Resizable:</p>
13397  * <pre><code>
13398 var resizer = new Roo.Resizable("element-id", {
13399     handles: 'all',
13400     minWidth: 200,
13401     minHeight: 100,
13402     maxWidth: 500,
13403     maxHeight: 400,
13404     pinned: true
13405 });
13406 resizer.on("resize", myHandler);
13407 </code></pre>
13408  * <p>To hide a particular handle, set its display to none in CSS, or through script:<br>
13409  * resizer.east.setDisplayed(false);</p>
13410  * @cfg {Boolean/String/Element} resizeChild True to resize the first child, or id/element to resize (defaults to false)
13411  * @cfg {Array/String} adjustments String "auto" or an array [width, height] with values to be <b>added</b> to the
13412  * resize operation's new size (defaults to [0, 0])
13413  * @cfg {Number} minWidth The minimum width for the element (defaults to 5)
13414  * @cfg {Number} minHeight The minimum height for the element (defaults to 5)
13415  * @cfg {Number} maxWidth The maximum width for the element (defaults to 10000)
13416  * @cfg {Number} maxHeight The maximum height for the element (defaults to 10000)
13417  * @cfg {Boolean} enabled False to disable resizing (defaults to true)
13418  * @cfg {Boolean} wrap True to wrap an element with a div if needed (required for textareas and images, defaults to false)
13419  * @cfg {Number} width The width of the element in pixels (defaults to null)
13420  * @cfg {Number} height The height of the element in pixels (defaults to null)
13421  * @cfg {Boolean} animate True to animate the resize (not compatible with dynamic sizing, defaults to false)
13422  * @cfg {Number} duration Animation duration if animate = true (defaults to .35)
13423  * @cfg {Boolean} dynamic True to resize the element while dragging instead of using a proxy (defaults to false)
13424  * @cfg {String} handles String consisting of the resize handles to display (defaults to undefined)
13425  * @cfg {Boolean} multiDirectional <b>Deprecated</b>.  The old style of adding multi-direction resize handles, deprecated
13426  * in favor of the handles config option (defaults to false)
13427  * @cfg {Boolean} disableTrackOver True to disable mouse tracking. This is only applied at config time. (defaults to false)
13428  * @cfg {String} easing Animation easing if animate = true (defaults to 'easingOutStrong')
13429  * @cfg {Number} widthIncrement The increment to snap the width resize in pixels (dynamic must be true, defaults to 0)
13430  * @cfg {Number} heightIncrement The increment to snap the height resize in pixels (dynamic must be true, defaults to 0)
13431  * @cfg {Boolean} pinned True to ensure that the resize handles are always visible, false to display them only when the
13432  * user mouses over the resizable borders. This is only applied at config time. (defaults to false)
13433  * @cfg {Boolean} preserveRatio True to preserve the original ratio between height and width during resize (defaults to false)
13434  * @cfg {Boolean} transparent True for transparent handles. This is only applied at config time. (defaults to false)
13435  * @cfg {Number} minX The minimum allowed page X for the element (only used for west resizing, defaults to 0)
13436  * @cfg {Number} minY The minimum allowed page Y for the element (only used for north resizing, defaults to 0)
13437  * @cfg {Boolean} draggable Convenience to initialize drag drop (defaults to false)
13438  * @constructor
13439  * Create a new resizable component
13440  * @param {String/HTMLElement/Roo.Element} el The id or element to resize
13441  * @param {Object} config configuration options
13442   */
13443 Roo.Resizable = function(el, config)
13444 {
13445     this.el = Roo.get(el);
13446
13447     if(config && config.wrap){
13448         config.resizeChild = this.el;
13449         this.el = this.el.wrap(typeof config.wrap == "object" ? config.wrap : {cls:"xresizable-wrap"});
13450         this.el.id = this.el.dom.id = config.resizeChild.id + "-rzwrap";
13451         this.el.setStyle("overflow", "hidden");
13452         this.el.setPositioning(config.resizeChild.getPositioning());
13453         config.resizeChild.clearPositioning();
13454         if(!config.width || !config.height){
13455             var csize = config.resizeChild.getSize();
13456             this.el.setSize(csize.width, csize.height);
13457         }
13458         if(config.pinned && !config.adjustments){
13459             config.adjustments = "auto";
13460         }
13461     }
13462
13463     this.proxy = this.el.createProxy({tag: "div", cls: "x-resizable-proxy", id: this.el.id + "-rzproxy"});
13464     this.proxy.unselectable();
13465     this.proxy.enableDisplayMode('block');
13466
13467     Roo.apply(this, config);
13468
13469     if(this.pinned){
13470         this.disableTrackOver = true;
13471         this.el.addClass("x-resizable-pinned");
13472     }
13473     // if the element isn't positioned, make it relative
13474     var position = this.el.getStyle("position");
13475     if(position != "absolute" && position != "fixed"){
13476         this.el.setStyle("position", "relative");
13477     }
13478     if(!this.handles){ // no handles passed, must be legacy style
13479         this.handles = 's,e,se';
13480         if(this.multiDirectional){
13481             this.handles += ',n,w';
13482         }
13483     }
13484     if(this.handles == "all"){
13485         this.handles = "n s e w ne nw se sw";
13486     }
13487     var hs = this.handles.split(/\s*?[,;]\s*?| /);
13488     var ps = Roo.Resizable.positions;
13489     for(var i = 0, len = hs.length; i < len; i++){
13490         if(hs[i] && ps[hs[i]]){
13491             var pos = ps[hs[i]];
13492             this[pos] = new Roo.Resizable.Handle(this, pos, this.disableTrackOver, this.transparent);
13493         }
13494     }
13495     // legacy
13496     this.corner = this.southeast;
13497     
13498     // updateBox = the box can move..
13499     if(this.handles.indexOf("n") != -1 || this.handles.indexOf("w") != -1 || this.handles.indexOf("hd") != -1) {
13500         this.updateBox = true;
13501     }
13502
13503     this.activeHandle = null;
13504
13505     if(this.resizeChild){
13506         if(typeof this.resizeChild == "boolean"){
13507             this.resizeChild = Roo.get(this.el.dom.firstChild, true);
13508         }else{
13509             this.resizeChild = Roo.get(this.resizeChild, true);
13510         }
13511     }
13512     
13513     if(this.adjustments == "auto"){
13514         var rc = this.resizeChild;
13515         var hw = this.west, he = this.east, hn = this.north, hs = this.south;
13516         if(rc && (hw || hn)){
13517             rc.position("relative");
13518             rc.setLeft(hw ? hw.el.getWidth() : 0);
13519             rc.setTop(hn ? hn.el.getHeight() : 0);
13520         }
13521         this.adjustments = [
13522             (he ? -he.el.getWidth() : 0) + (hw ? -hw.el.getWidth() : 0),
13523             (hn ? -hn.el.getHeight() : 0) + (hs ? -hs.el.getHeight() : 0) -1
13524         ];
13525     }
13526
13527     if(this.draggable){
13528         this.dd = this.dynamic ?
13529             this.el.initDD(null) : this.el.initDDProxy(null, {dragElId: this.proxy.id});
13530         this.dd.setHandleElId(this.resizeChild ? this.resizeChild.id : this.el.id);
13531     }
13532
13533     // public events
13534     this.addEvents({
13535         /**
13536          * @event beforeresize
13537          * Fired before resize is allowed. Set enabled to false to cancel resize.
13538          * @param {Roo.Resizable} this
13539          * @param {Roo.EventObject} e The mousedown event
13540          */
13541         "beforeresize" : true,
13542         /**
13543          * @event resizing
13544          * Fired a resizing.
13545          * @param {Roo.Resizable} this
13546          * @param {Number} x The new x position
13547          * @param {Number} y The new y position
13548          * @param {Number} w The new w width
13549          * @param {Number} h The new h hight
13550          * @param {Roo.EventObject} e The mouseup event
13551          */
13552         "resizing" : true,
13553         /**
13554          * @event resize
13555          * Fired after a resize.
13556          * @param {Roo.Resizable} this
13557          * @param {Number} width The new width
13558          * @param {Number} height The new height
13559          * @param {Roo.EventObject} e The mouseup event
13560          */
13561         "resize" : true
13562     });
13563
13564     if(this.width !== null && this.height !== null){
13565         this.resizeTo(this.width, this.height);
13566     }else{
13567         this.updateChildSize();
13568     }
13569     if(Roo.isIE){
13570         this.el.dom.style.zoom = 1;
13571     }
13572     Roo.Resizable.superclass.constructor.call(this);
13573 };
13574
13575 Roo.extend(Roo.Resizable, Roo.util.Observable, {
13576         resizeChild : false,
13577         adjustments : [0, 0],
13578         minWidth : 5,
13579         minHeight : 5,
13580         maxWidth : 10000,
13581         maxHeight : 10000,
13582         enabled : true,
13583         animate : false,
13584         duration : .35,
13585         dynamic : false,
13586         handles : false,
13587         multiDirectional : false,
13588         disableTrackOver : false,
13589         easing : 'easeOutStrong',
13590         widthIncrement : 0,
13591         heightIncrement : 0,
13592         pinned : false,
13593         width : null,
13594         height : null,
13595         preserveRatio : false,
13596         transparent: false,
13597         minX: 0,
13598         minY: 0,
13599         draggable: false,
13600
13601         /**
13602          * @cfg {String/HTMLElement/Element} constrainTo Constrain the resize to a particular element
13603          */
13604         constrainTo: undefined,
13605         /**
13606          * @cfg {Roo.lib.Region} resizeRegion Constrain the resize to a particular region
13607          */
13608         resizeRegion: undefined,
13609
13610
13611     /**
13612      * Perform a manual resize
13613      * @param {Number} width
13614      * @param {Number} height
13615      */
13616     resizeTo : function(width, height){
13617         this.el.setSize(width, height);
13618         this.updateChildSize();
13619         this.fireEvent("resize", this, width, height, null);
13620     },
13621
13622     // private
13623     startSizing : function(e, handle){
13624         this.fireEvent("beforeresize", this, e);
13625         if(this.enabled){ // 2nd enabled check in case disabled before beforeresize handler
13626
13627             if(!this.overlay){
13628                 this.overlay = this.el.createProxy({tag: "div", cls: "x-resizable-overlay", html: "&#160;"});
13629                 this.overlay.unselectable();
13630                 this.overlay.enableDisplayMode("block");
13631                 this.overlay.on("mousemove", this.onMouseMove, this);
13632                 this.overlay.on("mouseup", this.onMouseUp, this);
13633             }
13634             this.overlay.setStyle("cursor", handle.el.getStyle("cursor"));
13635
13636             this.resizing = true;
13637             this.startBox = this.el.getBox();
13638             this.startPoint = e.getXY();
13639             this.offsets = [(this.startBox.x + this.startBox.width) - this.startPoint[0],
13640                             (this.startBox.y + this.startBox.height) - this.startPoint[1]];
13641
13642             this.overlay.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
13643             this.overlay.show();
13644
13645             if(this.constrainTo) {
13646                 var ct = Roo.get(this.constrainTo);
13647                 this.resizeRegion = ct.getRegion().adjust(
13648                     ct.getFrameWidth('t'),
13649                     ct.getFrameWidth('l'),
13650                     -ct.getFrameWidth('b'),
13651                     -ct.getFrameWidth('r')
13652                 );
13653             }
13654
13655             this.proxy.setStyle('visibility', 'hidden'); // workaround display none
13656             this.proxy.show();
13657             this.proxy.setBox(this.startBox);
13658             if(!this.dynamic){
13659                 this.proxy.setStyle('visibility', 'visible');
13660             }
13661         }
13662     },
13663
13664     // private
13665     onMouseDown : function(handle, e){
13666         if(this.enabled){
13667             e.stopEvent();
13668             this.activeHandle = handle;
13669             this.startSizing(e, handle);
13670         }
13671     },
13672
13673     // private
13674     onMouseUp : function(e){
13675         var size = this.resizeElement();
13676         this.resizing = false;
13677         this.handleOut();
13678         this.overlay.hide();
13679         this.proxy.hide();
13680         this.fireEvent("resize", this, size.width, size.height, e);
13681     },
13682
13683     // private
13684     updateChildSize : function(){
13685         
13686         if(this.resizeChild){
13687             var el = this.el;
13688             var child = this.resizeChild;
13689             var adj = this.adjustments;
13690             if(el.dom.offsetWidth){
13691                 var b = el.getSize(true);
13692                 child.setSize(b.width+adj[0], b.height+adj[1]);
13693             }
13694             // Second call here for IE
13695             // The first call enables instant resizing and
13696             // the second call corrects scroll bars if they
13697             // exist
13698             if(Roo.isIE){
13699                 setTimeout(function(){
13700                     if(el.dom.offsetWidth){
13701                         var b = el.getSize(true);
13702                         child.setSize(b.width+adj[0], b.height+adj[1]);
13703                     }
13704                 }, 10);
13705             }
13706         }
13707     },
13708
13709     // private
13710     snap : function(value, inc, min){
13711         if(!inc || !value) return value;
13712         var newValue = value;
13713         var m = value % inc;
13714         if(m > 0){
13715             if(m > (inc/2)){
13716                 newValue = value + (inc-m);
13717             }else{
13718                 newValue = value - m;
13719             }
13720         }
13721         return Math.max(min, newValue);
13722     },
13723
13724     // private
13725     resizeElement : function(){
13726         var box = this.proxy.getBox();
13727         if(this.updateBox){
13728             this.el.setBox(box, false, this.animate, this.duration, null, this.easing);
13729         }else{
13730             this.el.setSize(box.width, box.height, this.animate, this.duration, null, this.easing);
13731         }
13732         this.updateChildSize();
13733         if(!this.dynamic){
13734             this.proxy.hide();
13735         }
13736         return box;
13737     },
13738
13739     // private
13740     constrain : function(v, diff, m, mx){
13741         if(v - diff < m){
13742             diff = v - m;
13743         }else if(v - diff > mx){
13744             diff = mx - v;
13745         }
13746         return diff;
13747     },
13748
13749     // private
13750     onMouseMove : function(e){
13751         
13752         if(this.enabled){
13753             try{// try catch so if something goes wrong the user doesn't get hung
13754
13755             if(this.resizeRegion && !this.resizeRegion.contains(e.getPoint())) {
13756                 return;
13757             }
13758
13759             //var curXY = this.startPoint;
13760             var curSize = this.curSize || this.startBox;
13761             var x = this.startBox.x, y = this.startBox.y;
13762             var ox = x, oy = y;
13763             var w = curSize.width, h = curSize.height;
13764             var ow = w, oh = h;
13765             var mw = this.minWidth, mh = this.minHeight;
13766             var mxw = this.maxWidth, mxh = this.maxHeight;
13767             var wi = this.widthIncrement;
13768             var hi = this.heightIncrement;
13769
13770             var eventXY = e.getXY();
13771             var diffX = -(this.startPoint[0] - Math.max(this.minX, eventXY[0]));
13772             var diffY = -(this.startPoint[1] - Math.max(this.minY, eventXY[1]));
13773
13774             var pos = this.activeHandle.position;
13775
13776             switch(pos){
13777                 case "east":
13778                     w += diffX;
13779                     w = Math.min(Math.max(mw, w), mxw);
13780                     break;
13781              
13782                 case "south":
13783                     h += diffY;
13784                     h = Math.min(Math.max(mh, h), mxh);
13785                     break;
13786                 case "southeast":
13787                     w += diffX;
13788                     h += diffY;
13789                     w = Math.min(Math.max(mw, w), mxw);
13790                     h = Math.min(Math.max(mh, h), mxh);
13791                     break;
13792                 case "north":
13793                     diffY = this.constrain(h, diffY, mh, mxh);
13794                     y += diffY;
13795                     h -= diffY;
13796                     break;
13797                 case "hdrag":
13798                     
13799                     if (wi) {
13800                         var adiffX = Math.abs(diffX);
13801                         var sub = (adiffX % wi); // how much 
13802                         if (sub > (wi/2)) { // far enough to snap
13803                             diffX = (diffX > 0) ? diffX-sub + wi : diffX+sub - wi;
13804                         } else {
13805                             // remove difference.. 
13806                             diffX = (diffX > 0) ? diffX-sub : diffX+sub;
13807                         }
13808                     }
13809                     x += diffX;
13810                     x = Math.max(this.minX, x);
13811                     break;
13812                 case "west":
13813                     diffX = this.constrain(w, diffX, mw, mxw);
13814                     x += diffX;
13815                     w -= diffX;
13816                     break;
13817                 case "northeast":
13818                     w += diffX;
13819                     w = Math.min(Math.max(mw, w), mxw);
13820                     diffY = this.constrain(h, diffY, mh, mxh);
13821                     y += diffY;
13822                     h -= diffY;
13823                     break;
13824                 case "northwest":
13825                     diffX = this.constrain(w, diffX, mw, mxw);
13826                     diffY = this.constrain(h, diffY, mh, mxh);
13827                     y += diffY;
13828                     h -= diffY;
13829                     x += diffX;
13830                     w -= diffX;
13831                     break;
13832                case "southwest":
13833                     diffX = this.constrain(w, diffX, mw, mxw);
13834                     h += diffY;
13835                     h = Math.min(Math.max(mh, h), mxh);
13836                     x += diffX;
13837                     w -= diffX;
13838                     break;
13839             }
13840
13841             var sw = this.snap(w, wi, mw);
13842             var sh = this.snap(h, hi, mh);
13843             if(sw != w || sh != h){
13844                 switch(pos){
13845                     case "northeast":
13846                         y -= sh - h;
13847                     break;
13848                     case "north":
13849                         y -= sh - h;
13850                         break;
13851                     case "southwest":
13852                         x -= sw - w;
13853                     break;
13854                     case "west":
13855                         x -= sw - w;
13856                         break;
13857                     case "northwest":
13858                         x -= sw - w;
13859                         y -= sh - h;
13860                     break;
13861                 }
13862                 w = sw;
13863                 h = sh;
13864             }
13865
13866             if(this.preserveRatio){
13867                 switch(pos){
13868                     case "southeast":
13869                     case "east":
13870                         h = oh * (w/ow);
13871                         h = Math.min(Math.max(mh, h), mxh);
13872                         w = ow * (h/oh);
13873                        break;
13874                     case "south":
13875                         w = ow * (h/oh);
13876                         w = Math.min(Math.max(mw, w), mxw);
13877                         h = oh * (w/ow);
13878                         break;
13879                     case "northeast":
13880                         w = ow * (h/oh);
13881                         w = Math.min(Math.max(mw, w), mxw);
13882                         h = oh * (w/ow);
13883                     break;
13884                     case "north":
13885                         var tw = w;
13886                         w = ow * (h/oh);
13887                         w = Math.min(Math.max(mw, w), mxw);
13888                         h = oh * (w/ow);
13889                         x += (tw - w) / 2;
13890                         break;
13891                     case "southwest":
13892                         h = oh * (w/ow);
13893                         h = Math.min(Math.max(mh, h), mxh);
13894                         var tw = w;
13895                         w = ow * (h/oh);
13896                         x += tw - w;
13897                         break;
13898                     case "west":
13899                         var th = h;
13900                         h = oh * (w/ow);
13901                         h = Math.min(Math.max(mh, h), mxh);
13902                         y += (th - h) / 2;
13903                         var tw = w;
13904                         w = ow * (h/oh);
13905                         x += tw - w;
13906                        break;
13907                     case "northwest":
13908                         var tw = w;
13909                         var th = h;
13910                         h = oh * (w/ow);
13911                         h = Math.min(Math.max(mh, h), mxh);
13912                         w = ow * (h/oh);
13913                         y += th - h;
13914                         x += tw - w;
13915                        break;
13916
13917                 }
13918             }
13919             if (pos == 'hdrag') {
13920                 w = ow;
13921             }
13922             this.proxy.setBounds(x, y, w, h);
13923             if(this.dynamic){
13924                 this.resizeElement();
13925             }
13926             }catch(e){}
13927         }
13928         this.fireEvent("resizing", this, x, y, w, h, e);
13929     },
13930
13931     // private
13932     handleOver : function(){
13933         if(this.enabled){
13934             this.el.addClass("x-resizable-over");
13935         }
13936     },
13937
13938     // private
13939     handleOut : function(){
13940         if(!this.resizing){
13941             this.el.removeClass("x-resizable-over");
13942         }
13943     },
13944
13945     /**
13946      * Returns the element this component is bound to.
13947      * @return {Roo.Element}
13948      */
13949     getEl : function(){
13950         return this.el;
13951     },
13952
13953     /**
13954      * Returns the resizeChild element (or null).
13955      * @return {Roo.Element}
13956      */
13957     getResizeChild : function(){
13958         return this.resizeChild;
13959     },
13960     groupHandler : function()
13961     {
13962         
13963     },
13964     /**
13965      * Destroys this resizable. If the element was wrapped and
13966      * removeEl is not true then the element remains.
13967      * @param {Boolean} removeEl (optional) true to remove the element from the DOM
13968      */
13969     destroy : function(removeEl){
13970         this.proxy.remove();
13971         if(this.overlay){
13972             this.overlay.removeAllListeners();
13973             this.overlay.remove();
13974         }
13975         var ps = Roo.Resizable.positions;
13976         for(var k in ps){
13977             if(typeof ps[k] != "function" && this[ps[k]]){
13978                 var h = this[ps[k]];
13979                 h.el.removeAllListeners();
13980                 h.el.remove();
13981             }
13982         }
13983         if(removeEl){
13984             this.el.update("");
13985             this.el.remove();
13986         }
13987     }
13988 });
13989
13990 // private
13991 // hash to map config positions to true positions
13992 Roo.Resizable.positions = {
13993     n: "north", s: "south", e: "east", w: "west", se: "southeast", sw: "southwest", nw: "northwest", ne: "northeast", 
13994     hd: "hdrag"
13995 };
13996
13997 // private
13998 Roo.Resizable.Handle = function(rz, pos, disableTrackOver, transparent){
13999     if(!this.tpl){
14000         // only initialize the template if resizable is used
14001         var tpl = Roo.DomHelper.createTemplate(
14002             {tag: "div", cls: "x-resizable-handle x-resizable-handle-{0}"}
14003         );
14004         tpl.compile();
14005         Roo.Resizable.Handle.prototype.tpl = tpl;
14006     }
14007     this.position = pos;
14008     this.rz = rz;
14009     // show north drag fro topdra
14010     var handlepos = pos == 'hdrag' ? 'north' : pos;
14011     
14012     this.el = this.tpl.append(rz.el.dom, [handlepos], true);
14013     if (pos == 'hdrag') {
14014         this.el.setStyle('cursor', 'pointer');
14015     }
14016     this.el.unselectable();
14017     if(transparent){
14018         this.el.setOpacity(0);
14019     }
14020     this.el.on("mousedown", this.onMouseDown, this);
14021     if(!disableTrackOver){
14022         this.el.on("mouseover", this.onMouseOver, this);
14023         this.el.on("mouseout", this.onMouseOut, this);
14024     }
14025 };
14026
14027 // private
14028 Roo.Resizable.Handle.prototype = {
14029     afterResize : function(rz){
14030         // do nothing
14031     },
14032     // private
14033     onMouseDown : function(e){
14034         this.rz.onMouseDown(this, e);
14035     },
14036     // private
14037     onMouseOver : function(e){
14038         this.rz.handleOver(this, e);
14039     },
14040     // private
14041     onMouseOut : function(e){
14042         this.rz.handleOut(this, e);
14043     }
14044 };/*
14045  * Based on:
14046  * Ext JS Library 1.1.1
14047  * Copyright(c) 2006-2007, Ext JS, LLC.
14048  *
14049  * Originally Released Under LGPL - original licence link has changed is not relivant.
14050  *
14051  * Fork - LGPL
14052  * <script type="text/javascript">
14053  */
14054
14055 /**
14056  * @class Roo.Editor
14057  * @extends Roo.Component
14058  * A base editor field that handles displaying/hiding on demand and has some built-in sizing and event handling logic.
14059  * @constructor
14060  * Create a new Editor
14061  * @param {Roo.form.Field} field The Field object (or descendant)
14062  * @param {Object} config The config object
14063  */
14064 Roo.Editor = function(field, config){
14065     Roo.Editor.superclass.constructor.call(this, config);
14066     this.field = field;
14067     this.addEvents({
14068         /**
14069              * @event beforestartedit
14070              * Fires when editing is initiated, but before the value changes.  Editing can be canceled by returning
14071              * false from the handler of this event.
14072              * @param {Editor} this
14073              * @param {Roo.Element} boundEl The underlying element bound to this editor
14074              * @param {Mixed} value The field value being set
14075              */
14076         "beforestartedit" : true,
14077         /**
14078              * @event startedit
14079              * Fires when this editor is displayed
14080              * @param {Roo.Element} boundEl The underlying element bound to this editor
14081              * @param {Mixed} value The starting field value
14082              */
14083         "startedit" : true,
14084         /**
14085              * @event beforecomplete
14086              * Fires after a change has been made to the field, but before the change is reflected in the underlying
14087              * field.  Saving the change to the field can be canceled by returning false from the handler of this event.
14088              * Note that if the value has not changed and ignoreNoChange = true, the editing will still end but this
14089              * event will not fire since no edit actually occurred.
14090              * @param {Editor} this
14091              * @param {Mixed} value The current field value
14092              * @param {Mixed} startValue The original field value
14093              */
14094         "beforecomplete" : true,
14095         /**
14096              * @event complete
14097              * Fires after editing is complete and any changed value has been written to the underlying field.
14098              * @param {Editor} this
14099              * @param {Mixed} value The current field value
14100              * @param {Mixed} startValue The original field value
14101              */
14102         "complete" : true,
14103         /**
14104          * @event specialkey
14105          * Fires when any key related to navigation (arrows, tab, enter, esc, etc.) is pressed.  You can check
14106          * {@link Roo.EventObject#getKey} to determine which key was pressed.
14107          * @param {Roo.form.Field} this
14108          * @param {Roo.EventObject} e The event object
14109          */
14110         "specialkey" : true
14111     });
14112 };
14113
14114 Roo.extend(Roo.Editor, Roo.Component, {
14115     /**
14116      * @cfg {Boolean/String} autosize
14117      * True for the editor to automatically adopt the size of the underlying field, "width" to adopt the width only,
14118      * or "height" to adopt the height only (defaults to false)
14119      */
14120     /**
14121      * @cfg {Boolean} revertInvalid
14122      * True to automatically revert the field value and cancel the edit when the user completes an edit and the field
14123      * validation fails (defaults to true)
14124      */
14125     /**
14126      * @cfg {Boolean} ignoreNoChange
14127      * True to skip the the edit completion process (no save, no events fired) if the user completes an edit and
14128      * the value has not changed (defaults to false).  Applies only to string values - edits for other data types
14129      * will never be ignored.
14130      */
14131     /**
14132      * @cfg {Boolean} hideEl
14133      * False to keep the bound element visible while the editor is displayed (defaults to true)
14134      */
14135     /**
14136      * @cfg {Mixed} value
14137      * The data value of the underlying field (defaults to "")
14138      */
14139     value : "",
14140     /**
14141      * @cfg {String} alignment
14142      * The position to align to (see {@link Roo.Element#alignTo} for more details, defaults to "c-c?").
14143      */
14144     alignment: "c-c?",
14145     /**
14146      * @cfg {Boolean/String} shadow "sides" for sides/bottom only, "frame" for 4-way shadow, and "drop"
14147      * for bottom-right shadow (defaults to "frame")
14148      */
14149     shadow : "frame",
14150     /**
14151      * @cfg {Boolean} constrain True to constrain the editor to the viewport
14152      */
14153     constrain : false,
14154     /**
14155      * @cfg {Boolean} completeOnEnter True to complete the edit when the enter key is pressed (defaults to false)
14156      */
14157     completeOnEnter : false,
14158     /**
14159      * @cfg {Boolean} cancelOnEsc True to cancel the edit when the escape key is pressed (defaults to false)
14160      */
14161     cancelOnEsc : false,
14162     /**
14163      * @cfg {Boolean} updateEl True to update the innerHTML of the bound element when the update completes (defaults to false)
14164      */
14165     updateEl : false,
14166
14167     // private
14168     onRender : function(ct, position){
14169         this.el = new Roo.Layer({
14170             shadow: this.shadow,
14171             cls: "x-editor",
14172             parentEl : ct,
14173             shim : this.shim,
14174             shadowOffset:4,
14175             id: this.id,
14176             constrain: this.constrain
14177         });
14178         this.el.setStyle("overflow", Roo.isGecko ? "auto" : "hidden");
14179         if(this.field.msgTarget != 'title'){
14180             this.field.msgTarget = 'qtip';
14181         }
14182         this.field.render(this.el);
14183         if(Roo.isGecko){
14184             this.field.el.dom.setAttribute('autocomplete', 'off');
14185         }
14186         this.field.on("specialkey", this.onSpecialKey, this);
14187         if(this.swallowKeys){
14188             this.field.el.swallowEvent(['keydown','keypress']);
14189         }
14190         this.field.show();
14191         this.field.on("blur", this.onBlur, this);
14192         if(this.field.grow){
14193             this.field.on("autosize", this.el.sync,  this.el, {delay:1});
14194         }
14195     },
14196
14197     onSpecialKey : function(field, e)
14198     {
14199         //Roo.log('editor onSpecialKey');
14200         if(this.completeOnEnter && e.getKey() == e.ENTER){
14201             e.stopEvent();
14202             this.completeEdit();
14203             return;
14204         }
14205         // do not fire special key otherwise it might hide close the editor...
14206         if(e.getKey() == e.ENTER){    
14207             return;
14208         }
14209         if(this.cancelOnEsc && e.getKey() == e.ESC){
14210             this.cancelEdit();
14211             return;
14212         } 
14213         this.fireEvent('specialkey', field, e);
14214     
14215     },
14216
14217     /**
14218      * Starts the editing process and shows the editor.
14219      * @param {String/HTMLElement/Element} el The element to edit
14220      * @param {String} value (optional) A value to initialize the editor with. If a value is not provided, it defaults
14221       * to the innerHTML of el.
14222      */
14223     startEdit : function(el, value){
14224         if(this.editing){
14225             this.completeEdit();
14226         }
14227         this.boundEl = Roo.get(el);
14228         var v = value !== undefined ? value : this.boundEl.dom.innerHTML;
14229         if(!this.rendered){
14230             this.render(this.parentEl || document.body);
14231         }
14232         if(this.fireEvent("beforestartedit", this, this.boundEl, v) === false){
14233             return;
14234         }
14235         this.startValue = v;
14236         this.field.setValue(v);
14237         if(this.autoSize){
14238             var sz = this.boundEl.getSize();
14239             switch(this.autoSize){
14240                 case "width":
14241                 this.setSize(sz.width,  "");
14242                 break;
14243                 case "height":
14244                 this.setSize("",  sz.height);
14245                 break;
14246                 default:
14247                 this.setSize(sz.width,  sz.height);
14248             }
14249         }
14250         this.el.alignTo(this.boundEl, this.alignment);
14251         this.editing = true;
14252         if(Roo.QuickTips){
14253             Roo.QuickTips.disable();
14254         }
14255         this.show();
14256     },
14257
14258     /**
14259      * Sets the height and width of this editor.
14260      * @param {Number} width The new width
14261      * @param {Number} height The new height
14262      */
14263     setSize : function(w, h){
14264         this.field.setSize(w, h);
14265         if(this.el){
14266             this.el.sync();
14267         }
14268     },
14269
14270     /**
14271      * Realigns the editor to the bound field based on the current alignment config value.
14272      */
14273     realign : function(){
14274         this.el.alignTo(this.boundEl, this.alignment);
14275     },
14276
14277     /**
14278      * Ends the editing process, persists the changed value to the underlying field, and hides the editor.
14279      * @param {Boolean} remainVisible Override the default behavior and keep the editor visible after edit (defaults to false)
14280      */
14281     completeEdit : function(remainVisible){
14282         if(!this.editing){
14283             return;
14284         }
14285         var v = this.getValue();
14286         if(this.revertInvalid !== false && !this.field.isValid()){
14287             v = this.startValue;
14288             this.cancelEdit(true);
14289         }
14290         if(String(v) === String(this.startValue) && this.ignoreNoChange){
14291             this.editing = false;
14292             this.hide();
14293             return;
14294         }
14295         if(this.fireEvent("beforecomplete", this, v, this.startValue) !== false){
14296             this.editing = false;
14297             if(this.updateEl && this.boundEl){
14298                 this.boundEl.update(v);
14299             }
14300             if(remainVisible !== true){
14301                 this.hide();
14302             }
14303             this.fireEvent("complete", this, v, this.startValue);
14304         }
14305     },
14306
14307     // private
14308     onShow : function(){
14309         this.el.show();
14310         if(this.hideEl !== false){
14311             this.boundEl.hide();
14312         }
14313         this.field.show();
14314         if(Roo.isIE && !this.fixIEFocus){ // IE has problems with focusing the first time
14315             this.fixIEFocus = true;
14316             this.deferredFocus.defer(50, this);
14317         }else{
14318             this.field.focus();
14319         }
14320         this.fireEvent("startedit", this.boundEl, this.startValue);
14321     },
14322
14323     deferredFocus : function(){
14324         if(this.editing){
14325             this.field.focus();
14326         }
14327     },
14328
14329     /**
14330      * Cancels the editing process and hides the editor without persisting any changes.  The field value will be
14331      * reverted to the original starting value.
14332      * @param {Boolean} remainVisible Override the default behavior and keep the editor visible after
14333      * cancel (defaults to false)
14334      */
14335     cancelEdit : function(remainVisible){
14336         if(this.editing){
14337             this.setValue(this.startValue);
14338             if(remainVisible !== true){
14339                 this.hide();
14340             }
14341         }
14342     },
14343
14344     // private
14345     onBlur : function(){
14346         if(this.allowBlur !== true && this.editing){
14347             this.completeEdit();
14348         }
14349     },
14350
14351     // private
14352     onHide : function(){
14353         if(this.editing){
14354             this.completeEdit();
14355             return;
14356         }
14357         this.field.blur();
14358         if(this.field.collapse){
14359             this.field.collapse();
14360         }
14361         this.el.hide();
14362         if(this.hideEl !== false){
14363             this.boundEl.show();
14364         }
14365         if(Roo.QuickTips){
14366             Roo.QuickTips.enable();
14367         }
14368     },
14369
14370     /**
14371      * Sets the data value of the editor
14372      * @param {Mixed} value Any valid value supported by the underlying field
14373      */
14374     setValue : function(v){
14375         this.field.setValue(v);
14376     },
14377
14378     /**
14379      * Gets the data value of the editor
14380      * @return {Mixed} The data value
14381      */
14382     getValue : function(){
14383         return this.field.getValue();
14384     }
14385 });/*
14386  * Based on:
14387  * Ext JS Library 1.1.1
14388  * Copyright(c) 2006-2007, Ext JS, LLC.
14389  *
14390  * Originally Released Under LGPL - original licence link has changed is not relivant.
14391  *
14392  * Fork - LGPL
14393  * <script type="text/javascript">
14394  */
14395  
14396 /**
14397  * @class Roo.BasicDialog
14398  * @extends Roo.util.Observable
14399  * Lightweight Dialog Class.  The code below shows the creation of a typical dialog using existing HTML markup:
14400  * <pre><code>
14401 var dlg = new Roo.BasicDialog("my-dlg", {
14402     height: 200,
14403     width: 300,
14404     minHeight: 100,
14405     minWidth: 150,
14406     modal: true,
14407     proxyDrag: true,
14408     shadow: true
14409 });
14410 dlg.addKeyListener(27, dlg.hide, dlg); // ESC can also close the dialog
14411 dlg.addButton('OK', dlg.hide, dlg);    // Could call a save function instead of hiding
14412 dlg.addButton('Cancel', dlg.hide, dlg);
14413 dlg.show();
14414 </code></pre>
14415   <b>A Dialog should always be a direct child of the body element.</b>
14416  * @cfg {Boolean/DomHelper} autoCreate True to auto create from scratch, or using a DomHelper Object (defaults to false)
14417  * @cfg {String} title Default text to display in the title bar (defaults to null)
14418  * @cfg {Number} width Width of the dialog in pixels (can also be set via CSS).  Determined by browser if unspecified.
14419  * @cfg {Number} height Height of the dialog in pixels (can also be set via CSS).  Determined by browser if unspecified.
14420  * @cfg {Number} x The default left page coordinate of the dialog (defaults to center screen)
14421  * @cfg {Number} y The default top page coordinate of the dialog (defaults to center screen)
14422  * @cfg {String/Element} animateTarget Id or element from which the dialog should animate while opening
14423  * (defaults to null with no animation)
14424  * @cfg {Boolean} resizable False to disable manual dialog resizing (defaults to true)
14425  * @cfg {String} resizeHandles Which resize handles to display - see the {@link Roo.Resizable} handles config
14426  * property for valid values (defaults to 'all')
14427  * @cfg {Number} minHeight The minimum allowable height for a resizable dialog (defaults to 80)
14428  * @cfg {Number} minWidth The minimum allowable width for a resizable dialog (defaults to 200)
14429  * @cfg {Boolean} modal True to show the dialog modally, preventing user interaction with the rest of the page (defaults to false)
14430  * @cfg {Boolean} autoScroll True to allow the dialog body contents to overflow and display scrollbars (defaults to false)
14431  * @cfg {Boolean} closable False to remove the built-in top-right corner close button (defaults to true)
14432  * @cfg {Boolean} collapsible False to remove the built-in top-right corner collapse button (defaults to true)
14433  * @cfg {Boolean} constraintoviewport True to keep the dialog constrained within the visible viewport boundaries (defaults to true)
14434  * @cfg {Boolean} syncHeightBeforeShow True to cause the dimensions to be recalculated before the dialog is shown (defaults to false)
14435  * @cfg {Boolean} draggable False to disable dragging of the dialog within the viewport (defaults to true)
14436  * @cfg {Boolean} autoTabs If true, all elements with class 'x-dlg-tab' will get automatically converted to tabs (defaults to false)
14437  * @cfg {String} tabTag The tag name of tab elements, used when autoTabs = true (defaults to 'div')
14438  * @cfg {Boolean} proxyDrag True to drag a lightweight proxy element rather than the dialog itself, used when
14439  * draggable = true (defaults to false)
14440  * @cfg {Boolean} fixedcenter True to ensure that anytime the dialog is shown or resized it gets centered (defaults to false)
14441  * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
14442  * shadow (defaults to false)
14443  * @cfg {Number} shadowOffset The number of pixels to offset the shadow if displayed (defaults to 5)
14444  * @cfg {String} buttonAlign Valid values are "left," "center" and "right" (defaults to "right")
14445  * @cfg {Number} minButtonWidth Minimum width of all dialog buttons (defaults to 75)
14446  * @cfg {Array} buttons Array of buttons
14447  * @cfg {Boolean} shim True to create an iframe shim that prevents selects from showing through (defaults to false)
14448  * @constructor
14449  * Create a new BasicDialog.
14450  * @param {String/HTMLElement/Roo.Element} el The container element or DOM node, or its id
14451  * @param {Object} config Configuration options
14452  */
14453 Roo.BasicDialog = function(el, config){
14454     this.el = Roo.get(el);
14455     var dh = Roo.DomHelper;
14456     if(!this.el && config && config.autoCreate){
14457         if(typeof config.autoCreate == "object"){
14458             if(!config.autoCreate.id){
14459                 config.autoCreate.id = el;
14460             }
14461             this.el = dh.append(document.body,
14462                         config.autoCreate, true);
14463         }else{
14464             this.el = dh.append(document.body,
14465                         {tag: "div", id: el, style:'visibility:hidden;'}, true);
14466         }
14467     }
14468     el = this.el;
14469     el.setDisplayed(true);
14470     el.hide = this.hideAction;
14471     this.id = el.id;
14472     el.addClass("x-dlg");
14473
14474     Roo.apply(this, config);
14475
14476     this.proxy = el.createProxy("x-dlg-proxy");
14477     this.proxy.hide = this.hideAction;
14478     this.proxy.setOpacity(.5);
14479     this.proxy.hide();
14480
14481     if(config.width){
14482         el.setWidth(config.width);
14483     }
14484     if(config.height){
14485         el.setHeight(config.height);
14486     }
14487     this.size = el.getSize();
14488     if(typeof config.x != "undefined" && typeof config.y != "undefined"){
14489         this.xy = [config.x,config.y];
14490     }else{
14491         this.xy = el.getCenterXY(true);
14492     }
14493     /** The header element @type Roo.Element */
14494     this.header = el.child("> .x-dlg-hd");
14495     /** The body element @type Roo.Element */
14496     this.body = el.child("> .x-dlg-bd");
14497     /** The footer element @type Roo.Element */
14498     this.footer = el.child("> .x-dlg-ft");
14499
14500     if(!this.header){
14501         this.header = el.createChild({tag: "div", cls:"x-dlg-hd", html: "&#160;"}, this.body ? this.body.dom : null);
14502     }
14503     if(!this.body){
14504         this.body = el.createChild({tag: "div", cls:"x-dlg-bd"});
14505     }
14506
14507     this.header.unselectable();
14508     if(this.title){
14509         this.header.update(this.title);
14510     }
14511     // this element allows the dialog to be focused for keyboard event
14512     this.focusEl = el.createChild({tag: "a", href:"#", cls:"x-dlg-focus", tabIndex:"-1"});
14513     this.focusEl.swallowEvent("click", true);
14514
14515     this.header.wrap({cls:"x-dlg-hd-right"}).wrap({cls:"x-dlg-hd-left"}, true);
14516
14517     // wrap the body and footer for special rendering
14518     this.bwrap = this.body.wrap({tag: "div", cls:"x-dlg-dlg-body"});
14519     if(this.footer){
14520         this.bwrap.dom.appendChild(this.footer.dom);
14521     }
14522
14523     this.bg = this.el.createChild({
14524         tag: "div", cls:"x-dlg-bg",
14525         html: '<div class="x-dlg-bg-left"><div class="x-dlg-bg-right"><div class="x-dlg-bg-center">&#160;</div></div></div>'
14526     });
14527     this.centerBg = this.bg.child("div.x-dlg-bg-center");
14528
14529
14530     if(this.autoScroll !== false && !this.autoTabs){
14531         this.body.setStyle("overflow", "auto");
14532     }
14533
14534     this.toolbox = this.el.createChild({cls: "x-dlg-toolbox"});
14535
14536     if(this.closable !== false){
14537         this.el.addClass("x-dlg-closable");
14538         this.close = this.toolbox.createChild({cls:"x-dlg-close"});
14539         this.close.on("click", this.closeClick, this);
14540         this.close.addClassOnOver("x-dlg-close-over");
14541     }
14542     if(this.collapsible !== false){
14543         this.collapseBtn = this.toolbox.createChild({cls:"x-dlg-collapse"});
14544         this.collapseBtn.on("click", this.collapseClick, this);
14545         this.collapseBtn.addClassOnOver("x-dlg-collapse-over");
14546         this.header.on("dblclick", this.collapseClick, this);
14547     }
14548     if(this.resizable !== false){
14549         this.el.addClass("x-dlg-resizable");
14550         this.resizer = new Roo.Resizable(el, {
14551             minWidth: this.minWidth || 80,
14552             minHeight:this.minHeight || 80,
14553             handles: this.resizeHandles || "all",
14554             pinned: true
14555         });
14556         this.resizer.on("beforeresize", this.beforeResize, this);
14557         this.resizer.on("resize", this.onResize, this);
14558     }
14559     if(this.draggable !== false){
14560         el.addClass("x-dlg-draggable");
14561         if (!this.proxyDrag) {
14562             var dd = new Roo.dd.DD(el.dom.id, "WindowDrag");
14563         }
14564         else {
14565             var dd = new Roo.dd.DDProxy(el.dom.id, "WindowDrag", {dragElId: this.proxy.id});
14566         }
14567         dd.setHandleElId(this.header.id);
14568         dd.endDrag = this.endMove.createDelegate(this);
14569         dd.startDrag = this.startMove.createDelegate(this);
14570         dd.onDrag = this.onDrag.createDelegate(this);
14571         dd.scroll = false;
14572         this.dd = dd;
14573     }
14574     if(this.modal){
14575         this.mask = dh.append(document.body, {tag: "div", cls:"x-dlg-mask"}, true);
14576         this.mask.enableDisplayMode("block");
14577         this.mask.hide();
14578         this.el.addClass("x-dlg-modal");
14579     }
14580     if(this.shadow){
14581         this.shadow = new Roo.Shadow({
14582             mode : typeof this.shadow == "string" ? this.shadow : "sides",
14583             offset : this.shadowOffset
14584         });
14585     }else{
14586         this.shadowOffset = 0;
14587     }
14588     if(Roo.useShims && this.shim !== false){
14589         this.shim = this.el.createShim();
14590         this.shim.hide = this.hideAction;
14591         this.shim.hide();
14592     }else{
14593         this.shim = false;
14594     }
14595     if(this.autoTabs){
14596         this.initTabs();
14597     }
14598     if (this.buttons) { 
14599         var bts= this.buttons;
14600         this.buttons = [];
14601         Roo.each(bts, function(b) {
14602             this.addButton(b);
14603         }, this);
14604     }
14605     
14606     
14607     this.addEvents({
14608         /**
14609          * @event keydown
14610          * Fires when a key is pressed
14611          * @param {Roo.BasicDialog} this
14612          * @param {Roo.EventObject} e
14613          */
14614         "keydown" : true,
14615         /**
14616          * @event move
14617          * Fires when this dialog is moved by the user.
14618          * @param {Roo.BasicDialog} this
14619          * @param {Number} x The new page X
14620          * @param {Number} y The new page Y
14621          */
14622         "move" : true,
14623         /**
14624          * @event resize
14625          * Fires when this dialog is resized by the user.
14626          * @param {Roo.BasicDialog} this
14627          * @param {Number} width The new width
14628          * @param {Number} height The new height
14629          */
14630         "resize" : true,
14631         /**
14632          * @event beforehide
14633          * Fires before this dialog is hidden.
14634          * @param {Roo.BasicDialog} this
14635          */
14636         "beforehide" : true,
14637         /**
14638          * @event hide
14639          * Fires when this dialog is hidden.
14640          * @param {Roo.BasicDialog} this
14641          */
14642         "hide" : true,
14643         /**
14644          * @event beforeshow
14645          * Fires before this dialog is shown.
14646          * @param {Roo.BasicDialog} this
14647          */
14648         "beforeshow" : true,
14649         /**
14650          * @event show
14651          * Fires when this dialog is shown.
14652          * @param {Roo.BasicDialog} this
14653          */
14654         "show" : true
14655     });
14656     el.on("keydown", this.onKeyDown, this);
14657     el.on("mousedown", this.toFront, this);
14658     Roo.EventManager.onWindowResize(this.adjustViewport, this, true);
14659     this.el.hide();
14660     Roo.DialogManager.register(this);
14661     Roo.BasicDialog.superclass.constructor.call(this);
14662 };
14663
14664 Roo.extend(Roo.BasicDialog, Roo.util.Observable, {
14665     shadowOffset: Roo.isIE ? 6 : 5,
14666     minHeight: 80,
14667     minWidth: 200,
14668     minButtonWidth: 75,
14669     defaultButton: null,
14670     buttonAlign: "right",
14671     tabTag: 'div',
14672     firstShow: true,
14673
14674     /**
14675      * Sets the dialog title text
14676      * @param {String} text The title text to display
14677      * @return {Roo.BasicDialog} this
14678      */
14679     setTitle : function(text){
14680         this.header.update(text);
14681         return this;
14682     },
14683
14684     // private
14685     closeClick : function(){
14686         this.hide();
14687     },
14688
14689     // private
14690     collapseClick : function(){
14691         this[this.collapsed ? "expand" : "collapse"]();
14692     },
14693
14694     /**
14695      * Collapses the dialog to its minimized state (only the title bar is visible).
14696      * Equivalent to the user clicking the collapse dialog button.
14697      */
14698     collapse : function(){
14699         if(!this.collapsed){
14700             this.collapsed = true;
14701             this.el.addClass("x-dlg-collapsed");
14702             this.restoreHeight = this.el.getHeight();
14703             this.resizeTo(this.el.getWidth(), this.header.getHeight());
14704         }
14705     },
14706
14707     /**
14708      * Expands a collapsed dialog back to its normal state.  Equivalent to the user
14709      * clicking the expand dialog button.
14710      */
14711     expand : function(){
14712         if(this.collapsed){
14713             this.collapsed = false;
14714             this.el.removeClass("x-dlg-collapsed");
14715             this.resizeTo(this.el.getWidth(), this.restoreHeight);
14716         }
14717     },
14718
14719     /**
14720      * Reinitializes the tabs component, clearing out old tabs and finding new ones.
14721      * @return {Roo.TabPanel} The tabs component
14722      */
14723     initTabs : function(){
14724         var tabs = this.getTabs();
14725         while(tabs.getTab(0)){
14726             tabs.removeTab(0);
14727         }
14728         this.el.select(this.tabTag+'.x-dlg-tab').each(function(el){
14729             var dom = el.dom;
14730             tabs.addTab(Roo.id(dom), dom.title);
14731             dom.title = "";
14732         });
14733         tabs.activate(0);
14734         return tabs;
14735     },
14736
14737     // private
14738     beforeResize : function(){
14739         this.resizer.minHeight = Math.max(this.minHeight, this.getHeaderFooterHeight(true)+40);
14740     },
14741
14742     // private
14743     onResize : function(){
14744         this.refreshSize();
14745         this.syncBodyHeight();
14746         this.adjustAssets();
14747         this.focus();
14748         this.fireEvent("resize", this, this.size.width, this.size.height);
14749     },
14750
14751     // private
14752     onKeyDown : function(e){
14753         if(this.isVisible()){
14754             this.fireEvent("keydown", this, e);
14755         }
14756     },
14757
14758     /**
14759      * Resizes the dialog.
14760      * @param {Number} width
14761      * @param {Number} height
14762      * @return {Roo.BasicDialog} this
14763      */
14764     resizeTo : function(width, height){
14765         this.el.setSize(width, height);
14766         this.size = {width: width, height: height};
14767         this.syncBodyHeight();
14768         if(this.fixedcenter){
14769             this.center();
14770         }
14771         if(this.isVisible()){
14772             this.constrainXY();
14773             this.adjustAssets();
14774         }
14775         this.fireEvent("resize", this, width, height);
14776         return this;
14777     },
14778
14779
14780     /**
14781      * Resizes the dialog to fit the specified content size.
14782      * @param {Number} width
14783      * @param {Number} height
14784      * @return {Roo.BasicDialog} this
14785      */
14786     setContentSize : function(w, h){
14787         h += this.getHeaderFooterHeight() + this.body.getMargins("tb");
14788         w += this.body.getMargins("lr") + this.bwrap.getMargins("lr") + this.centerBg.getPadding("lr");
14789         //if(!this.el.isBorderBox()){
14790             h +=  this.body.getPadding("tb") + this.bwrap.getBorderWidth("tb") + this.body.getBorderWidth("tb") + this.el.getBorderWidth("tb");
14791             w += this.body.getPadding("lr") + this.bwrap.getBorderWidth("lr") + this.body.getBorderWidth("lr") + this.bwrap.getPadding("lr") + this.el.getBorderWidth("lr");
14792         //}
14793         if(this.tabs){
14794             h += this.tabs.stripWrap.getHeight() + this.tabs.bodyEl.getMargins("tb") + this.tabs.bodyEl.getPadding("tb");
14795             w += this.tabs.bodyEl.getMargins("lr") + this.tabs.bodyEl.getPadding("lr");
14796         }
14797         this.resizeTo(w, h);
14798         return this;
14799     },
14800
14801     /**
14802      * Adds a key listener for when this dialog is displayed.  This allows you to hook in a function that will be
14803      * executed in response to a particular key being pressed while the dialog is active.
14804      * @param {Number/Array/Object} key Either the numeric key code, array of key codes or an object with the following options:
14805      *                                  {key: (number or array), shift: (true/false), ctrl: (true/false), alt: (true/false)}
14806      * @param {Function} fn The function to call
14807      * @param {Object} scope (optional) The scope of the function
14808      * @return {Roo.BasicDialog} this
14809      */
14810     addKeyListener : function(key, fn, scope){
14811         var keyCode, shift, ctrl, alt;
14812         if(typeof key == "object" && !(key instanceof Array)){
14813             keyCode = key["key"];
14814             shift = key["shift"];
14815             ctrl = key["ctrl"];
14816             alt = key["alt"];
14817         }else{
14818             keyCode = key;
14819         }
14820         var handler = function(dlg, e){
14821             if((!shift || e.shiftKey) && (!ctrl || e.ctrlKey) &&  (!alt || e.altKey)){
14822                 var k = e.getKey();
14823                 if(keyCode instanceof Array){
14824                     for(var i = 0, len = keyCode.length; i < len; i++){
14825                         if(keyCode[i] == k){
14826                           fn.call(scope || window, dlg, k, e);
14827                           return;
14828                         }
14829                     }
14830                 }else{
14831                     if(k == keyCode){
14832                         fn.call(scope || window, dlg, k, e);
14833                     }
14834                 }
14835             }
14836         };
14837         this.on("keydown", handler);
14838         return this;
14839     },
14840
14841     /**
14842      * Returns the TabPanel component (creates it if it doesn't exist).
14843      * Note: If you wish to simply check for the existence of tabs without creating them,
14844      * check for a null 'tabs' property.
14845      * @return {Roo.TabPanel} The tabs component
14846      */
14847     getTabs : function(){
14848         if(!this.tabs){
14849             this.el.addClass("x-dlg-auto-tabs");
14850             this.body.addClass(this.tabPosition == "bottom" ? "x-tabs-bottom" : "x-tabs-top");
14851             this.tabs = new Roo.TabPanel(this.body.dom, this.tabPosition == "bottom");
14852         }
14853         return this.tabs;
14854     },
14855
14856     /**
14857      * Adds a button to the footer section of the dialog.
14858      * @param {String/Object} config A string becomes the button text, an object can either be a Button config
14859      * object or a valid Roo.DomHelper element config
14860      * @param {Function} handler The function called when the button is clicked
14861      * @param {Object} scope (optional) The scope of the handler function (accepts position as a property)
14862      * @return {Roo.Button} The new button
14863      */
14864     addButton : function(config, handler, scope){
14865         var dh = Roo.DomHelper;
14866         if(!this.footer){
14867             this.footer = dh.append(this.bwrap, {tag: "div", cls:"x-dlg-ft"}, true);
14868         }
14869         if(!this.btnContainer){
14870             var tb = this.footer.createChild({
14871
14872                 cls:"x-dlg-btns x-dlg-btns-"+this.buttonAlign,
14873                 html:'<table cellspacing="0"><tbody><tr></tr></tbody></table><div class="x-clear"></div>'
14874             }, null, true);
14875             this.btnContainer = tb.firstChild.firstChild.firstChild;
14876         }
14877         var bconfig = {
14878             handler: handler,
14879             scope: scope,
14880             minWidth: this.minButtonWidth,
14881             hideParent:true
14882         };
14883         if(typeof config == "string"){
14884             bconfig.text = config;
14885         }else{
14886             if(config.tag){
14887                 bconfig.dhconfig = config;
14888             }else{
14889                 Roo.apply(bconfig, config);
14890             }
14891         }
14892         var fc = false;
14893         if ((typeof(bconfig.position) != 'undefined') && bconfig.position < this.btnContainer.childNodes.length-1) {
14894             bconfig.position = Math.max(0, bconfig.position);
14895             fc = this.btnContainer.childNodes[bconfig.position];
14896         }
14897          
14898         var btn = new Roo.Button(
14899             fc ? 
14900                 this.btnContainer.insertBefore(document.createElement("td"),fc)
14901                 : this.btnContainer.appendChild(document.createElement("td")),
14902             //Roo.get(this.btnContainer).createChild( { tag: 'td'},  fc ),
14903             bconfig
14904         );
14905         this.syncBodyHeight();
14906         if(!this.buttons){
14907             /**
14908              * Array of all the buttons that have been added to this dialog via addButton
14909              * @type Array
14910              */
14911             this.buttons = [];
14912         }
14913         this.buttons.push(btn);
14914         return btn;
14915     },
14916
14917     /**
14918      * Sets the default button to be focused when the dialog is displayed.
14919      * @param {Roo.BasicDialog.Button} btn The button object returned by {@link #addButton}
14920      * @return {Roo.BasicDialog} this
14921      */
14922     setDefaultButton : function(btn){
14923         this.defaultButton = btn;
14924         return this;
14925     },
14926
14927     // private
14928     getHeaderFooterHeight : function(safe){
14929         var height = 0;
14930         if(this.header){
14931            height += this.header.getHeight();
14932         }
14933         if(this.footer){
14934            var fm = this.footer.getMargins();
14935             height += (this.footer.getHeight()+fm.top+fm.bottom);
14936         }
14937         height += this.bwrap.getPadding("tb")+this.bwrap.getBorderWidth("tb");
14938         height += this.centerBg.getPadding("tb");
14939         return height;
14940     },
14941
14942     // private
14943     syncBodyHeight : function()
14944     {
14945         var bd = this.body, // the text
14946             cb = this.centerBg, // wrapper around bottom.. but does not seem to be used..
14947             bw = this.bwrap;
14948         var height = this.size.height - this.getHeaderFooterHeight(false);
14949         bd.setHeight(height-bd.getMargins("tb"));
14950         var hh = this.header.getHeight();
14951         var h = this.size.height-hh;
14952         cb.setHeight(h);
14953         
14954         bw.setLeftTop(cb.getPadding("l"), hh+cb.getPadding("t"));
14955         bw.setHeight(h-cb.getPadding("tb"));
14956         
14957         bw.setWidth(this.el.getWidth(true)-cb.getPadding("lr"));
14958         bd.setWidth(bw.getWidth(true));
14959         if(this.tabs){
14960             this.tabs.syncHeight();
14961             if(Roo.isIE){
14962                 this.tabs.el.repaint();
14963             }
14964         }
14965     },
14966
14967     /**
14968      * Restores the previous state of the dialog if Roo.state is configured.
14969      * @return {Roo.BasicDialog} this
14970      */
14971     restoreState : function(){
14972         var box = Roo.state.Manager.get(this.stateId || (this.el.id + "-state"));
14973         if(box && box.width){
14974             this.xy = [box.x, box.y];
14975             this.resizeTo(box.width, box.height);
14976         }
14977         return this;
14978     },
14979
14980     // private
14981     beforeShow : function(){
14982         this.expand();
14983         if(this.fixedcenter){
14984             this.xy = this.el.getCenterXY(true);
14985         }
14986         if(this.modal){
14987             Roo.get(document.body).addClass("x-body-masked");
14988             this.mask.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
14989             this.mask.show();
14990         }
14991         this.constrainXY();
14992     },
14993
14994     // private
14995     animShow : function(){
14996         var b = Roo.get(this.animateTarget).getBox();
14997         this.proxy.setSize(b.width, b.height);
14998         this.proxy.setLocation(b.x, b.y);
14999         this.proxy.show();
15000         this.proxy.setBounds(this.xy[0], this.xy[1], this.size.width, this.size.height,
15001                     true, .35, this.showEl.createDelegate(this));
15002     },
15003
15004     /**
15005      * Shows the dialog.
15006      * @param {String/HTMLElement/Roo.Element} animateTarget (optional) Reset the animation target
15007      * @return {Roo.BasicDialog} this
15008      */
15009     show : function(animateTarget){
15010         if (this.fireEvent("beforeshow", this) === false){
15011             return;
15012         }
15013         if(this.syncHeightBeforeShow){
15014             this.syncBodyHeight();
15015         }else if(this.firstShow){
15016             this.firstShow = false;
15017             this.syncBodyHeight(); // sync the height on the first show instead of in the constructor
15018         }
15019         this.animateTarget = animateTarget || this.animateTarget;
15020         if(!this.el.isVisible()){
15021             this.beforeShow();
15022             if(this.animateTarget && Roo.get(this.animateTarget)){
15023                 this.animShow();
15024             }else{
15025                 this.showEl();
15026             }
15027         }
15028         return this;
15029     },
15030
15031     // private
15032     showEl : function(){
15033         this.proxy.hide();
15034         this.el.setXY(this.xy);
15035         this.el.show();
15036         this.adjustAssets(true);
15037         this.toFront();
15038         this.focus();
15039         // IE peekaboo bug - fix found by Dave Fenwick
15040         if(Roo.isIE){
15041             this.el.repaint();
15042         }
15043         this.fireEvent("show", this);
15044     },
15045
15046     /**
15047      * Focuses the dialog.  If a defaultButton is set, it will receive focus, otherwise the
15048      * dialog itself will receive focus.
15049      */
15050     focus : function(){
15051         if(this.defaultButton){
15052             this.defaultButton.focus();
15053         }else{
15054             this.focusEl.focus();
15055         }
15056     },
15057
15058     // private
15059     constrainXY : function(){
15060         if(this.constraintoviewport !== false){
15061             if(!this.viewSize){
15062                 if(this.container){
15063                     var s = this.container.getSize();
15064                     this.viewSize = [s.width, s.height];
15065                 }else{
15066                     this.viewSize = [Roo.lib.Dom.getViewWidth(),Roo.lib.Dom.getViewHeight()];
15067                 }
15068             }
15069             var s = Roo.get(this.container||document).getScroll();
15070
15071             var x = this.xy[0], y = this.xy[1];
15072             var w = this.size.width, h = this.size.height;
15073             var vw = this.viewSize[0], vh = this.viewSize[1];
15074             // only move it if it needs it
15075             var moved = false;
15076             // first validate right/bottom
15077             if(x + w > vw+s.left){
15078                 x = vw - w;
15079                 moved = true;
15080             }
15081             if(y + h > vh+s.top){
15082                 y = vh - h;
15083                 moved = true;
15084             }
15085             // then make sure top/left isn't negative
15086             if(x < s.left){
15087                 x = s.left;
15088                 moved = true;
15089             }
15090             if(y < s.top){
15091                 y = s.top;
15092                 moved = true;
15093             }
15094             if(moved){
15095                 // cache xy
15096                 this.xy = [x, y];
15097                 if(this.isVisible()){
15098                     this.el.setLocation(x, y);
15099                     this.adjustAssets();
15100                 }
15101             }
15102         }
15103     },
15104
15105     // private
15106     onDrag : function(){
15107         if(!this.proxyDrag){
15108             this.xy = this.el.getXY();
15109             this.adjustAssets();
15110         }
15111     },
15112
15113     // private
15114     adjustAssets : function(doShow){
15115         var x = this.xy[0], y = this.xy[1];
15116         var w = this.size.width, h = this.size.height;
15117         if(doShow === true){
15118             if(this.shadow){
15119                 this.shadow.show(this.el);
15120             }
15121             if(this.shim){
15122                 this.shim.show();
15123             }
15124         }
15125         if(this.shadow && this.shadow.isVisible()){
15126             this.shadow.show(this.el);
15127         }
15128         if(this.shim && this.shim.isVisible()){
15129             this.shim.setBounds(x, y, w, h);
15130         }
15131     },
15132
15133     // private
15134     adjustViewport : function(w, h){
15135         if(!w || !h){
15136             w = Roo.lib.Dom.getViewWidth();
15137             h = Roo.lib.Dom.getViewHeight();
15138         }
15139         // cache the size
15140         this.viewSize = [w, h];
15141         if(this.modal && this.mask.isVisible()){
15142             this.mask.setSize(w, h); // first make sure the mask isn't causing overflow
15143             this.mask.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
15144         }
15145         if(this.isVisible()){
15146             this.constrainXY();
15147         }
15148     },
15149
15150     /**
15151      * Destroys this dialog and all its supporting elements (including any tabs, shim,
15152      * shadow, proxy, mask, etc.)  Also removes all event listeners.
15153      * @param {Boolean} removeEl (optional) true to remove the element from the DOM
15154      */
15155     destroy : function(removeEl){
15156         if(this.isVisible()){
15157             this.animateTarget = null;
15158             this.hide();
15159         }
15160         Roo.EventManager.removeResizeListener(this.adjustViewport, this);
15161         if(this.tabs){
15162             this.tabs.destroy(removeEl);
15163         }
15164         Roo.destroy(
15165              this.shim,
15166              this.proxy,
15167              this.resizer,
15168              this.close,
15169              this.mask
15170         );
15171         if(this.dd){
15172             this.dd.unreg();
15173         }
15174         if(this.buttons){
15175            for(var i = 0, len = this.buttons.length; i < len; i++){
15176                this.buttons[i].destroy();
15177            }
15178         }
15179         this.el.removeAllListeners();
15180         if(removeEl === true){
15181             this.el.update("");
15182             this.el.remove();
15183         }
15184         Roo.DialogManager.unregister(this);
15185     },
15186
15187     // private
15188     startMove : function(){
15189         if(this.proxyDrag){
15190             this.proxy.show();
15191         }
15192         if(this.constraintoviewport !== false){
15193             this.dd.constrainTo(document.body, {right: this.shadowOffset, bottom: this.shadowOffset});
15194         }
15195     },
15196
15197     // private
15198     endMove : function(){
15199         if(!this.proxyDrag){
15200             Roo.dd.DD.prototype.endDrag.apply(this.dd, arguments);
15201         }else{
15202             Roo.dd.DDProxy.prototype.endDrag.apply(this.dd, arguments);
15203             this.proxy.hide();
15204         }
15205         this.refreshSize();
15206         this.adjustAssets();
15207         this.focus();
15208         this.fireEvent("move", this, this.xy[0], this.xy[1]);
15209     },
15210
15211     /**
15212      * Brings this dialog to the front of any other visible dialogs
15213      * @return {Roo.BasicDialog} this
15214      */
15215     toFront : function(){
15216         Roo.DialogManager.bringToFront(this);
15217         return this;
15218     },
15219
15220     /**
15221      * Sends this dialog to the back (under) of any other visible dialogs
15222      * @return {Roo.BasicDialog} this
15223      */
15224     toBack : function(){
15225         Roo.DialogManager.sendToBack(this);
15226         return this;
15227     },
15228
15229     /**
15230      * Centers this dialog in the viewport
15231      * @return {Roo.BasicDialog} this
15232      */
15233     center : function(){
15234         var xy = this.el.getCenterXY(true);
15235         this.moveTo(xy[0], xy[1]);
15236         return this;
15237     },
15238
15239     /**
15240      * Moves the dialog's top-left corner to the specified point
15241      * @param {Number} x
15242      * @param {Number} y
15243      * @return {Roo.BasicDialog} this
15244      */
15245     moveTo : function(x, y){
15246         this.xy = [x,y];
15247         if(this.isVisible()){
15248             this.el.setXY(this.xy);
15249             this.adjustAssets();
15250         }
15251         return this;
15252     },
15253
15254     /**
15255      * Aligns the dialog to the specified element
15256      * @param {String/HTMLElement/Roo.Element} element The element to align to.
15257      * @param {String} position The position to align to (see {@link Roo.Element#alignTo} for more details).
15258      * @param {Array} offsets (optional) Offset the positioning by [x, y]
15259      * @return {Roo.BasicDialog} this
15260      */
15261     alignTo : function(element, position, offsets){
15262         this.xy = this.el.getAlignToXY(element, position, offsets);
15263         if(this.isVisible()){
15264             this.el.setXY(this.xy);
15265             this.adjustAssets();
15266         }
15267         return this;
15268     },
15269
15270     /**
15271      * Anchors an element to another element and realigns it when the window is resized.
15272      * @param {String/HTMLElement/Roo.Element} element The element to align to.
15273      * @param {String} position The position to align to (see {@link Roo.Element#alignTo} for more details)
15274      * @param {Array} offsets (optional) Offset the positioning by [x, y]
15275      * @param {Boolean/Number} monitorScroll (optional) true to monitor body scroll and reposition. If this parameter
15276      * is a number, it is used as the buffer delay (defaults to 50ms).
15277      * @return {Roo.BasicDialog} this
15278      */
15279     anchorTo : function(el, alignment, offsets, monitorScroll){
15280         var action = function(){
15281             this.alignTo(el, alignment, offsets);
15282         };
15283         Roo.EventManager.onWindowResize(action, this);
15284         var tm = typeof monitorScroll;
15285         if(tm != 'undefined'){
15286             Roo.EventManager.on(window, 'scroll', action, this,
15287                 {buffer: tm == 'number' ? monitorScroll : 50});
15288         }
15289         action.call(this);
15290         return this;
15291     },
15292
15293     /**
15294      * Returns true if the dialog is visible
15295      * @return {Boolean}
15296      */
15297     isVisible : function(){
15298         return this.el.isVisible();
15299     },
15300
15301     // private
15302     animHide : function(callback){
15303         var b = Roo.get(this.animateTarget).getBox();
15304         this.proxy.show();
15305         this.proxy.setBounds(this.xy[0], this.xy[1], this.size.width, this.size.height);
15306         this.el.hide();
15307         this.proxy.setBounds(b.x, b.y, b.width, b.height, true, .35,
15308                     this.hideEl.createDelegate(this, [callback]));
15309     },
15310
15311     /**
15312      * Hides the dialog.
15313      * @param {Function} callback (optional) Function to call when the dialog is hidden
15314      * @return {Roo.BasicDialog} this
15315      */
15316     hide : function(callback){
15317         if (this.fireEvent("beforehide", this) === false){
15318             return;
15319         }
15320         if(this.shadow){
15321             this.shadow.hide();
15322         }
15323         if(this.shim) {
15324           this.shim.hide();
15325         }
15326         // sometimes animateTarget seems to get set.. causing problems...
15327         // this just double checks..
15328         if(this.animateTarget && Roo.get(this.animateTarget)) {
15329            this.animHide(callback);
15330         }else{
15331             this.el.hide();
15332             this.hideEl(callback);
15333         }
15334         return this;
15335     },
15336
15337     // private
15338     hideEl : function(callback){
15339         this.proxy.hide();
15340         if(this.modal){
15341             this.mask.hide();
15342             Roo.get(document.body).removeClass("x-body-masked");
15343         }
15344         this.fireEvent("hide", this);
15345         if(typeof callback == "function"){
15346             callback();
15347         }
15348     },
15349
15350     // private
15351     hideAction : function(){
15352         this.setLeft("-10000px");
15353         this.setTop("-10000px");
15354         this.setStyle("visibility", "hidden");
15355     },
15356
15357     // private
15358     refreshSize : function(){
15359         this.size = this.el.getSize();
15360         this.xy = this.el.getXY();
15361         Roo.state.Manager.set(this.stateId || this.el.id + "-state", this.el.getBox());
15362     },
15363
15364     // private
15365     // z-index is managed by the DialogManager and may be overwritten at any time
15366     setZIndex : function(index){
15367         if(this.modal){
15368             this.mask.setStyle("z-index", index);
15369         }
15370         if(this.shim){
15371             this.shim.setStyle("z-index", ++index);
15372         }
15373         if(this.shadow){
15374             this.shadow.setZIndex(++index);
15375         }
15376         this.el.setStyle("z-index", ++index);
15377         if(this.proxy){
15378             this.proxy.setStyle("z-index", ++index);
15379         }
15380         if(this.resizer){
15381             this.resizer.proxy.setStyle("z-index", ++index);
15382         }
15383
15384         this.lastZIndex = index;
15385     },
15386
15387     /**
15388      * Returns the element for this dialog
15389      * @return {Roo.Element} The underlying dialog Element
15390      */
15391     getEl : function(){
15392         return this.el;
15393     }
15394 });
15395
15396 /**
15397  * @class Roo.DialogManager
15398  * Provides global access to BasicDialogs that have been created and
15399  * support for z-indexing (layering) multiple open dialogs.
15400  */
15401 Roo.DialogManager = function(){
15402     var list = {};
15403     var accessList = [];
15404     var front = null;
15405
15406     // private
15407     var sortDialogs = function(d1, d2){
15408         return (!d1._lastAccess || d1._lastAccess < d2._lastAccess) ? -1 : 1;
15409     };
15410
15411     // private
15412     var orderDialogs = function(){
15413         accessList.sort(sortDialogs);
15414         var seed = Roo.DialogManager.zseed;
15415         for(var i = 0, len = accessList.length; i < len; i++){
15416             var dlg = accessList[i];
15417             if(dlg){
15418                 dlg.setZIndex(seed + (i*10));
15419             }
15420         }
15421     };
15422
15423     return {
15424         /**
15425          * The starting z-index for BasicDialogs (defaults to 9000)
15426          * @type Number The z-index value
15427          */
15428         zseed : 9000,
15429
15430         // private
15431         register : function(dlg){
15432             list[dlg.id] = dlg;
15433             accessList.push(dlg);
15434         },
15435
15436         // private
15437         unregister : function(dlg){
15438             delete list[dlg.id];
15439             var i=0;
15440             var len=0;
15441             if(!accessList.indexOf){
15442                 for(  i = 0, len = accessList.length; i < len; i++){
15443                     if(accessList[i] == dlg){
15444                         accessList.splice(i, 1);
15445                         return;
15446                     }
15447                 }
15448             }else{
15449                  i = accessList.indexOf(dlg);
15450                 if(i != -1){
15451                     accessList.splice(i, 1);
15452                 }
15453             }
15454         },
15455
15456         /**
15457          * Gets a registered dialog by id
15458          * @param {String/Object} id The id of the dialog or a dialog
15459          * @return {Roo.BasicDialog} this
15460          */
15461         get : function(id){
15462             return typeof id == "object" ? id : list[id];
15463         },
15464
15465         /**
15466          * Brings the specified dialog to the front
15467          * @param {String/Object} dlg The id of the dialog or a dialog
15468          * @return {Roo.BasicDialog} this
15469          */
15470         bringToFront : function(dlg){
15471             dlg = this.get(dlg);
15472             if(dlg != front){
15473                 front = dlg;
15474                 dlg._lastAccess = new Date().getTime();
15475                 orderDialogs();
15476             }
15477             return dlg;
15478         },
15479
15480         /**
15481          * Sends the specified dialog to the back
15482          * @param {String/Object} dlg The id of the dialog or a dialog
15483          * @return {Roo.BasicDialog} this
15484          */
15485         sendToBack : function(dlg){
15486             dlg = this.get(dlg);
15487             dlg._lastAccess = -(new Date().getTime());
15488             orderDialogs();
15489             return dlg;
15490         },
15491
15492         /**
15493          * Hides all dialogs
15494          */
15495         hideAll : function(){
15496             for(var id in list){
15497                 if(list[id] && typeof list[id] != "function" && list[id].isVisible()){
15498                     list[id].hide();
15499                 }
15500             }
15501         }
15502     };
15503 }();
15504
15505 /**
15506  * @class Roo.LayoutDialog
15507  * @extends Roo.BasicDialog
15508  * Dialog which provides adjustments for working with a layout in a Dialog.
15509  * Add your necessary layout config options to the dialog's config.<br>
15510  * Example usage (including a nested layout):
15511  * <pre><code>
15512 if(!dialog){
15513     dialog = new Roo.LayoutDialog("download-dlg", {
15514         modal: true,
15515         width:600,
15516         height:450,
15517         shadow:true,
15518         minWidth:500,
15519         minHeight:350,
15520         autoTabs:true,
15521         proxyDrag:true,
15522         // layout config merges with the dialog config
15523         center:{
15524             tabPosition: "top",
15525             alwaysShowTabs: true
15526         }
15527     });
15528     dialog.addKeyListener(27, dialog.hide, dialog);
15529     dialog.setDefaultButton(dialog.addButton("Close", dialog.hide, dialog));
15530     dialog.addButton("Build It!", this.getDownload, this);
15531
15532     // we can even add nested layouts
15533     var innerLayout = new Roo.BorderLayout("dl-inner", {
15534         east: {
15535             initialSize: 200,
15536             autoScroll:true,
15537             split:true
15538         },
15539         center: {
15540             autoScroll:true
15541         }
15542     });
15543     innerLayout.beginUpdate();
15544     innerLayout.add("east", new Roo.ContentPanel("dl-details"));
15545     innerLayout.add("center", new Roo.ContentPanel("selection-panel"));
15546     innerLayout.endUpdate(true);
15547
15548     var layout = dialog.getLayout();
15549     layout.beginUpdate();
15550     layout.add("center", new Roo.ContentPanel("standard-panel",
15551                         {title: "Download the Source", fitToFrame:true}));
15552     layout.add("center", new Roo.NestedLayoutPanel(innerLayout,
15553                {title: "Build your own roo.js"}));
15554     layout.getRegion("center").showPanel(sp);
15555     layout.endUpdate();
15556 }
15557 </code></pre>
15558     * @constructor
15559     * @param {String/HTMLElement/Roo.Element} el The id of or container element, or config
15560     * @param {Object} config configuration options
15561   */
15562 Roo.LayoutDialog = function(el, cfg){
15563     
15564     var config=  cfg;
15565     if (typeof(cfg) == 'undefined') {
15566         config = Roo.apply({}, el);
15567         // not sure why we use documentElement here.. - it should always be body.
15568         // IE7 borks horribly if we use documentElement.
15569         // webkit also does not like documentElement - it creates a body element...
15570         el = Roo.get( document.body || document.documentElement ).createChild();
15571         //config.autoCreate = true;
15572     }
15573     
15574     
15575     config.autoTabs = false;
15576     Roo.LayoutDialog.superclass.constructor.call(this, el, config);
15577     this.body.setStyle({overflow:"hidden", position:"relative"});
15578     this.layout = new Roo.BorderLayout(this.body.dom, config);
15579     this.layout.monitorWindowResize = false;
15580     this.el.addClass("x-dlg-auto-layout");
15581     // fix case when center region overwrites center function
15582     this.center = Roo.BasicDialog.prototype.center;
15583     this.on("show", this.layout.layout, this.layout, true);
15584     if (config.items) {
15585         var xitems = config.items;
15586         delete config.items;
15587         Roo.each(xitems, this.addxtype, this);
15588     }
15589     
15590     
15591 };
15592 Roo.extend(Roo.LayoutDialog, Roo.BasicDialog, {
15593     /**
15594      * Ends update of the layout <strike>and resets display to none</strike>. Use standard beginUpdate/endUpdate on the layout.
15595      * @deprecated
15596      */
15597     endUpdate : function(){
15598         this.layout.endUpdate();
15599     },
15600
15601     /**
15602      * Begins an update of the layout <strike>and sets display to block and visibility to hidden</strike>. Use standard beginUpdate/endUpdate on the layout.
15603      *  @deprecated
15604      */
15605     beginUpdate : function(){
15606         this.layout.beginUpdate();
15607     },
15608
15609     /**
15610      * Get the BorderLayout for this dialog
15611      * @return {Roo.BorderLayout}
15612      */
15613     getLayout : function(){
15614         return this.layout;
15615     },
15616
15617     showEl : function(){
15618         Roo.LayoutDialog.superclass.showEl.apply(this, arguments);
15619         if(Roo.isIE7){
15620             this.layout.layout();
15621         }
15622     },
15623
15624     // private
15625     // Use the syncHeightBeforeShow config option to control this automatically
15626     syncBodyHeight : function(){
15627         Roo.LayoutDialog.superclass.syncBodyHeight.call(this);
15628         if(this.layout){this.layout.layout();}
15629     },
15630     
15631       /**
15632      * Add an xtype element (actually adds to the layout.)
15633      * @return {Object} xdata xtype object data.
15634      */
15635     
15636     addxtype : function(c) {
15637         return this.layout.addxtype(c);
15638     }
15639 });/*
15640  * Based on:
15641  * Ext JS Library 1.1.1
15642  * Copyright(c) 2006-2007, Ext JS, LLC.
15643  *
15644  * Originally Released Under LGPL - original licence link has changed is not relivant.
15645  *
15646  * Fork - LGPL
15647  * <script type="text/javascript">
15648  */
15649  
15650 /**
15651  * @class Roo.MessageBox
15652  * Utility class for generating different styles of message boxes.  The alias Roo.Msg can also be used.
15653  * Example usage:
15654  *<pre><code>
15655 // Basic alert:
15656 Roo.Msg.alert('Status', 'Changes saved successfully.');
15657
15658 // Prompt for user data:
15659 Roo.Msg.prompt('Name', 'Please enter your name:', function(btn, text){
15660     if (btn == 'ok'){
15661         // process text value...
15662     }
15663 });
15664
15665 // Show a dialog using config options:
15666 Roo.Msg.show({
15667    title:'Save Changes?',
15668    msg: 'Your are closing a tab that has unsaved changes. Would you like to save your changes?',
15669    buttons: Roo.Msg.YESNOCANCEL,
15670    fn: processResult,
15671    animEl: 'elId'
15672 });
15673 </code></pre>
15674  * @singleton
15675  */
15676 Roo.MessageBox = function(){
15677     var dlg, opt, mask, waitTimer;
15678     var bodyEl, msgEl, textboxEl, textareaEl, progressEl, pp;
15679     var buttons, activeTextEl, bwidth;
15680
15681     // private
15682     var handleButton = function(button){
15683         dlg.hide();
15684         Roo.callback(opt.fn, opt.scope||window, [button, activeTextEl.dom.value], 1);
15685     };
15686
15687     // private
15688     var handleHide = function(){
15689         if(opt && opt.cls){
15690             dlg.el.removeClass(opt.cls);
15691         }
15692         if(waitTimer){
15693             Roo.TaskMgr.stop(waitTimer);
15694             waitTimer = null;
15695         }
15696     };
15697
15698     // private
15699     var updateButtons = function(b){
15700         var width = 0;
15701         if(!b){
15702             buttons["ok"].hide();
15703             buttons["cancel"].hide();
15704             buttons["yes"].hide();
15705             buttons["no"].hide();
15706             dlg.footer.dom.style.display = 'none';
15707             return width;
15708         }
15709         dlg.footer.dom.style.display = '';
15710         for(var k in buttons){
15711             if(typeof buttons[k] != "function"){
15712                 if(b[k]){
15713                     buttons[k].show();
15714                     buttons[k].setText(typeof b[k] == "string" ? b[k] : Roo.MessageBox.buttonText[k]);
15715                     width += buttons[k].el.getWidth()+15;
15716                 }else{
15717                     buttons[k].hide();
15718                 }
15719             }
15720         }
15721         return width;
15722     };
15723
15724     // private
15725     var handleEsc = function(d, k, e){
15726         if(opt && opt.closable !== false){
15727             dlg.hide();
15728         }
15729         if(e){
15730             e.stopEvent();
15731         }
15732     };
15733
15734     return {
15735         /**
15736          * Returns a reference to the underlying {@link Roo.BasicDialog} element
15737          * @return {Roo.BasicDialog} The BasicDialog element
15738          */
15739         getDialog : function(){
15740            if(!dlg){
15741                 dlg = new Roo.BasicDialog("x-msg-box", {
15742                     autoCreate : true,
15743                     shadow: true,
15744                     draggable: true,
15745                     resizable:false,
15746                     constraintoviewport:false,
15747                     fixedcenter:true,
15748                     collapsible : false,
15749                     shim:true,
15750                     modal: true,
15751                     width:400, height:100,
15752                     buttonAlign:"center",
15753                     closeClick : function(){
15754                         if(opt && opt.buttons && opt.buttons.no && !opt.buttons.cancel){
15755                             handleButton("no");
15756                         }else{
15757                             handleButton("cancel");
15758                         }
15759                     }
15760                 });
15761                 dlg.on("hide", handleHide);
15762                 mask = dlg.mask;
15763                 dlg.addKeyListener(27, handleEsc);
15764                 buttons = {};
15765                 var bt = this.buttonText;
15766                 buttons["ok"] = dlg.addButton(bt["ok"], handleButton.createCallback("ok"));
15767                 buttons["yes"] = dlg.addButton(bt["yes"], handleButton.createCallback("yes"));
15768                 buttons["no"] = dlg.addButton(bt["no"], handleButton.createCallback("no"));
15769                 buttons["cancel"] = dlg.addButton(bt["cancel"], handleButton.createCallback("cancel"));
15770                 bodyEl = dlg.body.createChild({
15771
15772                     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>'
15773                 });
15774                 msgEl = bodyEl.dom.firstChild;
15775                 textboxEl = Roo.get(bodyEl.dom.childNodes[2]);
15776                 textboxEl.enableDisplayMode();
15777                 textboxEl.addKeyListener([10,13], function(){
15778                     if(dlg.isVisible() && opt && opt.buttons){
15779                         if(opt.buttons.ok){
15780                             handleButton("ok");
15781                         }else if(opt.buttons.yes){
15782                             handleButton("yes");
15783                         }
15784                     }
15785                 });
15786                 textareaEl = Roo.get(bodyEl.dom.childNodes[3]);
15787                 textareaEl.enableDisplayMode();
15788                 progressEl = Roo.get(bodyEl.dom.childNodes[4]);
15789                 progressEl.enableDisplayMode();
15790                 var pf = progressEl.dom.firstChild;
15791                 if (pf) {
15792                     pp = Roo.get(pf.firstChild);
15793                     pp.setHeight(pf.offsetHeight);
15794                 }
15795                 
15796             }
15797             return dlg;
15798         },
15799
15800         /**
15801          * Updates the message box body text
15802          * @param {String} text (optional) Replaces the message box element's innerHTML with the specified string (defaults to
15803          * the XHTML-compliant non-breaking space character '&amp;#160;')
15804          * @return {Roo.MessageBox} This message box
15805          */
15806         updateText : function(text){
15807             if(!dlg.isVisible() && !opt.width){
15808                 dlg.resizeTo(this.maxWidth, 100); // resize first so content is never clipped from previous shows
15809             }
15810             msgEl.innerHTML = text || '&#160;';
15811       
15812             var cw =  Math.max(msgEl.offsetWidth, msgEl.parentNode.scrollWidth);
15813             //Roo.log("guesed size: " + JSON.stringify([cw,msgEl.offsetWidth, msgEl.parentNode.scrollWidth]));
15814             var w = Math.max(
15815                     Math.min(opt.width || cw , this.maxWidth), 
15816                     Math.max(opt.minWidth || this.minWidth, bwidth)
15817             );
15818             if(opt.prompt){
15819                 activeTextEl.setWidth(w);
15820             }
15821             if(dlg.isVisible()){
15822                 dlg.fixedcenter = false;
15823             }
15824             // to big, make it scroll. = But as usual stupid IE does not support
15825             // !important..
15826             
15827             if ( bodyEl.getHeight() > (Roo.lib.Dom.getViewHeight() - 100)) {
15828                 bodyEl.setHeight ( Roo.lib.Dom.getViewHeight() - 100 );
15829                 bodyEl.dom.style.overflowY = 'auto' + ( Roo.isIE ? '' : ' !important');
15830             } else {
15831                 bodyEl.dom.style.height = '';
15832                 bodyEl.dom.style.overflowY = '';
15833             }
15834             if (cw > w) {
15835                 bodyEl.dom.style.get = 'auto' + ( Roo.isIE ? '' : ' !important');
15836             } else {
15837                 bodyEl.dom.style.overflowX = '';
15838             }
15839             
15840             dlg.setContentSize(w, bodyEl.getHeight());
15841             if(dlg.isVisible()){
15842                 dlg.fixedcenter = true;
15843             }
15844             return this;
15845         },
15846
15847         /**
15848          * Updates a progress-style message box's text and progress bar.  Only relevant on message boxes
15849          * initiated via {@link Roo.MessageBox#progress} or by calling {@link Roo.MessageBox#show} with progress: true.
15850          * @param {Number} value Any number between 0 and 1 (e.g., .5)
15851          * @param {String} text (optional) If defined, the message box's body text is replaced with the specified string (defaults to undefined)
15852          * @return {Roo.MessageBox} This message box
15853          */
15854         updateProgress : function(value, text){
15855             if(text){
15856                 this.updateText(text);
15857             }
15858             if (pp) { // weird bug on my firefox - for some reason this is not defined
15859                 pp.setWidth(Math.floor(value*progressEl.dom.firstChild.offsetWidth));
15860             }
15861             return this;
15862         },        
15863
15864         /**
15865          * Returns true if the message box is currently displayed
15866          * @return {Boolean} True if the message box is visible, else false
15867          */
15868         isVisible : function(){
15869             return dlg && dlg.isVisible();  
15870         },
15871
15872         /**
15873          * Hides the message box if it is displayed
15874          */
15875         hide : function(){
15876             if(this.isVisible()){
15877                 dlg.hide();
15878             }  
15879         },
15880
15881         /**
15882          * Displays a new message box, or reinitializes an existing message box, based on the config options
15883          * passed in. All functions (e.g. prompt, alert, etc) on MessageBox call this function internally.
15884          * The following config object properties are supported:
15885          * <pre>
15886 Property    Type             Description
15887 ----------  ---------------  ------------------------------------------------------------------------------------
15888 animEl            String/Element   An id or Element from which the message box should animate as it opens and
15889                                    closes (defaults to undefined)
15890 buttons           Object/Boolean   A button config object (e.g., Roo.MessageBox.OKCANCEL or {ok:'Foo',
15891                                    cancel:'Bar'}), or false to not show any buttons (defaults to false)
15892 closable          Boolean          False to hide the top-right close button (defaults to true).  Note that
15893                                    progress and wait dialogs will ignore this property and always hide the
15894                                    close button as they can only be closed programmatically.
15895 cls               String           A custom CSS class to apply to the message box element
15896 defaultTextHeight Number           The default height in pixels of the message box's multiline textarea if
15897                                    displayed (defaults to 75)
15898 fn                Function         A callback function to execute after closing the dialog.  The arguments to the
15899                                    function will be btn (the name of the button that was clicked, if applicable,
15900                                    e.g. "ok"), and text (the value of the active text field, if applicable).
15901                                    Progress and wait dialogs will ignore this option since they do not respond to
15902                                    user actions and can only be closed programmatically, so any required function
15903                                    should be called by the same code after it closes the dialog.
15904 icon              String           A CSS class that provides a background image to be used as an icon for
15905                                    the dialog (e.g., Roo.MessageBox.WARNING or 'custom-class', defaults to '')
15906 maxWidth          Number           The maximum width in pixels of the message box (defaults to 600)
15907 minWidth          Number           The minimum width in pixels of the message box (defaults to 100)
15908 modal             Boolean          False to allow user interaction with the page while the message box is
15909                                    displayed (defaults to true)
15910 msg               String           A string that will replace the existing message box body text (defaults
15911                                    to the XHTML-compliant non-breaking space character '&#160;')
15912 multiline         Boolean          True to prompt the user to enter multi-line text (defaults to false)
15913 progress          Boolean          True to display a progress bar (defaults to false)
15914 progressText      String           The text to display inside the progress bar if progress = true (defaults to '')
15915 prompt            Boolean          True to prompt the user to enter single-line text (defaults to false)
15916 proxyDrag         Boolean          True to display a lightweight proxy while dragging (defaults to false)
15917 title             String           The title text
15918 value             String           The string value to set into the active textbox element if displayed
15919 wait              Boolean          True to display a progress bar (defaults to false)
15920 width             Number           The width of the dialog in pixels
15921 </pre>
15922          *
15923          * Example usage:
15924          * <pre><code>
15925 Roo.Msg.show({
15926    title: 'Address',
15927    msg: 'Please enter your address:',
15928    width: 300,
15929    buttons: Roo.MessageBox.OKCANCEL,
15930    multiline: true,
15931    fn: saveAddress,
15932    animEl: 'addAddressBtn'
15933 });
15934 </code></pre>
15935          * @param {Object} config Configuration options
15936          * @return {Roo.MessageBox} This message box
15937          */
15938         show : function(options)
15939         {
15940             
15941             // this causes nightmares if you show one dialog after another
15942             // especially on callbacks..
15943              
15944             if(this.isVisible()){
15945                 
15946                 this.hide();
15947                 Roo.log("[Roo.Messagebox] Show called while message displayed:" );
15948                 Roo.log("Old Dialog Message:" +  msgEl.innerHTML );
15949                 Roo.log("New Dialog Message:" +  options.msg )
15950                 //this.alert("ERROR", "Multiple dialogs where displayed at the same time");
15951                 //throw "Roo.MessageBox ERROR : Multiple dialogs where displayed at the same time";
15952                 
15953             }
15954             var d = this.getDialog();
15955             opt = options;
15956             d.setTitle(opt.title || "&#160;");
15957             d.close.setDisplayed(opt.closable !== false);
15958             activeTextEl = textboxEl;
15959             opt.prompt = opt.prompt || (opt.multiline ? true : false);
15960             if(opt.prompt){
15961                 if(opt.multiline){
15962                     textboxEl.hide();
15963                     textareaEl.show();
15964                     textareaEl.setHeight(typeof opt.multiline == "number" ?
15965                         opt.multiline : this.defaultTextHeight);
15966                     activeTextEl = textareaEl;
15967                 }else{
15968                     textboxEl.show();
15969                     textareaEl.hide();
15970                 }
15971             }else{
15972                 textboxEl.hide();
15973                 textareaEl.hide();
15974             }
15975             progressEl.setDisplayed(opt.progress === true);
15976             this.updateProgress(0);
15977             activeTextEl.dom.value = opt.value || "";
15978             if(opt.prompt){
15979                 dlg.setDefaultButton(activeTextEl);
15980             }else{
15981                 var bs = opt.buttons;
15982                 var db = null;
15983                 if(bs && bs.ok){
15984                     db = buttons["ok"];
15985                 }else if(bs && bs.yes){
15986                     db = buttons["yes"];
15987                 }
15988                 dlg.setDefaultButton(db);
15989             }
15990             bwidth = updateButtons(opt.buttons);
15991             this.updateText(opt.msg);
15992             if(opt.cls){
15993                 d.el.addClass(opt.cls);
15994             }
15995             d.proxyDrag = opt.proxyDrag === true;
15996             d.modal = opt.modal !== false;
15997             d.mask = opt.modal !== false ? mask : false;
15998             if(!d.isVisible()){
15999                 // force it to the end of the z-index stack so it gets a cursor in FF
16000                 document.body.appendChild(dlg.el.dom);
16001                 d.animateTarget = null;
16002                 d.show(options.animEl);
16003             }
16004             return this;
16005         },
16006
16007         /**
16008          * Displays a message box with a progress bar.  This message box has no buttons and is not closeable by
16009          * the user.  You are responsible for updating the progress bar as needed via {@link Roo.MessageBox#updateProgress}
16010          * and closing the message box when the process is complete.
16011          * @param {String} title The title bar text
16012          * @param {String} msg The message box body text
16013          * @return {Roo.MessageBox} This message box
16014          */
16015         progress : function(title, msg){
16016             this.show({
16017                 title : title,
16018                 msg : msg,
16019                 buttons: false,
16020                 progress:true,
16021                 closable:false,
16022                 minWidth: this.minProgressWidth,
16023                 modal : true
16024             });
16025             return this;
16026         },
16027
16028         /**
16029          * Displays a standard read-only message box with an OK button (comparable to the basic JavaScript Window.alert).
16030          * If a callback function is passed it will be called after the user clicks the button, and the
16031          * id of the button that was clicked will be passed as the only parameter to the callback
16032          * (could also be the top-right close button).
16033          * @param {String} title The title bar text
16034          * @param {String} msg The message box body text
16035          * @param {Function} fn (optional) The callback function invoked after the message box is closed
16036          * @param {Object} scope (optional) The scope of the callback function
16037          * @return {Roo.MessageBox} This message box
16038          */
16039         alert : function(title, msg, fn, scope){
16040             this.show({
16041                 title : title,
16042                 msg : msg,
16043                 buttons: this.OK,
16044                 fn: fn,
16045                 scope : scope,
16046                 modal : true
16047             });
16048             return this;
16049         },
16050
16051         /**
16052          * Displays a message box with an infinitely auto-updating progress bar.  This can be used to block user
16053          * interaction while waiting for a long-running process to complete that does not have defined intervals.
16054          * You are responsible for closing the message box when the process is complete.
16055          * @param {String} msg The message box body text
16056          * @param {String} title (optional) The title bar text
16057          * @return {Roo.MessageBox} This message box
16058          */
16059         wait : function(msg, title){
16060             this.show({
16061                 title : title,
16062                 msg : msg,
16063                 buttons: false,
16064                 closable:false,
16065                 progress:true,
16066                 modal:true,
16067                 width:300,
16068                 wait:true
16069             });
16070             waitTimer = Roo.TaskMgr.start({
16071                 run: function(i){
16072                     Roo.MessageBox.updateProgress(((((i+20)%20)+1)*5)*.01);
16073                 },
16074                 interval: 1000
16075             });
16076             return this;
16077         },
16078
16079         /**
16080          * Displays a confirmation message box with Yes and No buttons (comparable to JavaScript's Window.confirm).
16081          * If a callback function is passed it will be called after the user clicks either button, and the id of the
16082          * button that was clicked will be passed as the only parameter to the callback (could also be the top-right close button).
16083          * @param {String} title The title bar text
16084          * @param {String} msg The message box body text
16085          * @param {Function} fn (optional) The callback function invoked after the message box is closed
16086          * @param {Object} scope (optional) The scope of the callback function
16087          * @return {Roo.MessageBox} This message box
16088          */
16089         confirm : function(title, msg, fn, scope){
16090             this.show({
16091                 title : title,
16092                 msg : msg,
16093                 buttons: this.YESNO,
16094                 fn: fn,
16095                 scope : scope,
16096                 modal : true
16097             });
16098             return this;
16099         },
16100
16101         /**
16102          * Displays a message box with OK and Cancel buttons prompting the user to enter some text (comparable to
16103          * JavaScript's Window.prompt).  The prompt can be a single-line or multi-line textbox.  If a callback function
16104          * is passed it will be called after the user clicks either button, and the id of the button that was clicked
16105          * (could also be the top-right close button) and the text that was entered will be passed as the two
16106          * parameters to the callback.
16107          * @param {String} title The title bar text
16108          * @param {String} msg The message box body text
16109          * @param {Function} fn (optional) The callback function invoked after the message box is closed
16110          * @param {Object} scope (optional) The scope of the callback function
16111          * @param {Boolean/Number} multiline (optional) True to create a multiline textbox using the defaultTextHeight
16112          * property, or the height in pixels to create the textbox (defaults to false / single-line)
16113          * @return {Roo.MessageBox} This message box
16114          */
16115         prompt : function(title, msg, fn, scope, multiline){
16116             this.show({
16117                 title : title,
16118                 msg : msg,
16119                 buttons: this.OKCANCEL,
16120                 fn: fn,
16121                 minWidth:250,
16122                 scope : scope,
16123                 prompt:true,
16124                 multiline: multiline,
16125                 modal : true
16126             });
16127             return this;
16128         },
16129
16130         /**
16131          * Button config that displays a single OK button
16132          * @type Object
16133          */
16134         OK : {ok:true},
16135         /**
16136          * Button config that displays Yes and No buttons
16137          * @type Object
16138          */
16139         YESNO : {yes:true, no:true},
16140         /**
16141          * Button config that displays OK and Cancel buttons
16142          * @type Object
16143          */
16144         OKCANCEL : {ok:true, cancel:true},
16145         /**
16146          * Button config that displays Yes, No and Cancel buttons
16147          * @type Object
16148          */
16149         YESNOCANCEL : {yes:true, no:true, cancel:true},
16150
16151         /**
16152          * The default height in pixels of the message box's multiline textarea if displayed (defaults to 75)
16153          * @type Number
16154          */
16155         defaultTextHeight : 75,
16156         /**
16157          * The maximum width in pixels of the message box (defaults to 600)
16158          * @type Number
16159          */
16160         maxWidth : 600,
16161         /**
16162          * The minimum width in pixels of the message box (defaults to 100)
16163          * @type Number
16164          */
16165         minWidth : 100,
16166         /**
16167          * The minimum width in pixels of the message box if it is a progress-style dialog.  This is useful
16168          * for setting a different minimum width than text-only dialogs may need (defaults to 250)
16169          * @type Number
16170          */
16171         minProgressWidth : 250,
16172         /**
16173          * An object containing the default button text strings that can be overriden for localized language support.
16174          * Supported properties are: ok, cancel, yes and no.
16175          * Customize the default text like so: Roo.MessageBox.buttonText.yes = "S?";
16176          * @type Object
16177          */
16178         buttonText : {
16179             ok : "OK",
16180             cancel : "Cancel",
16181             yes : "Yes",
16182             no : "No"
16183         }
16184     };
16185 }();
16186
16187 /**
16188  * Shorthand for {@link Roo.MessageBox}
16189  */
16190 Roo.Msg = Roo.MessageBox;/*
16191  * Based on:
16192  * Ext JS Library 1.1.1
16193  * Copyright(c) 2006-2007, Ext JS, LLC.
16194  *
16195  * Originally Released Under LGPL - original licence link has changed is not relivant.
16196  *
16197  * Fork - LGPL
16198  * <script type="text/javascript">
16199  */
16200 /**
16201  * @class Roo.QuickTips
16202  * Provides attractive and customizable tooltips for any element.
16203  * @singleton
16204  */
16205 Roo.QuickTips = function(){
16206     var el, tipBody, tipBodyText, tipTitle, tm, cfg, close, tagEls = {}, esc, removeCls = null, bdLeft, bdRight;
16207     var ce, bd, xy, dd;
16208     var visible = false, disabled = true, inited = false;
16209     var showProc = 1, hideProc = 1, dismissProc = 1, locks = [];
16210     
16211     var onOver = function(e){
16212         if(disabled){
16213             return;
16214         }
16215         var t = e.getTarget();
16216         if(!t || t.nodeType !== 1 || t == document || t == document.body){
16217             return;
16218         }
16219         if(ce && t == ce.el){
16220             clearTimeout(hideProc);
16221             return;
16222         }
16223         if(t && tagEls[t.id]){
16224             tagEls[t.id].el = t;
16225             showProc = show.defer(tm.showDelay, tm, [tagEls[t.id]]);
16226             return;
16227         }
16228         var ttp, et = Roo.fly(t);
16229         var ns = cfg.namespace;
16230         if(tm.interceptTitles && t.title){
16231             ttp = t.title;
16232             t.qtip = ttp;
16233             t.removeAttribute("title");
16234             e.preventDefault();
16235         }else{
16236             ttp = t.qtip || et.getAttributeNS(ns, cfg.attribute);
16237         }
16238         if(ttp){
16239             showProc = show.defer(tm.showDelay, tm, [{
16240                 el: t, 
16241                 text: ttp, 
16242                 width: et.getAttributeNS(ns, cfg.width),
16243                 autoHide: et.getAttributeNS(ns, cfg.hide) != "user",
16244                 title: et.getAttributeNS(ns, cfg.title),
16245                     cls: et.getAttributeNS(ns, cfg.cls)
16246             }]);
16247         }
16248     };
16249     
16250     var onOut = function(e){
16251         clearTimeout(showProc);
16252         var t = e.getTarget();
16253         if(t && ce && ce.el == t && (tm.autoHide && ce.autoHide !== false)){
16254             hideProc = setTimeout(hide, tm.hideDelay);
16255         }
16256     };
16257     
16258     var onMove = function(e){
16259         if(disabled){
16260             return;
16261         }
16262         xy = e.getXY();
16263         xy[1] += 18;
16264         if(tm.trackMouse && ce){
16265             el.setXY(xy);
16266         }
16267     };
16268     
16269     var onDown = function(e){
16270         clearTimeout(showProc);
16271         clearTimeout(hideProc);
16272         if(!e.within(el)){
16273             if(tm.hideOnClick){
16274                 hide();
16275                 tm.disable();
16276                 tm.enable.defer(100, tm);
16277             }
16278         }
16279     };
16280     
16281     var getPad = function(){
16282         return 2;//bdLeft.getPadding('l')+bdRight.getPadding('r');
16283     };
16284
16285     var show = function(o){
16286         if(disabled){
16287             return;
16288         }
16289         clearTimeout(dismissProc);
16290         ce = o;
16291         if(removeCls){ // in case manually hidden
16292             el.removeClass(removeCls);
16293             removeCls = null;
16294         }
16295         if(ce.cls){
16296             el.addClass(ce.cls);
16297             removeCls = ce.cls;
16298         }
16299         if(ce.title){
16300             tipTitle.update(ce.title);
16301             tipTitle.show();
16302         }else{
16303             tipTitle.update('');
16304             tipTitle.hide();
16305         }
16306         el.dom.style.width  = tm.maxWidth+'px';
16307         //tipBody.dom.style.width = '';
16308         tipBodyText.update(o.text);
16309         var p = getPad(), w = ce.width;
16310         if(!w){
16311             var td = tipBodyText.dom;
16312             var aw = Math.max(td.offsetWidth, td.clientWidth, td.scrollWidth);
16313             if(aw > tm.maxWidth){
16314                 w = tm.maxWidth;
16315             }else if(aw < tm.minWidth){
16316                 w = tm.minWidth;
16317             }else{
16318                 w = aw;
16319             }
16320         }
16321         //tipBody.setWidth(w);
16322         el.setWidth(parseInt(w, 10) + p);
16323         if(ce.autoHide === false){
16324             close.setDisplayed(true);
16325             if(dd){
16326                 dd.unlock();
16327             }
16328         }else{
16329             close.setDisplayed(false);
16330             if(dd){
16331                 dd.lock();
16332             }
16333         }
16334         if(xy){
16335             el.avoidY = xy[1]-18;
16336             el.setXY(xy);
16337         }
16338         if(tm.animate){
16339             el.setOpacity(.1);
16340             el.setStyle("visibility", "visible");
16341             el.fadeIn({callback: afterShow});
16342         }else{
16343             afterShow();
16344         }
16345     };
16346     
16347     var afterShow = function(){
16348         if(ce){
16349             el.show();
16350             esc.enable();
16351             if(tm.autoDismiss && ce.autoHide !== false){
16352                 dismissProc = setTimeout(hide, tm.autoDismissDelay);
16353             }
16354         }
16355     };
16356     
16357     var hide = function(noanim){
16358         clearTimeout(dismissProc);
16359         clearTimeout(hideProc);
16360         ce = null;
16361         if(el.isVisible()){
16362             esc.disable();
16363             if(noanim !== true && tm.animate){
16364                 el.fadeOut({callback: afterHide});
16365             }else{
16366                 afterHide();
16367             } 
16368         }
16369     };
16370     
16371     var afterHide = function(){
16372         el.hide();
16373         if(removeCls){
16374             el.removeClass(removeCls);
16375             removeCls = null;
16376         }
16377     };
16378     
16379     return {
16380         /**
16381         * @cfg {Number} minWidth
16382         * The minimum width of the quick tip (defaults to 40)
16383         */
16384        minWidth : 40,
16385         /**
16386         * @cfg {Number} maxWidth
16387         * The maximum width of the quick tip (defaults to 300)
16388         */
16389        maxWidth : 300,
16390         /**
16391         * @cfg {Boolean} interceptTitles
16392         * True to automatically use the element's DOM title value if available (defaults to false)
16393         */
16394        interceptTitles : false,
16395         /**
16396         * @cfg {Boolean} trackMouse
16397         * True to have the quick tip follow the mouse as it moves over the target element (defaults to false)
16398         */
16399        trackMouse : false,
16400         /**
16401         * @cfg {Boolean} hideOnClick
16402         * True to hide the quick tip if the user clicks anywhere in the document (defaults to true)
16403         */
16404        hideOnClick : true,
16405         /**
16406         * @cfg {Number} showDelay
16407         * Delay in milliseconds before the quick tip displays after the mouse enters the target element (defaults to 500)
16408         */
16409        showDelay : 500,
16410         /**
16411         * @cfg {Number} hideDelay
16412         * Delay in milliseconds before the quick tip hides when autoHide = true (defaults to 200)
16413         */
16414        hideDelay : 200,
16415         /**
16416         * @cfg {Boolean} autoHide
16417         * True to automatically hide the quick tip after the mouse exits the target element (defaults to true).
16418         * Used in conjunction with hideDelay.
16419         */
16420        autoHide : true,
16421         /**
16422         * @cfg {Boolean}
16423         * True to automatically hide the quick tip after a set period of time, regardless of the user's actions
16424         * (defaults to true).  Used in conjunction with autoDismissDelay.
16425         */
16426        autoDismiss : true,
16427         /**
16428         * @cfg {Number}
16429         * Delay in milliseconds before the quick tip hides when autoDismiss = true (defaults to 5000)
16430         */
16431        autoDismissDelay : 5000,
16432        /**
16433         * @cfg {Boolean} animate
16434         * True to turn on fade animation. Defaults to false (ClearType/scrollbar flicker issues in IE7).
16435         */
16436        animate : false,
16437
16438        /**
16439         * @cfg {String} title
16440         * Title text to display (defaults to '').  This can be any valid HTML markup.
16441         */
16442         title: '',
16443        /**
16444         * @cfg {String} text
16445         * Body text to display (defaults to '').  This can be any valid HTML markup.
16446         */
16447         text : '',
16448        /**
16449         * @cfg {String} cls
16450         * A CSS class to apply to the base quick tip element (defaults to '').
16451         */
16452         cls : '',
16453        /**
16454         * @cfg {Number} width
16455         * Width in pixels of the quick tip (defaults to auto).  Width will be ignored if it exceeds the bounds of
16456         * minWidth or maxWidth.
16457         */
16458         width : null,
16459
16460     /**
16461      * Initialize and enable QuickTips for first use.  This should be called once before the first attempt to access
16462      * or display QuickTips in a page.
16463      */
16464        init : function(){
16465           tm = Roo.QuickTips;
16466           cfg = tm.tagConfig;
16467           if(!inited){
16468               if(!Roo.isReady){ // allow calling of init() before onReady
16469                   Roo.onReady(Roo.QuickTips.init, Roo.QuickTips);
16470                   return;
16471               }
16472               el = new Roo.Layer({cls:"x-tip", shadow:"drop", shim: true, constrain:true, shadowOffset:4});
16473               el.fxDefaults = {stopFx: true};
16474               // maximum custom styling
16475               //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>');
16476               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>');              
16477               tipTitle = el.child('h3');
16478               tipTitle.enableDisplayMode("block");
16479               tipBody = el.child('div.x-tip-bd');
16480               tipBodyText = el.child('div.x-tip-bd-inner');
16481               //bdLeft = el.child('div.x-tip-bd-left');
16482               //bdRight = el.child('div.x-tip-bd-right');
16483               close = el.child('div.x-tip-close');
16484               close.enableDisplayMode("block");
16485               close.on("click", hide);
16486               var d = Roo.get(document);
16487               d.on("mousedown", onDown);
16488               d.on("mouseover", onOver);
16489               d.on("mouseout", onOut);
16490               d.on("mousemove", onMove);
16491               esc = d.addKeyListener(27, hide);
16492               esc.disable();
16493               if(Roo.dd.DD){
16494                   dd = el.initDD("default", null, {
16495                       onDrag : function(){
16496                           el.sync();  
16497                       }
16498                   });
16499                   dd.setHandleElId(tipTitle.id);
16500                   dd.lock();
16501               }
16502               inited = true;
16503           }
16504           this.enable(); 
16505        },
16506
16507     /**
16508      * Configures a new quick tip instance and assigns it to a target element.  The following config options
16509      * are supported:
16510      * <pre>
16511 Property    Type                   Description
16512 ----------  ---------------------  ------------------------------------------------------------------------
16513 target      Element/String/Array   An Element, id or array of ids that this quick tip should be tied to
16514      * </ul>
16515      * @param {Object} config The config object
16516      */
16517        register : function(config){
16518            var cs = config instanceof Array ? config : arguments;
16519            for(var i = 0, len = cs.length; i < len; i++) {
16520                var c = cs[i];
16521                var target = c.target;
16522                if(target){
16523                    if(target instanceof Array){
16524                        for(var j = 0, jlen = target.length; j < jlen; j++){
16525                            tagEls[target[j]] = c;
16526                        }
16527                    }else{
16528                        tagEls[typeof target == 'string' ? target : Roo.id(target)] = c;
16529                    }
16530                }
16531            }
16532        },
16533
16534     /**
16535      * Removes this quick tip from its element and destroys it.
16536      * @param {String/HTMLElement/Element} el The element from which the quick tip is to be removed.
16537      */
16538        unregister : function(el){
16539            delete tagEls[Roo.id(el)];
16540        },
16541
16542     /**
16543      * Enable this quick tip.
16544      */
16545        enable : function(){
16546            if(inited && disabled){
16547                locks.pop();
16548                if(locks.length < 1){
16549                    disabled = false;
16550                }
16551            }
16552        },
16553
16554     /**
16555      * Disable this quick tip.
16556      */
16557        disable : function(){
16558           disabled = true;
16559           clearTimeout(showProc);
16560           clearTimeout(hideProc);
16561           clearTimeout(dismissProc);
16562           if(ce){
16563               hide(true);
16564           }
16565           locks.push(1);
16566        },
16567
16568     /**
16569      * Returns true if the quick tip is enabled, else false.
16570      */
16571        isEnabled : function(){
16572             return !disabled;
16573        },
16574
16575         // private
16576        tagConfig : {
16577            namespace : "ext",
16578            attribute : "qtip",
16579            width : "width",
16580            target : "target",
16581            title : "qtitle",
16582            hide : "hide",
16583            cls : "qclass"
16584        }
16585    };
16586 }();
16587
16588 // backwards compat
16589 Roo.QuickTips.tips = Roo.QuickTips.register;/*
16590  * Based on:
16591  * Ext JS Library 1.1.1
16592  * Copyright(c) 2006-2007, Ext JS, LLC.
16593  *
16594  * Originally Released Under LGPL - original licence link has changed is not relivant.
16595  *
16596  * Fork - LGPL
16597  * <script type="text/javascript">
16598  */
16599  
16600
16601 /**
16602  * @class Roo.tree.TreePanel
16603  * @extends Roo.data.Tree
16604
16605  * @cfg {Boolean} rootVisible false to hide the root node (defaults to true)
16606  * @cfg {Boolean} lines false to disable tree lines (defaults to true)
16607  * @cfg {Boolean} enableDD true to enable drag and drop
16608  * @cfg {Boolean} enableDrag true to enable just drag
16609  * @cfg {Boolean} enableDrop true to enable just drop
16610  * @cfg {Object} dragConfig Custom config to pass to the {@link Roo.tree.TreeDragZone} instance
16611  * @cfg {Object} dropConfig Custom config to pass to the {@link Roo.tree.TreeDropZone} instance
16612  * @cfg {String} ddGroup The DD group this TreePanel belongs to
16613  * @cfg {String} ddAppendOnly True if the tree should only allow append drops (use for trees which are sorted)
16614  * @cfg {Boolean} ddScroll true to enable YUI body scrolling
16615  * @cfg {Boolean} containerScroll true to register this container with ScrollManager
16616  * @cfg {Boolean} hlDrop false to disable node highlight on drop (defaults to the value of Roo.enableFx)
16617  * @cfg {String} hlColor The color of the node highlight (defaults to C3DAF9)
16618  * @cfg {Boolean} animate true to enable animated expand/collapse (defaults to the value of Roo.enableFx)
16619  * @cfg {Boolean} singleExpand true if only 1 node per branch may be expanded
16620  * @cfg {Boolean} selModel A tree selection model to use with this TreePanel (defaults to a {@link Roo.tree.DefaultSelectionModel})
16621  * @cfg {Boolean} loader A TreeLoader for use with this TreePanel
16622  * @cfg {Object|Roo.tree.TreeEditor} editor The TreeEditor or xtype data to display when clicked.
16623  * @cfg {String} pathSeparator The token used to separate sub-paths in path strings (defaults to '/')
16624  * @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>
16625  * @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>
16626  * 
16627  * @constructor
16628  * @param {String/HTMLElement/Element} el The container element
16629  * @param {Object} config
16630  */
16631 Roo.tree.TreePanel = function(el, config){
16632     var root = false;
16633     var loader = false;
16634     if (config.root) {
16635         root = config.root;
16636         delete config.root;
16637     }
16638     if (config.loader) {
16639         loader = config.loader;
16640         delete config.loader;
16641     }
16642     
16643     Roo.apply(this, config);
16644     Roo.tree.TreePanel.superclass.constructor.call(this);
16645     this.el = Roo.get(el);
16646     this.el.addClass('x-tree');
16647     //console.log(root);
16648     if (root) {
16649         this.setRootNode( Roo.factory(root, Roo.tree));
16650     }
16651     if (loader) {
16652         this.loader = Roo.factory(loader, Roo.tree);
16653     }
16654    /**
16655     * Read-only. The id of the container element becomes this TreePanel's id.
16656     */
16657     this.id = this.el.id;
16658     this.addEvents({
16659         /**
16660         * @event beforeload
16661         * Fires before a node is loaded, return false to cancel
16662         * @param {Node} node The node being loaded
16663         */
16664         "beforeload" : true,
16665         /**
16666         * @event load
16667         * Fires when a node is loaded
16668         * @param {Node} node The node that was loaded
16669         */
16670         "load" : true,
16671         /**
16672         * @event textchange
16673         * Fires when the text for a node is changed
16674         * @param {Node} node The node
16675         * @param {String} text The new text
16676         * @param {String} oldText The old text
16677         */
16678         "textchange" : true,
16679         /**
16680         * @event beforeexpand
16681         * Fires before a node is expanded, return false to cancel.
16682         * @param {Node} node The node
16683         * @param {Boolean} deep
16684         * @param {Boolean} anim
16685         */
16686         "beforeexpand" : true,
16687         /**
16688         * @event beforecollapse
16689         * Fires before a node is collapsed, return false to cancel.
16690         * @param {Node} node The node
16691         * @param {Boolean} deep
16692         * @param {Boolean} anim
16693         */
16694         "beforecollapse" : true,
16695         /**
16696         * @event expand
16697         * Fires when a node is expanded
16698         * @param {Node} node The node
16699         */
16700         "expand" : true,
16701         /**
16702         * @event disabledchange
16703         * Fires when the disabled status of a node changes
16704         * @param {Node} node The node
16705         * @param {Boolean} disabled
16706         */
16707         "disabledchange" : true,
16708         /**
16709         * @event collapse
16710         * Fires when a node is collapsed
16711         * @param {Node} node The node
16712         */
16713         "collapse" : true,
16714         /**
16715         * @event beforeclick
16716         * Fires before click processing on a node. Return false to cancel the default action.
16717         * @param {Node} node The node
16718         * @param {Roo.EventObject} e The event object
16719         */
16720         "beforeclick":true,
16721         /**
16722         * @event checkchange
16723         * Fires when a node with a checkbox's checked property changes
16724         * @param {Node} this This node
16725         * @param {Boolean} checked
16726         */
16727         "checkchange":true,
16728         /**
16729         * @event click
16730         * Fires when a node is clicked
16731         * @param {Node} node The node
16732         * @param {Roo.EventObject} e The event object
16733         */
16734         "click":true,
16735         /**
16736         * @event dblclick
16737         * Fires when a node is double clicked
16738         * @param {Node} node The node
16739         * @param {Roo.EventObject} e The event object
16740         */
16741         "dblclick":true,
16742         /**
16743         * @event contextmenu
16744         * Fires when a node is right clicked
16745         * @param {Node} node The node
16746         * @param {Roo.EventObject} e The event object
16747         */
16748         "contextmenu":true,
16749         /**
16750         * @event beforechildrenrendered
16751         * Fires right before the child nodes for a node are rendered
16752         * @param {Node} node The node
16753         */
16754         "beforechildrenrendered":true,
16755         /**
16756         * @event startdrag
16757         * Fires when a node starts being dragged
16758         * @param {Roo.tree.TreePanel} this
16759         * @param {Roo.tree.TreeNode} node
16760         * @param {event} e The raw browser event
16761         */ 
16762        "startdrag" : true,
16763        /**
16764         * @event enddrag
16765         * Fires when a drag operation is complete
16766         * @param {Roo.tree.TreePanel} this
16767         * @param {Roo.tree.TreeNode} node
16768         * @param {event} e The raw browser event
16769         */
16770        "enddrag" : true,
16771        /**
16772         * @event dragdrop
16773         * Fires when a dragged node is dropped on a valid DD target
16774         * @param {Roo.tree.TreePanel} this
16775         * @param {Roo.tree.TreeNode} node
16776         * @param {DD} dd The dd it was dropped on
16777         * @param {event} e The raw browser event
16778         */
16779        "dragdrop" : true,
16780        /**
16781         * @event beforenodedrop
16782         * Fires when a DD object is dropped on a node in this tree for preprocessing. Return false to cancel the drop. The dropEvent
16783         * passed to handlers has the following properties:<br />
16784         * <ul style="padding:5px;padding-left:16px;">
16785         * <li>tree - The TreePanel</li>
16786         * <li>target - The node being targeted for the drop</li>
16787         * <li>data - The drag data from the drag source</li>
16788         * <li>point - The point of the drop - append, above or below</li>
16789         * <li>source - The drag source</li>
16790         * <li>rawEvent - Raw mouse event</li>
16791         * <li>dropNode - Drop node(s) provided by the source <b>OR</b> you can supply node(s)
16792         * to be inserted by setting them on this object.</li>
16793         * <li>cancel - Set this to true to cancel the drop.</li>
16794         * </ul>
16795         * @param {Object} dropEvent
16796         */
16797        "beforenodedrop" : true,
16798        /**
16799         * @event nodedrop
16800         * Fires after a DD object is dropped on a node in this tree. The dropEvent
16801         * passed to handlers has the following properties:<br />
16802         * <ul style="padding:5px;padding-left:16px;">
16803         * <li>tree - The TreePanel</li>
16804         * <li>target - The node being targeted for the drop</li>
16805         * <li>data - The drag data from the drag source</li>
16806         * <li>point - The point of the drop - append, above or below</li>
16807         * <li>source - The drag source</li>
16808         * <li>rawEvent - Raw mouse event</li>
16809         * <li>dropNode - Dropped node(s).</li>
16810         * </ul>
16811         * @param {Object} dropEvent
16812         */
16813        "nodedrop" : true,
16814         /**
16815         * @event nodedragover
16816         * Fires when a tree node is being targeted for a drag drop, return false to signal drop not allowed. The dragOverEvent
16817         * passed to handlers has the following properties:<br />
16818         * <ul style="padding:5px;padding-left:16px;">
16819         * <li>tree - The TreePanel</li>
16820         * <li>target - The node being targeted for the drop</li>
16821         * <li>data - The drag data from the drag source</li>
16822         * <li>point - The point of the drop - append, above or below</li>
16823         * <li>source - The drag source</li>
16824         * <li>rawEvent - Raw mouse event</li>
16825         * <li>dropNode - Drop node(s) provided by the source.</li>
16826         * <li>cancel - Set this to true to signal drop not allowed.</li>
16827         * </ul>
16828         * @param {Object} dragOverEvent
16829         */
16830        "nodedragover" : true
16831         
16832     });
16833     if(this.singleExpand){
16834        this.on("beforeexpand", this.restrictExpand, this);
16835     }
16836     if (this.editor) {
16837         this.editor.tree = this;
16838         this.editor = Roo.factory(this.editor, Roo.tree);
16839     }
16840     
16841     if (this.selModel) {
16842         this.selModel = Roo.factory(this.selModel, Roo.tree);
16843     }
16844    
16845 };
16846 Roo.extend(Roo.tree.TreePanel, Roo.data.Tree, {
16847     rootVisible : true,
16848     animate: Roo.enableFx,
16849     lines : true,
16850     enableDD : false,
16851     hlDrop : Roo.enableFx,
16852   
16853     renderer: false,
16854     
16855     rendererTip: false,
16856     // private
16857     restrictExpand : function(node){
16858         var p = node.parentNode;
16859         if(p){
16860             if(p.expandedChild && p.expandedChild.parentNode == p){
16861                 p.expandedChild.collapse();
16862             }
16863             p.expandedChild = node;
16864         }
16865     },
16866
16867     // private override
16868     setRootNode : function(node){
16869         Roo.tree.TreePanel.superclass.setRootNode.call(this, node);
16870         if(!this.rootVisible){
16871             node.ui = new Roo.tree.RootTreeNodeUI(node);
16872         }
16873         return node;
16874     },
16875
16876     /**
16877      * Returns the container element for this TreePanel
16878      */
16879     getEl : function(){
16880         return this.el;
16881     },
16882
16883     /**
16884      * Returns the default TreeLoader for this TreePanel
16885      */
16886     getLoader : function(){
16887         return this.loader;
16888     },
16889
16890     /**
16891      * Expand all nodes
16892      */
16893     expandAll : function(){
16894         this.root.expand(true);
16895     },
16896
16897     /**
16898      * Collapse all nodes
16899      */
16900     collapseAll : function(){
16901         this.root.collapse(true);
16902     },
16903
16904     /**
16905      * Returns the selection model used by this TreePanel
16906      */
16907     getSelectionModel : function(){
16908         if(!this.selModel){
16909             this.selModel = new Roo.tree.DefaultSelectionModel();
16910         }
16911         return this.selModel;
16912     },
16913
16914     /**
16915      * Retrieve an array of checked nodes, or an array of a specific attribute of checked nodes (e.g. "id")
16916      * @param {String} attribute (optional) Defaults to null (return the actual nodes)
16917      * @param {TreeNode} startNode (optional) The node to start from, defaults to the root
16918      * @return {Array}
16919      */
16920     getChecked : function(a, startNode){
16921         startNode = startNode || this.root;
16922         var r = [];
16923         var f = function(){
16924             if(this.attributes.checked){
16925                 r.push(!a ? this : (a == 'id' ? this.id : this.attributes[a]));
16926             }
16927         }
16928         startNode.cascade(f);
16929         return r;
16930     },
16931
16932     /**
16933      * Expands a specified path in this TreePanel. A path can be retrieved from a node with {@link Roo.data.Node#getPath}
16934      * @param {String} path
16935      * @param {String} attr (optional) The attribute used in the path (see {@link Roo.data.Node#getPath} for more info)
16936      * @param {Function} callback (optional) The callback to call when the expand is complete. The callback will be called with
16937      * (bSuccess, oLastNode) where bSuccess is if the expand was successful and oLastNode is the last node that was expanded.
16938      */
16939     expandPath : function(path, attr, callback){
16940         attr = attr || "id";
16941         var keys = path.split(this.pathSeparator);
16942         var curNode = this.root;
16943         if(curNode.attributes[attr] != keys[1]){ // invalid root
16944             if(callback){
16945                 callback(false, null);
16946             }
16947             return;
16948         }
16949         var index = 1;
16950         var f = function(){
16951             if(++index == keys.length){
16952                 if(callback){
16953                     callback(true, curNode);
16954                 }
16955                 return;
16956             }
16957             var c = curNode.findChild(attr, keys[index]);
16958             if(!c){
16959                 if(callback){
16960                     callback(false, curNode);
16961                 }
16962                 return;
16963             }
16964             curNode = c;
16965             c.expand(false, false, f);
16966         };
16967         curNode.expand(false, false, f);
16968     },
16969
16970     /**
16971      * Selects the node in this tree at the specified path. A path can be retrieved from a node with {@link Roo.data.Node#getPath}
16972      * @param {String} path
16973      * @param {String} attr (optional) The attribute used in the path (see {@link Roo.data.Node#getPath} for more info)
16974      * @param {Function} callback (optional) The callback to call when the selection is complete. The callback will be called with
16975      * (bSuccess, oSelNode) where bSuccess is if the selection was successful and oSelNode is the selected node.
16976      */
16977     selectPath : function(path, attr, callback){
16978         attr = attr || "id";
16979         var keys = path.split(this.pathSeparator);
16980         var v = keys.pop();
16981         if(keys.length > 0){
16982             var f = function(success, node){
16983                 if(success && node){
16984                     var n = node.findChild(attr, v);
16985                     if(n){
16986                         n.select();
16987                         if(callback){
16988                             callback(true, n);
16989                         }
16990                     }else if(callback){
16991                         callback(false, n);
16992                     }
16993                 }else{
16994                     if(callback){
16995                         callback(false, n);
16996                     }
16997                 }
16998             };
16999             this.expandPath(keys.join(this.pathSeparator), attr, f);
17000         }else{
17001             this.root.select();
17002             if(callback){
17003                 callback(true, this.root);
17004             }
17005         }
17006     },
17007
17008     getTreeEl : function(){
17009         return this.el;
17010     },
17011
17012     /**
17013      * Trigger rendering of this TreePanel
17014      */
17015     render : function(){
17016         if (this.innerCt) {
17017             return this; // stop it rendering more than once!!
17018         }
17019         
17020         this.innerCt = this.el.createChild({tag:"ul",
17021                cls:"x-tree-root-ct " +
17022                (this.lines ? "x-tree-lines" : "x-tree-no-lines")});
17023
17024         if(this.containerScroll){
17025             Roo.dd.ScrollManager.register(this.el);
17026         }
17027         if((this.enableDD || this.enableDrop) && !this.dropZone){
17028            /**
17029             * The dropZone used by this tree if drop is enabled
17030             * @type Roo.tree.TreeDropZone
17031             */
17032              this.dropZone = new Roo.tree.TreeDropZone(this, this.dropConfig || {
17033                ddGroup: this.ddGroup || "TreeDD", appendOnly: this.ddAppendOnly === true
17034            });
17035         }
17036         if((this.enableDD || this.enableDrag) && !this.dragZone){
17037            /**
17038             * The dragZone used by this tree if drag is enabled
17039             * @type Roo.tree.TreeDragZone
17040             */
17041             this.dragZone = new Roo.tree.TreeDragZone(this, this.dragConfig || {
17042                ddGroup: this.ddGroup || "TreeDD",
17043                scroll: this.ddScroll
17044            });
17045         }
17046         this.getSelectionModel().init(this);
17047         if (!this.root) {
17048             Roo.log("ROOT not set in tree");
17049             return this;
17050         }
17051         this.root.render();
17052         if(!this.rootVisible){
17053             this.root.renderChildren();
17054         }
17055         return this;
17056     }
17057 });/*
17058  * Based on:
17059  * Ext JS Library 1.1.1
17060  * Copyright(c) 2006-2007, Ext JS, LLC.
17061  *
17062  * Originally Released Under LGPL - original licence link has changed is not relivant.
17063  *
17064  * Fork - LGPL
17065  * <script type="text/javascript">
17066  */
17067  
17068
17069 /**
17070  * @class Roo.tree.DefaultSelectionModel
17071  * @extends Roo.util.Observable
17072  * The default single selection for a TreePanel.
17073  * @param {Object} cfg Configuration
17074  */
17075 Roo.tree.DefaultSelectionModel = function(cfg){
17076    this.selNode = null;
17077    
17078    
17079    
17080    this.addEvents({
17081        /**
17082         * @event selectionchange
17083         * Fires when the selected node changes
17084         * @param {DefaultSelectionModel} this
17085         * @param {TreeNode} node the new selection
17086         */
17087        "selectionchange" : true,
17088
17089        /**
17090         * @event beforeselect
17091         * Fires before the selected node changes, return false to cancel the change
17092         * @param {DefaultSelectionModel} this
17093         * @param {TreeNode} node the new selection
17094         * @param {TreeNode} node the old selection
17095         */
17096        "beforeselect" : true
17097    });
17098    
17099     Roo.tree.DefaultSelectionModel.superclass.constructor.call(this,cfg);
17100 };
17101
17102 Roo.extend(Roo.tree.DefaultSelectionModel, Roo.util.Observable, {
17103     init : function(tree){
17104         this.tree = tree;
17105         tree.getTreeEl().on("keydown", this.onKeyDown, this);
17106         tree.on("click", this.onNodeClick, this);
17107     },
17108     
17109     onNodeClick : function(node, e){
17110         if (e.ctrlKey && this.selNode == node)  {
17111             this.unselect(node);
17112             return;
17113         }
17114         this.select(node);
17115     },
17116     
17117     /**
17118      * Select a node.
17119      * @param {TreeNode} node The node to select
17120      * @return {TreeNode} The selected node
17121      */
17122     select : function(node){
17123         var last = this.selNode;
17124         if(last != node && this.fireEvent('beforeselect', this, node, last) !== false){
17125             if(last){
17126                 last.ui.onSelectedChange(false);
17127             }
17128             this.selNode = node;
17129             node.ui.onSelectedChange(true);
17130             this.fireEvent("selectionchange", this, node, last);
17131         }
17132         return node;
17133     },
17134     
17135     /**
17136      * Deselect a node.
17137      * @param {TreeNode} node The node to unselect
17138      */
17139     unselect : function(node){
17140         if(this.selNode == node){
17141             this.clearSelections();
17142         }    
17143     },
17144     
17145     /**
17146      * Clear all selections
17147      */
17148     clearSelections : function(){
17149         var n = this.selNode;
17150         if(n){
17151             n.ui.onSelectedChange(false);
17152             this.selNode = null;
17153             this.fireEvent("selectionchange", this, null);
17154         }
17155         return n;
17156     },
17157     
17158     /**
17159      * Get the selected node
17160      * @return {TreeNode} The selected node
17161      */
17162     getSelectedNode : function(){
17163         return this.selNode;    
17164     },
17165     
17166     /**
17167      * Returns true if the node is selected
17168      * @param {TreeNode} node The node to check
17169      * @return {Boolean}
17170      */
17171     isSelected : function(node){
17172         return this.selNode == node;  
17173     },
17174
17175     /**
17176      * Selects the node above the selected node in the tree, intelligently walking the nodes
17177      * @return TreeNode The new selection
17178      */
17179     selectPrevious : function(){
17180         var s = this.selNode || this.lastSelNode;
17181         if(!s){
17182             return null;
17183         }
17184         var ps = s.previousSibling;
17185         if(ps){
17186             if(!ps.isExpanded() || ps.childNodes.length < 1){
17187                 return this.select(ps);
17188             } else{
17189                 var lc = ps.lastChild;
17190                 while(lc && lc.isExpanded() && lc.childNodes.length > 0){
17191                     lc = lc.lastChild;
17192                 }
17193                 return this.select(lc);
17194             }
17195         } else if(s.parentNode && (this.tree.rootVisible || !s.parentNode.isRoot)){
17196             return this.select(s.parentNode);
17197         }
17198         return null;
17199     },
17200
17201     /**
17202      * Selects the node above the selected node in the tree, intelligently walking the nodes
17203      * @return TreeNode The new selection
17204      */
17205     selectNext : function(){
17206         var s = this.selNode || this.lastSelNode;
17207         if(!s){
17208             return null;
17209         }
17210         if(s.firstChild && s.isExpanded()){
17211              return this.select(s.firstChild);
17212          }else if(s.nextSibling){
17213              return this.select(s.nextSibling);
17214          }else if(s.parentNode){
17215             var newS = null;
17216             s.parentNode.bubble(function(){
17217                 if(this.nextSibling){
17218                     newS = this.getOwnerTree().selModel.select(this.nextSibling);
17219                     return false;
17220                 }
17221             });
17222             return newS;
17223          }
17224         return null;
17225     },
17226
17227     onKeyDown : function(e){
17228         var s = this.selNode || this.lastSelNode;
17229         // undesirable, but required
17230         var sm = this;
17231         if(!s){
17232             return;
17233         }
17234         var k = e.getKey();
17235         switch(k){
17236              case e.DOWN:
17237                  e.stopEvent();
17238                  this.selectNext();
17239              break;
17240              case e.UP:
17241                  e.stopEvent();
17242                  this.selectPrevious();
17243              break;
17244              case e.RIGHT:
17245                  e.preventDefault();
17246                  if(s.hasChildNodes()){
17247                      if(!s.isExpanded()){
17248                          s.expand();
17249                      }else if(s.firstChild){
17250                          this.select(s.firstChild, e);
17251                      }
17252                  }
17253              break;
17254              case e.LEFT:
17255                  e.preventDefault();
17256                  if(s.hasChildNodes() && s.isExpanded()){
17257                      s.collapse();
17258                  }else if(s.parentNode && (this.tree.rootVisible || s.parentNode != this.tree.getRootNode())){
17259                      this.select(s.parentNode, e);
17260                  }
17261              break;
17262         };
17263     }
17264 });
17265
17266 /**
17267  * @class Roo.tree.MultiSelectionModel
17268  * @extends Roo.util.Observable
17269  * Multi selection for a TreePanel.
17270  * @param {Object} cfg Configuration
17271  */
17272 Roo.tree.MultiSelectionModel = function(){
17273    this.selNodes = [];
17274    this.selMap = {};
17275    this.addEvents({
17276        /**
17277         * @event selectionchange
17278         * Fires when the selected nodes change
17279         * @param {MultiSelectionModel} this
17280         * @param {Array} nodes Array of the selected nodes
17281         */
17282        "selectionchange" : true
17283    });
17284    Roo.tree.MultiSelectionModel.superclass.constructor.call(this,cfg);
17285    
17286 };
17287
17288 Roo.extend(Roo.tree.MultiSelectionModel, Roo.util.Observable, {
17289     init : function(tree){
17290         this.tree = tree;
17291         tree.getTreeEl().on("keydown", this.onKeyDown, this);
17292         tree.on("click", this.onNodeClick, this);
17293     },
17294     
17295     onNodeClick : function(node, e){
17296         this.select(node, e, e.ctrlKey);
17297     },
17298     
17299     /**
17300      * Select a node.
17301      * @param {TreeNode} node The node to select
17302      * @param {EventObject} e (optional) An event associated with the selection
17303      * @param {Boolean} keepExisting True to retain existing selections
17304      * @return {TreeNode} The selected node
17305      */
17306     select : function(node, e, keepExisting){
17307         if(keepExisting !== true){
17308             this.clearSelections(true);
17309         }
17310         if(this.isSelected(node)){
17311             this.lastSelNode = node;
17312             return node;
17313         }
17314         this.selNodes.push(node);
17315         this.selMap[node.id] = node;
17316         this.lastSelNode = node;
17317         node.ui.onSelectedChange(true);
17318         this.fireEvent("selectionchange", this, this.selNodes);
17319         return node;
17320     },
17321     
17322     /**
17323      * Deselect a node.
17324      * @param {TreeNode} node The node to unselect
17325      */
17326     unselect : function(node){
17327         if(this.selMap[node.id]){
17328             node.ui.onSelectedChange(false);
17329             var sn = this.selNodes;
17330             var index = -1;
17331             if(sn.indexOf){
17332                 index = sn.indexOf(node);
17333             }else{
17334                 for(var i = 0, len = sn.length; i < len; i++){
17335                     if(sn[i] == node){
17336                         index = i;
17337                         break;
17338                     }
17339                 }
17340             }
17341             if(index != -1){
17342                 this.selNodes.splice(index, 1);
17343             }
17344             delete this.selMap[node.id];
17345             this.fireEvent("selectionchange", this, this.selNodes);
17346         }
17347     },
17348     
17349     /**
17350      * Clear all selections
17351      */
17352     clearSelections : function(suppressEvent){
17353         var sn = this.selNodes;
17354         if(sn.length > 0){
17355             for(var i = 0, len = sn.length; i < len; i++){
17356                 sn[i].ui.onSelectedChange(false);
17357             }
17358             this.selNodes = [];
17359             this.selMap = {};
17360             if(suppressEvent !== true){
17361                 this.fireEvent("selectionchange", this, this.selNodes);
17362             }
17363         }
17364     },
17365     
17366     /**
17367      * Returns true if the node is selected
17368      * @param {TreeNode} node The node to check
17369      * @return {Boolean}
17370      */
17371     isSelected : function(node){
17372         return this.selMap[node.id] ? true : false;  
17373     },
17374     
17375     /**
17376      * Returns an array of the selected nodes
17377      * @return {Array}
17378      */
17379     getSelectedNodes : function(){
17380         return this.selNodes;    
17381     },
17382
17383     onKeyDown : Roo.tree.DefaultSelectionModel.prototype.onKeyDown,
17384
17385     selectNext : Roo.tree.DefaultSelectionModel.prototype.selectNext,
17386
17387     selectPrevious : Roo.tree.DefaultSelectionModel.prototype.selectPrevious
17388 });/*
17389  * Based on:
17390  * Ext JS Library 1.1.1
17391  * Copyright(c) 2006-2007, Ext JS, LLC.
17392  *
17393  * Originally Released Under LGPL - original licence link has changed is not relivant.
17394  *
17395  * Fork - LGPL
17396  * <script type="text/javascript">
17397  */
17398  
17399 /**
17400  * @class Roo.tree.TreeNode
17401  * @extends Roo.data.Node
17402  * @cfg {String} text The text for this node
17403  * @cfg {Boolean} expanded true to start the node expanded
17404  * @cfg {Boolean} allowDrag false to make this node undraggable if DD is on (defaults to true)
17405  * @cfg {Boolean} allowDrop false if this node cannot be drop on
17406  * @cfg {Boolean} disabled true to start the node disabled
17407  * @cfg {String} icon The path to an icon for the node. The preferred way to do this
17408  * is to use the cls or iconCls attributes and add the icon via a CSS background image.
17409  * @cfg {String} cls A css class to be added to the node
17410  * @cfg {String} iconCls A css class to be added to the nodes icon element for applying css background images
17411  * @cfg {String} href URL of the link used for the node (defaults to #)
17412  * @cfg {String} hrefTarget target frame for the link
17413  * @cfg {String} qtip An Ext QuickTip for the node
17414  * @cfg {String} qtipCfg An Ext QuickTip config for the node (used instead of qtip)
17415  * @cfg {Boolean} singleClickExpand True for single click expand on this node
17416  * @cfg {Function} uiProvider A UI <b>class</b> to use for this node (defaults to Roo.tree.TreeNodeUI)
17417  * @cfg {Boolean} checked True to render a checked checkbox for this node, false to render an unchecked checkbox
17418  * (defaults to undefined with no checkbox rendered)
17419  * @constructor
17420  * @param {Object/String} attributes The attributes/config for the node or just a string with the text for the node
17421  */
17422 Roo.tree.TreeNode = function(attributes){
17423     attributes = attributes || {};
17424     if(typeof attributes == "string"){
17425         attributes = {text: attributes};
17426     }
17427     this.childrenRendered = false;
17428     this.rendered = false;
17429     Roo.tree.TreeNode.superclass.constructor.call(this, attributes);
17430     this.expanded = attributes.expanded === true;
17431     this.isTarget = attributes.isTarget !== false;
17432     this.draggable = attributes.draggable !== false && attributes.allowDrag !== false;
17433     this.allowChildren = attributes.allowChildren !== false && attributes.allowDrop !== false;
17434
17435     /**
17436      * Read-only. The text for this node. To change it use setText().
17437      * @type String
17438      */
17439     this.text = attributes.text;
17440     /**
17441      * True if this node is disabled.
17442      * @type Boolean
17443      */
17444     this.disabled = attributes.disabled === true;
17445
17446     this.addEvents({
17447         /**
17448         * @event textchange
17449         * Fires when the text for this node is changed
17450         * @param {Node} this This node
17451         * @param {String} text The new text
17452         * @param {String} oldText The old text
17453         */
17454         "textchange" : true,
17455         /**
17456         * @event beforeexpand
17457         * Fires before this node is expanded, return false to cancel.
17458         * @param {Node} this This node
17459         * @param {Boolean} deep
17460         * @param {Boolean} anim
17461         */
17462         "beforeexpand" : true,
17463         /**
17464         * @event beforecollapse
17465         * Fires before this node is collapsed, return false to cancel.
17466         * @param {Node} this This node
17467         * @param {Boolean} deep
17468         * @param {Boolean} anim
17469         */
17470         "beforecollapse" : true,
17471         /**
17472         * @event expand
17473         * Fires when this node is expanded
17474         * @param {Node} this This node
17475         */
17476         "expand" : true,
17477         /**
17478         * @event disabledchange
17479         * Fires when the disabled status of this node changes
17480         * @param {Node} this This node
17481         * @param {Boolean} disabled
17482         */
17483         "disabledchange" : true,
17484         /**
17485         * @event collapse
17486         * Fires when this node is collapsed
17487         * @param {Node} this This node
17488         */
17489         "collapse" : true,
17490         /**
17491         * @event beforeclick
17492         * Fires before click processing. Return false to cancel the default action.
17493         * @param {Node} this This node
17494         * @param {Roo.EventObject} e The event object
17495         */
17496         "beforeclick":true,
17497         /**
17498         * @event checkchange
17499         * Fires when a node with a checkbox's checked property changes
17500         * @param {Node} this This node
17501         * @param {Boolean} checked
17502         */
17503         "checkchange":true,
17504         /**
17505         * @event click
17506         * Fires when this node is clicked
17507         * @param {Node} this This node
17508         * @param {Roo.EventObject} e The event object
17509         */
17510         "click":true,
17511         /**
17512         * @event dblclick
17513         * Fires when this node is double clicked
17514         * @param {Node} this This node
17515         * @param {Roo.EventObject} e The event object
17516         */
17517         "dblclick":true,
17518         /**
17519         * @event contextmenu
17520         * Fires when this node is right clicked
17521         * @param {Node} this This node
17522         * @param {Roo.EventObject} e The event object
17523         */
17524         "contextmenu":true,
17525         /**
17526         * @event beforechildrenrendered
17527         * Fires right before the child nodes for this node are rendered
17528         * @param {Node} this This node
17529         */
17530         "beforechildrenrendered":true
17531     });
17532
17533     var uiClass = this.attributes.uiProvider || Roo.tree.TreeNodeUI;
17534
17535     /**
17536      * Read-only. The UI for this node
17537      * @type TreeNodeUI
17538      */
17539     this.ui = new uiClass(this);
17540     
17541     // finally support items[]
17542     if (typeof(this.attributes.items) == 'undefined' || !this.attributes.items) {
17543         return;
17544     }
17545     
17546     
17547     Roo.each(this.attributes.items, function(c) {
17548         this.appendChild(Roo.factory(c,Roo.Tree));
17549     }, this);
17550     delete this.attributes.items;
17551     
17552     
17553     
17554 };
17555 Roo.extend(Roo.tree.TreeNode, Roo.data.Node, {
17556     preventHScroll: true,
17557     /**
17558      * Returns true if this node is expanded
17559      * @return {Boolean}
17560      */
17561     isExpanded : function(){
17562         return this.expanded;
17563     },
17564
17565     /**
17566      * Returns the UI object for this node
17567      * @return {TreeNodeUI}
17568      */
17569     getUI : function(){
17570         return this.ui;
17571     },
17572
17573     // private override
17574     setFirstChild : function(node){
17575         var of = this.firstChild;
17576         Roo.tree.TreeNode.superclass.setFirstChild.call(this, node);
17577         if(this.childrenRendered && of && node != of){
17578             of.renderIndent(true, true);
17579         }
17580         if(this.rendered){
17581             this.renderIndent(true, true);
17582         }
17583     },
17584
17585     // private override
17586     setLastChild : function(node){
17587         var ol = this.lastChild;
17588         Roo.tree.TreeNode.superclass.setLastChild.call(this, node);
17589         if(this.childrenRendered && ol && node != ol){
17590             ol.renderIndent(true, true);
17591         }
17592         if(this.rendered){
17593             this.renderIndent(true, true);
17594         }
17595     },
17596
17597     // these methods are overridden to provide lazy rendering support
17598     // private override
17599     appendChild : function()
17600     {
17601         var node = Roo.tree.TreeNode.superclass.appendChild.apply(this, arguments);
17602         if(node && this.childrenRendered){
17603             node.render();
17604         }
17605         this.ui.updateExpandIcon();
17606         return node;
17607     },
17608
17609     // private override
17610     removeChild : function(node){
17611         this.ownerTree.getSelectionModel().unselect(node);
17612         Roo.tree.TreeNode.superclass.removeChild.apply(this, arguments);
17613         // if it's been rendered remove dom node
17614         if(this.childrenRendered){
17615             node.ui.remove();
17616         }
17617         if(this.childNodes.length < 1){
17618             this.collapse(false, false);
17619         }else{
17620             this.ui.updateExpandIcon();
17621         }
17622         if(!this.firstChild) {
17623             this.childrenRendered = false;
17624         }
17625         return node;
17626     },
17627
17628     // private override
17629     insertBefore : function(node, refNode){
17630         var newNode = Roo.tree.TreeNode.superclass.insertBefore.apply(this, arguments);
17631         if(newNode && refNode && this.childrenRendered){
17632             node.render();
17633         }
17634         this.ui.updateExpandIcon();
17635         return newNode;
17636     },
17637
17638     /**
17639      * Sets the text for this node
17640      * @param {String} text
17641      */
17642     setText : function(text){
17643         var oldText = this.text;
17644         this.text = text;
17645         this.attributes.text = text;
17646         if(this.rendered){ // event without subscribing
17647             this.ui.onTextChange(this, text, oldText);
17648         }
17649         this.fireEvent("textchange", this, text, oldText);
17650     },
17651
17652     /**
17653      * Triggers selection of this node
17654      */
17655     select : function(){
17656         this.getOwnerTree().getSelectionModel().select(this);
17657     },
17658
17659     /**
17660      * Triggers deselection of this node
17661      */
17662     unselect : function(){
17663         this.getOwnerTree().getSelectionModel().unselect(this);
17664     },
17665
17666     /**
17667      * Returns true if this node is selected
17668      * @return {Boolean}
17669      */
17670     isSelected : function(){
17671         return this.getOwnerTree().getSelectionModel().isSelected(this);
17672     },
17673
17674     /**
17675      * Expand this node.
17676      * @param {Boolean} deep (optional) True to expand all children as well
17677      * @param {Boolean} anim (optional) false to cancel the default animation
17678      * @param {Function} callback (optional) A callback to be called when
17679      * expanding this node completes (does not wait for deep expand to complete).
17680      * Called with 1 parameter, this node.
17681      */
17682     expand : function(deep, anim, callback){
17683         if(!this.expanded){
17684             if(this.fireEvent("beforeexpand", this, deep, anim) === false){
17685                 return;
17686             }
17687             if(!this.childrenRendered){
17688                 this.renderChildren();
17689             }
17690             this.expanded = true;
17691             if(!this.isHiddenRoot() && (this.getOwnerTree().animate && anim !== false) || anim){
17692                 this.ui.animExpand(function(){
17693                     this.fireEvent("expand", this);
17694                     if(typeof callback == "function"){
17695                         callback(this);
17696                     }
17697                     if(deep === true){
17698                         this.expandChildNodes(true);
17699                     }
17700                 }.createDelegate(this));
17701                 return;
17702             }else{
17703                 this.ui.expand();
17704                 this.fireEvent("expand", this);
17705                 if(typeof callback == "function"){
17706                     callback(this);
17707                 }
17708             }
17709         }else{
17710            if(typeof callback == "function"){
17711                callback(this);
17712            }
17713         }
17714         if(deep === true){
17715             this.expandChildNodes(true);
17716         }
17717     },
17718
17719     isHiddenRoot : function(){
17720         return this.isRoot && !this.getOwnerTree().rootVisible;
17721     },
17722
17723     /**
17724      * Collapse this node.
17725      * @param {Boolean} deep (optional) True to collapse all children as well
17726      * @param {Boolean} anim (optional) false to cancel the default animation
17727      */
17728     collapse : function(deep, anim){
17729         if(this.expanded && !this.isHiddenRoot()){
17730             if(this.fireEvent("beforecollapse", this, deep, anim) === false){
17731                 return;
17732             }
17733             this.expanded = false;
17734             if((this.getOwnerTree().animate && anim !== false) || anim){
17735                 this.ui.animCollapse(function(){
17736                     this.fireEvent("collapse", this);
17737                     if(deep === true){
17738                         this.collapseChildNodes(true);
17739                     }
17740                 }.createDelegate(this));
17741                 return;
17742             }else{
17743                 this.ui.collapse();
17744                 this.fireEvent("collapse", this);
17745             }
17746         }
17747         if(deep === true){
17748             var cs = this.childNodes;
17749             for(var i = 0, len = cs.length; i < len; i++) {
17750                 cs[i].collapse(true, false);
17751             }
17752         }
17753     },
17754
17755     // private
17756     delayedExpand : function(delay){
17757         if(!this.expandProcId){
17758             this.expandProcId = this.expand.defer(delay, this);
17759         }
17760     },
17761
17762     // private
17763     cancelExpand : function(){
17764         if(this.expandProcId){
17765             clearTimeout(this.expandProcId);
17766         }
17767         this.expandProcId = false;
17768     },
17769
17770     /**
17771      * Toggles expanded/collapsed state of the node
17772      */
17773     toggle : function(){
17774         if(this.expanded){
17775             this.collapse();
17776         }else{
17777             this.expand();
17778         }
17779     },
17780
17781     /**
17782      * Ensures all parent nodes are expanded
17783      */
17784     ensureVisible : function(callback){
17785         var tree = this.getOwnerTree();
17786         tree.expandPath(this.parentNode.getPath(), false, function(){
17787             tree.getTreeEl().scrollChildIntoView(this.ui.anchor);
17788             Roo.callback(callback);
17789         }.createDelegate(this));
17790     },
17791
17792     /**
17793      * Expand all child nodes
17794      * @param {Boolean} deep (optional) true if the child nodes should also expand their child nodes
17795      */
17796     expandChildNodes : function(deep){
17797         var cs = this.childNodes;
17798         for(var i = 0, len = cs.length; i < len; i++) {
17799                 cs[i].expand(deep);
17800         }
17801     },
17802
17803     /**
17804      * Collapse all child nodes
17805      * @param {Boolean} deep (optional) true if the child nodes should also collapse their child nodes
17806      */
17807     collapseChildNodes : function(deep){
17808         var cs = this.childNodes;
17809         for(var i = 0, len = cs.length; i < len; i++) {
17810                 cs[i].collapse(deep);
17811         }
17812     },
17813
17814     /**
17815      * Disables this node
17816      */
17817     disable : function(){
17818         this.disabled = true;
17819         this.unselect();
17820         if(this.rendered && this.ui.onDisableChange){ // event without subscribing
17821             this.ui.onDisableChange(this, true);
17822         }
17823         this.fireEvent("disabledchange", this, true);
17824     },
17825
17826     /**
17827      * Enables this node
17828      */
17829     enable : function(){
17830         this.disabled = false;
17831         if(this.rendered && this.ui.onDisableChange){ // event without subscribing
17832             this.ui.onDisableChange(this, false);
17833         }
17834         this.fireEvent("disabledchange", this, false);
17835     },
17836
17837     // private
17838     renderChildren : function(suppressEvent){
17839         if(suppressEvent !== false){
17840             this.fireEvent("beforechildrenrendered", this);
17841         }
17842         var cs = this.childNodes;
17843         for(var i = 0, len = cs.length; i < len; i++){
17844             cs[i].render(true);
17845         }
17846         this.childrenRendered = true;
17847     },
17848
17849     // private
17850     sort : function(fn, scope){
17851         Roo.tree.TreeNode.superclass.sort.apply(this, arguments);
17852         if(this.childrenRendered){
17853             var cs = this.childNodes;
17854             for(var i = 0, len = cs.length; i < len; i++){
17855                 cs[i].render(true);
17856             }
17857         }
17858     },
17859
17860     // private
17861     render : function(bulkRender){
17862         this.ui.render(bulkRender);
17863         if(!this.rendered){
17864             this.rendered = true;
17865             if(this.expanded){
17866                 this.expanded = false;
17867                 this.expand(false, false);
17868             }
17869         }
17870     },
17871
17872     // private
17873     renderIndent : function(deep, refresh){
17874         if(refresh){
17875             this.ui.childIndent = null;
17876         }
17877         this.ui.renderIndent();
17878         if(deep === true && this.childrenRendered){
17879             var cs = this.childNodes;
17880             for(var i = 0, len = cs.length; i < len; i++){
17881                 cs[i].renderIndent(true, refresh);
17882             }
17883         }
17884     }
17885 });/*
17886  * Based on:
17887  * Ext JS Library 1.1.1
17888  * Copyright(c) 2006-2007, Ext JS, LLC.
17889  *
17890  * Originally Released Under LGPL - original licence link has changed is not relivant.
17891  *
17892  * Fork - LGPL
17893  * <script type="text/javascript">
17894  */
17895  
17896 /**
17897  * @class Roo.tree.AsyncTreeNode
17898  * @extends Roo.tree.TreeNode
17899  * @cfg {TreeLoader} loader A TreeLoader to be used by this node (defaults to the loader defined on the tree)
17900  * @constructor
17901  * @param {Object/String} attributes The attributes/config for the node or just a string with the text for the node 
17902  */
17903  Roo.tree.AsyncTreeNode = function(config){
17904     this.loaded = false;
17905     this.loading = false;
17906     Roo.tree.AsyncTreeNode.superclass.constructor.apply(this, arguments);
17907     /**
17908     * @event beforeload
17909     * Fires before this node is loaded, return false to cancel
17910     * @param {Node} this This node
17911     */
17912     this.addEvents({'beforeload':true, 'load': true});
17913     /**
17914     * @event load
17915     * Fires when this node is loaded
17916     * @param {Node} this This node
17917     */
17918     /**
17919      * The loader used by this node (defaults to using the tree's defined loader)
17920      * @type TreeLoader
17921      * @property loader
17922      */
17923 };
17924 Roo.extend(Roo.tree.AsyncTreeNode, Roo.tree.TreeNode, {
17925     expand : function(deep, anim, callback){
17926         if(this.loading){ // if an async load is already running, waiting til it's done
17927             var timer;
17928             var f = function(){
17929                 if(!this.loading){ // done loading
17930                     clearInterval(timer);
17931                     this.expand(deep, anim, callback);
17932                 }
17933             }.createDelegate(this);
17934             timer = setInterval(f, 200);
17935             return;
17936         }
17937         if(!this.loaded){
17938             if(this.fireEvent("beforeload", this) === false){
17939                 return;
17940             }
17941             this.loading = true;
17942             this.ui.beforeLoad(this);
17943             var loader = this.loader || this.attributes.loader || this.getOwnerTree().getLoader();
17944             if(loader){
17945                 loader.load(this, this.loadComplete.createDelegate(this, [deep, anim, callback]));
17946                 return;
17947             }
17948         }
17949         Roo.tree.AsyncTreeNode.superclass.expand.call(this, deep, anim, callback);
17950     },
17951     
17952     /**
17953      * Returns true if this node is currently loading
17954      * @return {Boolean}
17955      */
17956     isLoading : function(){
17957         return this.loading;  
17958     },
17959     
17960     loadComplete : function(deep, anim, callback){
17961         this.loading = false;
17962         this.loaded = true;
17963         this.ui.afterLoad(this);
17964         this.fireEvent("load", this);
17965         this.expand(deep, anim, callback);
17966     },
17967     
17968     /**
17969      * Returns true if this node has been loaded
17970      * @return {Boolean}
17971      */
17972     isLoaded : function(){
17973         return this.loaded;
17974     },
17975     
17976     hasChildNodes : function(){
17977         if(!this.isLeaf() && !this.loaded){
17978             return true;
17979         }else{
17980             return Roo.tree.AsyncTreeNode.superclass.hasChildNodes.call(this);
17981         }
17982     },
17983
17984     /**
17985      * Trigger a reload for this node
17986      * @param {Function} callback
17987      */
17988     reload : function(callback){
17989         this.collapse(false, false);
17990         while(this.firstChild){
17991             this.removeChild(this.firstChild);
17992         }
17993         this.childrenRendered = false;
17994         this.loaded = false;
17995         if(this.isHiddenRoot()){
17996             this.expanded = false;
17997         }
17998         this.expand(false, false, callback);
17999     }
18000 });/*
18001  * Based on:
18002  * Ext JS Library 1.1.1
18003  * Copyright(c) 2006-2007, Ext JS, LLC.
18004  *
18005  * Originally Released Under LGPL - original licence link has changed is not relivant.
18006  *
18007  * Fork - LGPL
18008  * <script type="text/javascript">
18009  */
18010  
18011 /**
18012  * @class Roo.tree.TreeNodeUI
18013  * @constructor
18014  * @param {Object} node The node to render
18015  * The TreeNode UI implementation is separate from the
18016  * tree implementation. Unless you are customizing the tree UI,
18017  * you should never have to use this directly.
18018  */
18019 Roo.tree.TreeNodeUI = function(node){
18020     this.node = node;
18021     this.rendered = false;
18022     this.animating = false;
18023     this.emptyIcon = Roo.BLANK_IMAGE_URL;
18024 };
18025
18026 Roo.tree.TreeNodeUI.prototype = {
18027     removeChild : function(node){
18028         if(this.rendered){
18029             this.ctNode.removeChild(node.ui.getEl());
18030         }
18031     },
18032
18033     beforeLoad : function(){
18034          this.addClass("x-tree-node-loading");
18035     },
18036
18037     afterLoad : function(){
18038          this.removeClass("x-tree-node-loading");
18039     },
18040
18041     onTextChange : function(node, text, oldText){
18042         if(this.rendered){
18043             this.textNode.innerHTML = text;
18044         }
18045     },
18046
18047     onDisableChange : function(node, state){
18048         this.disabled = state;
18049         if(state){
18050             this.addClass("x-tree-node-disabled");
18051         }else{
18052             this.removeClass("x-tree-node-disabled");
18053         }
18054     },
18055
18056     onSelectedChange : function(state){
18057         if(state){
18058             this.focus();
18059             this.addClass("x-tree-selected");
18060         }else{
18061             //this.blur();
18062             this.removeClass("x-tree-selected");
18063         }
18064     },
18065
18066     onMove : function(tree, node, oldParent, newParent, index, refNode){
18067         this.childIndent = null;
18068         if(this.rendered){
18069             var targetNode = newParent.ui.getContainer();
18070             if(!targetNode){//target not rendered
18071                 this.holder = document.createElement("div");
18072                 this.holder.appendChild(this.wrap);
18073                 return;
18074             }
18075             var insertBefore = refNode ? refNode.ui.getEl() : null;
18076             if(insertBefore){
18077                 targetNode.insertBefore(this.wrap, insertBefore);
18078             }else{
18079                 targetNode.appendChild(this.wrap);
18080             }
18081             this.node.renderIndent(true);
18082         }
18083     },
18084
18085     addClass : function(cls){
18086         if(this.elNode){
18087             Roo.fly(this.elNode).addClass(cls);
18088         }
18089     },
18090
18091     removeClass : function(cls){
18092         if(this.elNode){
18093             Roo.fly(this.elNode).removeClass(cls);
18094         }
18095     },
18096
18097     remove : function(){
18098         if(this.rendered){
18099             this.holder = document.createElement("div");
18100             this.holder.appendChild(this.wrap);
18101         }
18102     },
18103
18104     fireEvent : function(){
18105         return this.node.fireEvent.apply(this.node, arguments);
18106     },
18107
18108     initEvents : function(){
18109         this.node.on("move", this.onMove, this);
18110         var E = Roo.EventManager;
18111         var a = this.anchor;
18112
18113         var el = Roo.fly(a, '_treeui');
18114
18115         if(Roo.isOpera){ // opera render bug ignores the CSS
18116             el.setStyle("text-decoration", "none");
18117         }
18118
18119         el.on("click", this.onClick, this);
18120         el.on("dblclick", this.onDblClick, this);
18121
18122         if(this.checkbox){
18123             Roo.EventManager.on(this.checkbox,
18124                     Roo.isIE ? 'click' : 'change', this.onCheckChange, this);
18125         }
18126
18127         el.on("contextmenu", this.onContextMenu, this);
18128
18129         var icon = Roo.fly(this.iconNode);
18130         icon.on("click", this.onClick, this);
18131         icon.on("dblclick", this.onDblClick, this);
18132         icon.on("contextmenu", this.onContextMenu, this);
18133         E.on(this.ecNode, "click", this.ecClick, this, true);
18134
18135         if(this.node.disabled){
18136             this.addClass("x-tree-node-disabled");
18137         }
18138         if(this.node.hidden){
18139             this.addClass("x-tree-node-disabled");
18140         }
18141         var ot = this.node.getOwnerTree();
18142         var dd = ot.enableDD || ot.enableDrag || ot.enableDrop;
18143         if(dd && (!this.node.isRoot || ot.rootVisible)){
18144             Roo.dd.Registry.register(this.elNode, {
18145                 node: this.node,
18146                 handles: this.getDDHandles(),
18147                 isHandle: false
18148             });
18149         }
18150     },
18151
18152     getDDHandles : function(){
18153         return [this.iconNode, this.textNode];
18154     },
18155
18156     hide : function(){
18157         if(this.rendered){
18158             this.wrap.style.display = "none";
18159         }
18160     },
18161
18162     show : function(){
18163         if(this.rendered){
18164             this.wrap.style.display = "";
18165         }
18166     },
18167
18168     onContextMenu : function(e){
18169         if (this.node.hasListener("contextmenu") || this.node.getOwnerTree().hasListener("contextmenu")) {
18170             e.preventDefault();
18171             this.focus();
18172             this.fireEvent("contextmenu", this.node, e);
18173         }
18174     },
18175
18176     onClick : function(e){
18177         if(this.dropping){
18178             e.stopEvent();
18179             return;
18180         }
18181         if(this.fireEvent("beforeclick", this.node, e) !== false){
18182             if(!this.disabled && this.node.attributes.href){
18183                 this.fireEvent("click", this.node, e);
18184                 return;
18185             }
18186             e.preventDefault();
18187             if(this.disabled){
18188                 return;
18189             }
18190
18191             if(this.node.attributes.singleClickExpand && !this.animating && this.node.hasChildNodes()){
18192                 this.node.toggle();
18193             }
18194
18195             this.fireEvent("click", this.node, e);
18196         }else{
18197             e.stopEvent();
18198         }
18199     },
18200
18201     onDblClick : function(e){
18202         e.preventDefault();
18203         if(this.disabled){
18204             return;
18205         }
18206         if(this.checkbox){
18207             this.toggleCheck();
18208         }
18209         if(!this.animating && this.node.hasChildNodes()){
18210             this.node.toggle();
18211         }
18212         this.fireEvent("dblclick", this.node, e);
18213     },
18214
18215     onCheckChange : function(){
18216         var checked = this.checkbox.checked;
18217         this.node.attributes.checked = checked;
18218         this.fireEvent('checkchange', this.node, checked);
18219     },
18220
18221     ecClick : function(e){
18222         if(!this.animating && this.node.hasChildNodes()){
18223             this.node.toggle();
18224         }
18225     },
18226
18227     startDrop : function(){
18228         this.dropping = true;
18229     },
18230
18231     // delayed drop so the click event doesn't get fired on a drop
18232     endDrop : function(){
18233        setTimeout(function(){
18234            this.dropping = false;
18235        }.createDelegate(this), 50);
18236     },
18237
18238     expand : function(){
18239         this.updateExpandIcon();
18240         this.ctNode.style.display = "";
18241     },
18242
18243     focus : function(){
18244         if(!this.node.preventHScroll){
18245             try{this.anchor.focus();
18246             }catch(e){}
18247         }else if(!Roo.isIE){
18248             try{
18249                 var noscroll = this.node.getOwnerTree().getTreeEl().dom;
18250                 var l = noscroll.scrollLeft;
18251                 this.anchor.focus();
18252                 noscroll.scrollLeft = l;
18253             }catch(e){}
18254         }
18255     },
18256
18257     toggleCheck : function(value){
18258         var cb = this.checkbox;
18259         if(cb){
18260             cb.checked = (value === undefined ? !cb.checked : value);
18261         }
18262     },
18263
18264     blur : function(){
18265         try{
18266             this.anchor.blur();
18267         }catch(e){}
18268     },
18269
18270     animExpand : function(callback){
18271         var ct = Roo.get(this.ctNode);
18272         ct.stopFx();
18273         if(!this.node.hasChildNodes()){
18274             this.updateExpandIcon();
18275             this.ctNode.style.display = "";
18276             Roo.callback(callback);
18277             return;
18278         }
18279         this.animating = true;
18280         this.updateExpandIcon();
18281
18282         ct.slideIn('t', {
18283            callback : function(){
18284                this.animating = false;
18285                Roo.callback(callback);
18286             },
18287             scope: this,
18288             duration: this.node.ownerTree.duration || .25
18289         });
18290     },
18291
18292     highlight : function(){
18293         var tree = this.node.getOwnerTree();
18294         Roo.fly(this.wrap).highlight(
18295             tree.hlColor || "C3DAF9",
18296             {endColor: tree.hlBaseColor}
18297         );
18298     },
18299
18300     collapse : function(){
18301         this.updateExpandIcon();
18302         this.ctNode.style.display = "none";
18303     },
18304
18305     animCollapse : function(callback){
18306         var ct = Roo.get(this.ctNode);
18307         ct.enableDisplayMode('block');
18308         ct.stopFx();
18309
18310         this.animating = true;
18311         this.updateExpandIcon();
18312
18313         ct.slideOut('t', {
18314             callback : function(){
18315                this.animating = false;
18316                Roo.callback(callback);
18317             },
18318             scope: this,
18319             duration: this.node.ownerTree.duration || .25
18320         });
18321     },
18322
18323     getContainer : function(){
18324         return this.ctNode;
18325     },
18326
18327     getEl : function(){
18328         return this.wrap;
18329     },
18330
18331     appendDDGhost : function(ghostNode){
18332         ghostNode.appendChild(this.elNode.cloneNode(true));
18333     },
18334
18335     getDDRepairXY : function(){
18336         return Roo.lib.Dom.getXY(this.iconNode);
18337     },
18338
18339     onRender : function(){
18340         this.render();
18341     },
18342
18343     render : function(bulkRender){
18344         var n = this.node, a = n.attributes;
18345         var targetNode = n.parentNode ?
18346               n.parentNode.ui.getContainer() : n.ownerTree.innerCt.dom;
18347
18348         if(!this.rendered){
18349             this.rendered = true;
18350
18351             this.renderElements(n, a, targetNode, bulkRender);
18352
18353             if(a.qtip){
18354                if(this.textNode.setAttributeNS){
18355                    this.textNode.setAttributeNS("ext", "qtip", a.qtip);
18356                    if(a.qtipTitle){
18357                        this.textNode.setAttributeNS("ext", "qtitle", a.qtipTitle);
18358                    }
18359                }else{
18360                    this.textNode.setAttribute("ext:qtip", a.qtip);
18361                    if(a.qtipTitle){
18362                        this.textNode.setAttribute("ext:qtitle", a.qtipTitle);
18363                    }
18364                }
18365             }else if(a.qtipCfg){
18366                 a.qtipCfg.target = Roo.id(this.textNode);
18367                 Roo.QuickTips.register(a.qtipCfg);
18368             }
18369             this.initEvents();
18370             if(!this.node.expanded){
18371                 this.updateExpandIcon();
18372             }
18373         }else{
18374             if(bulkRender === true) {
18375                 targetNode.appendChild(this.wrap);
18376             }
18377         }
18378     },
18379
18380     renderElements : function(n, a, targetNode, bulkRender)
18381     {
18382         // add some indent caching, this helps performance when rendering a large tree
18383         this.indentMarkup = n.parentNode ? n.parentNode.ui.getChildIndent() : '';
18384         var t = n.getOwnerTree();
18385         var txt = t.renderer ? t.renderer(n.attributes) : Roo.util.Format.htmlEncode(n.text);
18386         if (typeof(n.attributes.html) != 'undefined') {
18387             txt = n.attributes.html;
18388         }
18389         var tip = t.rendererTip ? t.rendererTip(n.attributes) : txt;
18390         var cb = typeof a.checked == 'boolean';
18391         var href = a.href ? a.href : Roo.isGecko ? "" : "#";
18392         var buf = ['<li class="x-tree-node"><div class="x-tree-node-el ', a.cls,'">',
18393             '<span class="x-tree-node-indent">',this.indentMarkup,"</span>",
18394             '<img src="', this.emptyIcon, '" class="x-tree-ec-icon" />',
18395             '<img src="', a.icon || this.emptyIcon, '" class="x-tree-node-icon',(a.icon ? " x-tree-node-inline-icon" : ""),(a.iconCls ? " "+a.iconCls : ""),'" unselectable="on" />',
18396             cb ? ('<input class="x-tree-node-cb" type="checkbox" ' + (a.checked ? 'checked="checked" />' : ' />')) : '',
18397             '<a hidefocus="on" href="',href,'" tabIndex="1" ',
18398              a.hrefTarget ? ' target="'+a.hrefTarget+'"' : "", 
18399                 '><span unselectable="on" qtip="' , tip ,'">',txt,"</span></a></div>",
18400             '<ul class="x-tree-node-ct" style="display:none;"></ul>',
18401             "</li>"];
18402
18403         if(bulkRender !== true && n.nextSibling && n.nextSibling.ui.getEl()){
18404             this.wrap = Roo.DomHelper.insertHtml("beforeBegin",
18405                                 n.nextSibling.ui.getEl(), buf.join(""));
18406         }else{
18407             this.wrap = Roo.DomHelper.insertHtml("beforeEnd", targetNode, buf.join(""));
18408         }
18409
18410         this.elNode = this.wrap.childNodes[0];
18411         this.ctNode = this.wrap.childNodes[1];
18412         var cs = this.elNode.childNodes;
18413         this.indentNode = cs[0];
18414         this.ecNode = cs[1];
18415         this.iconNode = cs[2];
18416         var index = 3;
18417         if(cb){
18418             this.checkbox = cs[3];
18419             index++;
18420         }
18421         this.anchor = cs[index];
18422         this.textNode = cs[index].firstChild;
18423     },
18424
18425     getAnchor : function(){
18426         return this.anchor;
18427     },
18428
18429     getTextEl : function(){
18430         return this.textNode;
18431     },
18432
18433     getIconEl : function(){
18434         return this.iconNode;
18435     },
18436
18437     isChecked : function(){
18438         return this.checkbox ? this.checkbox.checked : false;
18439     },
18440
18441     updateExpandIcon : function(){
18442         if(this.rendered){
18443             var n = this.node, c1, c2;
18444             var cls = n.isLast() ? "x-tree-elbow-end" : "x-tree-elbow";
18445             var hasChild = n.hasChildNodes();
18446             if(hasChild){
18447                 if(n.expanded){
18448                     cls += "-minus";
18449                     c1 = "x-tree-node-collapsed";
18450                     c2 = "x-tree-node-expanded";
18451                 }else{
18452                     cls += "-plus";
18453                     c1 = "x-tree-node-expanded";
18454                     c2 = "x-tree-node-collapsed";
18455                 }
18456                 if(this.wasLeaf){
18457                     this.removeClass("x-tree-node-leaf");
18458                     this.wasLeaf = false;
18459                 }
18460                 if(this.c1 != c1 || this.c2 != c2){
18461                     Roo.fly(this.elNode).replaceClass(c1, c2);
18462                     this.c1 = c1; this.c2 = c2;
18463                 }
18464             }else{
18465                 // this changes non-leafs into leafs if they have no children.
18466                 // it's not very rational behaviour..
18467                 
18468                 if(!this.wasLeaf && this.node.leaf){
18469                     Roo.fly(this.elNode).replaceClass("x-tree-node-expanded", "x-tree-node-leaf");
18470                     delete this.c1;
18471                     delete this.c2;
18472                     this.wasLeaf = true;
18473                 }
18474             }
18475             var ecc = "x-tree-ec-icon "+cls;
18476             if(this.ecc != ecc){
18477                 this.ecNode.className = ecc;
18478                 this.ecc = ecc;
18479             }
18480         }
18481     },
18482
18483     getChildIndent : function(){
18484         if(!this.childIndent){
18485             var buf = [];
18486             var p = this.node;
18487             while(p){
18488                 if(!p.isRoot || (p.isRoot && p.ownerTree.rootVisible)){
18489                     if(!p.isLast()) {
18490                         buf.unshift('<img src="'+this.emptyIcon+'" class="x-tree-elbow-line" />');
18491                     } else {
18492                         buf.unshift('<img src="'+this.emptyIcon+'" class="x-tree-icon" />');
18493                     }
18494                 }
18495                 p = p.parentNode;
18496             }
18497             this.childIndent = buf.join("");
18498         }
18499         return this.childIndent;
18500     },
18501
18502     renderIndent : function(){
18503         if(this.rendered){
18504             var indent = "";
18505             var p = this.node.parentNode;
18506             if(p){
18507                 indent = p.ui.getChildIndent();
18508             }
18509             if(this.indentMarkup != indent){ // don't rerender if not required
18510                 this.indentNode.innerHTML = indent;
18511                 this.indentMarkup = indent;
18512             }
18513             this.updateExpandIcon();
18514         }
18515     }
18516 };
18517
18518 Roo.tree.RootTreeNodeUI = function(){
18519     Roo.tree.RootTreeNodeUI.superclass.constructor.apply(this, arguments);
18520 };
18521 Roo.extend(Roo.tree.RootTreeNodeUI, Roo.tree.TreeNodeUI, {
18522     render : function(){
18523         if(!this.rendered){
18524             var targetNode = this.node.ownerTree.innerCt.dom;
18525             this.node.expanded = true;
18526             targetNode.innerHTML = '<div class="x-tree-root-node"></div>';
18527             this.wrap = this.ctNode = targetNode.firstChild;
18528         }
18529     },
18530     collapse : function(){
18531     },
18532     expand : function(){
18533     }
18534 });/*
18535  * Based on:
18536  * Ext JS Library 1.1.1
18537  * Copyright(c) 2006-2007, Ext JS, LLC.
18538  *
18539  * Originally Released Under LGPL - original licence link has changed is not relivant.
18540  *
18541  * Fork - LGPL
18542  * <script type="text/javascript">
18543  */
18544 /**
18545  * @class Roo.tree.TreeLoader
18546  * @extends Roo.util.Observable
18547  * A TreeLoader provides for lazy loading of an {@link Roo.tree.TreeNode}'s child
18548  * nodes from a specified URL. The response must be a javascript Array definition
18549  * who's elements are node definition objects. eg:
18550  * <pre><code>
18551 {  success : true,
18552    data :      [
18553    
18554     { 'id': 1, 'text': 'A folder Node', 'leaf': false },
18555     { 'id': 2, 'text': 'A leaf Node', 'leaf': true }
18556     ]
18557 }
18558
18559
18560 </code></pre>
18561  * <br><br>
18562  * The old style respose with just an array is still supported, but not recommended.
18563  * <br><br>
18564  *
18565  * A server request is sent, and child nodes are loaded only when a node is expanded.
18566  * The loading node's id is passed to the server under the parameter name "node" to
18567  * enable the server to produce the correct child nodes.
18568  * <br><br>
18569  * To pass extra parameters, an event handler may be attached to the "beforeload"
18570  * event, and the parameters specified in the TreeLoader's baseParams property:
18571  * <pre><code>
18572     myTreeLoader.on("beforeload", function(treeLoader, node) {
18573         this.baseParams.category = node.attributes.category;
18574     }, this);
18575 </code></pre><
18576  * This would pass an HTTP parameter called "category" to the server containing
18577  * the value of the Node's "category" attribute.
18578  * @constructor
18579  * Creates a new Treeloader.
18580  * @param {Object} config A config object containing config properties.
18581  */
18582 Roo.tree.TreeLoader = function(config){
18583     this.baseParams = {};
18584     this.requestMethod = "POST";
18585     Roo.apply(this, config);
18586
18587     this.addEvents({
18588     
18589         /**
18590          * @event beforeload
18591          * Fires before a network request is made to retrieve the Json text which specifies a node's children.
18592          * @param {Object} This TreeLoader object.
18593          * @param {Object} node The {@link Roo.tree.TreeNode} object being loaded.
18594          * @param {Object} callback The callback function specified in the {@link #load} call.
18595          */
18596         beforeload : true,
18597         /**
18598          * @event load
18599          * Fires when the node has been successfuly loaded.
18600          * @param {Object} This TreeLoader object.
18601          * @param {Object} node The {@link Roo.tree.TreeNode} object being loaded.
18602          * @param {Object} response The response object containing the data from the server.
18603          */
18604         load : true,
18605         /**
18606          * @event loadexception
18607          * Fires if the network request failed.
18608          * @param {Object} This TreeLoader object.
18609          * @param {Object} node The {@link Roo.tree.TreeNode} object being loaded.
18610          * @param {Object} response The response object containing the data from the server.
18611          */
18612         loadexception : true,
18613         /**
18614          * @event create
18615          * Fires before a node is created, enabling you to return custom Node types 
18616          * @param {Object} This TreeLoader object.
18617          * @param {Object} attr - the data returned from the AJAX call (modify it to suit)
18618          */
18619         create : true
18620     });
18621
18622     Roo.tree.TreeLoader.superclass.constructor.call(this);
18623 };
18624
18625 Roo.extend(Roo.tree.TreeLoader, Roo.util.Observable, {
18626     /**
18627     * @cfg {String} dataUrl The URL from which to request a Json string which
18628     * specifies an array of node definition object representing the child nodes
18629     * to be loaded.
18630     */
18631     /**
18632     * @cfg {String} requestMethod either GET or POST
18633     * defaults to POST (due to BC)
18634     * to be loaded.
18635     */
18636     /**
18637     * @cfg {Object} baseParams (optional) An object containing properties which
18638     * specify HTTP parameters to be passed to each request for child nodes.
18639     */
18640     /**
18641     * @cfg {Object} baseAttrs (optional) An object containing attributes to be added to all nodes
18642     * created by this loader. If the attributes sent by the server have an attribute in this object,
18643     * they take priority.
18644     */
18645     /**
18646     * @cfg {Object} uiProviders (optional) An object containing properties which
18647     * 
18648     * DEPRECATED - use 'create' event handler to modify attributes - which affect creation.
18649     * specify custom {@link Roo.tree.TreeNodeUI} implementations. If the optional
18650     * <i>uiProvider</i> attribute of a returned child node is a string rather
18651     * than a reference to a TreeNodeUI implementation, this that string value
18652     * is used as a property name in the uiProviders object. You can define the provider named
18653     * 'default' , and this will be used for all nodes (if no uiProvider is delivered by the node data)
18654     */
18655     uiProviders : {},
18656
18657     /**
18658     * @cfg {Boolean} clearOnLoad (optional) Default to true. Remove previously existing
18659     * child nodes before loading.
18660     */
18661     clearOnLoad : true,
18662
18663     /**
18664     * @cfg {String} root (optional) Default to false. Use this to read data from an object 
18665     * property on loading, rather than expecting an array. (eg. more compatible to a standard
18666     * Grid query { data : [ .....] }
18667     */
18668     
18669     root : false,
18670      /**
18671     * @cfg {String} queryParam (optional) 
18672     * Name of the query as it will be passed on the querystring (defaults to 'node')
18673     * eg. the request will be ?node=[id]
18674     */
18675     
18676     
18677     queryParam: false,
18678     
18679     /**
18680      * Load an {@link Roo.tree.TreeNode} from the URL specified in the constructor.
18681      * This is called automatically when a node is expanded, but may be used to reload
18682      * a node (or append new children if the {@link #clearOnLoad} option is false.)
18683      * @param {Roo.tree.TreeNode} node
18684      * @param {Function} callback
18685      */
18686     load : function(node, callback){
18687         if(this.clearOnLoad){
18688             while(node.firstChild){
18689                 node.removeChild(node.firstChild);
18690             }
18691         }
18692         if(node.attributes.children){ // preloaded json children
18693             var cs = node.attributes.children;
18694             for(var i = 0, len = cs.length; i < len; i++){
18695                 node.appendChild(this.createNode(cs[i]));
18696             }
18697             if(typeof callback == "function"){
18698                 callback();
18699             }
18700         }else if(this.dataUrl){
18701             this.requestData(node, callback);
18702         }
18703     },
18704
18705     getParams: function(node){
18706         var buf = [], bp = this.baseParams;
18707         for(var key in bp){
18708             if(typeof bp[key] != "function"){
18709                 buf.push(encodeURIComponent(key), "=", encodeURIComponent(bp[key]), "&");
18710             }
18711         }
18712         var n = this.queryParam === false ? 'node' : this.queryParam;
18713         buf.push(n + "=", encodeURIComponent(node.id));
18714         return buf.join("");
18715     },
18716
18717     requestData : function(node, callback){
18718         if(this.fireEvent("beforeload", this, node, callback) !== false){
18719             this.transId = Roo.Ajax.request({
18720                 method:this.requestMethod,
18721                 url: this.dataUrl||this.url,
18722                 success: this.handleResponse,
18723                 failure: this.handleFailure,
18724                 scope: this,
18725                 argument: {callback: callback, node: node},
18726                 params: this.getParams(node)
18727             });
18728         }else{
18729             // if the load is cancelled, make sure we notify
18730             // the node that we are done
18731             if(typeof callback == "function"){
18732                 callback();
18733             }
18734         }
18735     },
18736
18737     isLoading : function(){
18738         return this.transId ? true : false;
18739     },
18740
18741     abort : function(){
18742         if(this.isLoading()){
18743             Roo.Ajax.abort(this.transId);
18744         }
18745     },
18746
18747     // private
18748     createNode : function(attr)
18749     {
18750         // apply baseAttrs, nice idea Corey!
18751         if(this.baseAttrs){
18752             Roo.applyIf(attr, this.baseAttrs);
18753         }
18754         if(this.applyLoader !== false){
18755             attr.loader = this;
18756         }
18757         // uiProvider = depreciated..
18758         
18759         if(typeof(attr.uiProvider) == 'string'){
18760            attr.uiProvider = this.uiProviders[attr.uiProvider] || 
18761                 /**  eval:var:attr */ eval(attr.uiProvider);
18762         }
18763         if(typeof(this.uiProviders['default']) != 'undefined') {
18764             attr.uiProvider = this.uiProviders['default'];
18765         }
18766         
18767         this.fireEvent('create', this, attr);
18768         
18769         attr.leaf  = typeof(attr.leaf) == 'string' ? attr.leaf * 1 : attr.leaf;
18770         return(attr.leaf ?
18771                         new Roo.tree.TreeNode(attr) :
18772                         new Roo.tree.AsyncTreeNode(attr));
18773     },
18774
18775     processResponse : function(response, node, callback)
18776     {
18777         var json = response.responseText;
18778         try {
18779             
18780             var o = Roo.decode(json);
18781             
18782             if (this.root === false && typeof(o.success) != undefined) {
18783                 this.root = 'data'; // the default behaviour for list like data..
18784                 }
18785                 
18786             if (this.root !== false &&  !o.success) {
18787                 // it's a failure condition.
18788                 var a = response.argument;
18789                 this.fireEvent("loadexception", this, a.node, response);
18790                 Roo.log("Load failed - should have a handler really");
18791                 return;
18792             }
18793             
18794             
18795             
18796             if (this.root !== false) {
18797                  o = o[this.root];
18798             }
18799             
18800             for(var i = 0, len = o.length; i < len; i++){
18801                 var n = this.createNode(o[i]);
18802                 if(n){
18803                     node.appendChild(n);
18804                 }
18805             }
18806             if(typeof callback == "function"){
18807                 callback(this, node);
18808             }
18809         }catch(e){
18810             this.handleFailure(response);
18811         }
18812     },
18813
18814     handleResponse : function(response){
18815         this.transId = false;
18816         var a = response.argument;
18817         this.processResponse(response, a.node, a.callback);
18818         this.fireEvent("load", this, a.node, response);
18819     },
18820
18821     handleFailure : function(response)
18822     {
18823         // should handle failure better..
18824         this.transId = false;
18825         var a = response.argument;
18826         this.fireEvent("loadexception", this, a.node, response);
18827         if(typeof a.callback == "function"){
18828             a.callback(this, a.node);
18829         }
18830     }
18831 });/*
18832  * Based on:
18833  * Ext JS Library 1.1.1
18834  * Copyright(c) 2006-2007, Ext JS, LLC.
18835  *
18836  * Originally Released Under LGPL - original licence link has changed is not relivant.
18837  *
18838  * Fork - LGPL
18839  * <script type="text/javascript">
18840  */
18841
18842 /**
18843 * @class Roo.tree.TreeFilter
18844 * Note this class is experimental and doesn't update the indent (lines) or expand collapse icons of the nodes
18845 * @param {TreePanel} tree
18846 * @param {Object} config (optional)
18847  */
18848 Roo.tree.TreeFilter = function(tree, config){
18849     this.tree = tree;
18850     this.filtered = {};
18851     Roo.apply(this, config);
18852 };
18853
18854 Roo.tree.TreeFilter.prototype = {
18855     clearBlank:false,
18856     reverse:false,
18857     autoClear:false,
18858     remove:false,
18859
18860      /**
18861      * Filter the data by a specific attribute.
18862      * @param {String/RegExp} value Either string that the attribute value
18863      * should start with or a RegExp to test against the attribute
18864      * @param {String} attr (optional) The attribute passed in your node's attributes collection. Defaults to "text".
18865      * @param {TreeNode} startNode (optional) The node to start the filter at.
18866      */
18867     filter : function(value, attr, startNode){
18868         attr = attr || "text";
18869         var f;
18870         if(typeof value == "string"){
18871             var vlen = value.length;
18872             // auto clear empty filter
18873             if(vlen == 0 && this.clearBlank){
18874                 this.clear();
18875                 return;
18876             }
18877             value = value.toLowerCase();
18878             f = function(n){
18879                 return n.attributes[attr].substr(0, vlen).toLowerCase() == value;
18880             };
18881         }else if(value.exec){ // regex?
18882             f = function(n){
18883                 return value.test(n.attributes[attr]);
18884             };
18885         }else{
18886             throw 'Illegal filter type, must be string or regex';
18887         }
18888         this.filterBy(f, null, startNode);
18889         },
18890
18891     /**
18892      * Filter by a function. The passed function will be called with each
18893      * node in the tree (or from the startNode). If the function returns true, the node is kept
18894      * otherwise it is filtered. If a node is filtered, its children are also filtered.
18895      * @param {Function} fn The filter function
18896      * @param {Object} scope (optional) The scope of the function (defaults to the current node)
18897      */
18898     filterBy : function(fn, scope, startNode){
18899         startNode = startNode || this.tree.root;
18900         if(this.autoClear){
18901             this.clear();
18902         }
18903         var af = this.filtered, rv = this.reverse;
18904         var f = function(n){
18905             if(n == startNode){
18906                 return true;
18907             }
18908             if(af[n.id]){
18909                 return false;
18910             }
18911             var m = fn.call(scope || n, n);
18912             if(!m || rv){
18913                 af[n.id] = n;
18914                 n.ui.hide();
18915                 return false;
18916             }
18917             return true;
18918         };
18919         startNode.cascade(f);
18920         if(this.remove){
18921            for(var id in af){
18922                if(typeof id != "function"){
18923                    var n = af[id];
18924                    if(n && n.parentNode){
18925                        n.parentNode.removeChild(n);
18926                    }
18927                }
18928            }
18929         }
18930     },
18931
18932     /**
18933      * Clears the current filter. Note: with the "remove" option
18934      * set a filter cannot be cleared.
18935      */
18936     clear : function(){
18937         var t = this.tree;
18938         var af = this.filtered;
18939         for(var id in af){
18940             if(typeof id != "function"){
18941                 var n = af[id];
18942                 if(n){
18943                     n.ui.show();
18944                 }
18945             }
18946         }
18947         this.filtered = {};
18948     }
18949 };
18950 /*
18951  * Based on:
18952  * Ext JS Library 1.1.1
18953  * Copyright(c) 2006-2007, Ext JS, LLC.
18954  *
18955  * Originally Released Under LGPL - original licence link has changed is not relivant.
18956  *
18957  * Fork - LGPL
18958  * <script type="text/javascript">
18959  */
18960  
18961
18962 /**
18963  * @class Roo.tree.TreeSorter
18964  * Provides sorting of nodes in a TreePanel
18965  * 
18966  * @cfg {Boolean} folderSort True to sort leaf nodes under non leaf nodes
18967  * @cfg {String} property The named attribute on the node to sort by (defaults to text)
18968  * @cfg {String} dir The direction to sort (asc or desc) (defaults to asc)
18969  * @cfg {String} leafAttr The attribute used to determine leaf nodes in folder sort (defaults to "leaf")
18970  * @cfg {Boolean} caseSensitive true for case sensitive sort (defaults to false)
18971  * @cfg {Function} sortType A custom "casting" function used to convert node values before sorting
18972  * @constructor
18973  * @param {TreePanel} tree
18974  * @param {Object} config
18975  */
18976 Roo.tree.TreeSorter = function(tree, config){
18977     Roo.apply(this, config);
18978     tree.on("beforechildrenrendered", this.doSort, this);
18979     tree.on("append", this.updateSort, this);
18980     tree.on("insert", this.updateSort, this);
18981     
18982     var dsc = this.dir && this.dir.toLowerCase() == "desc";
18983     var p = this.property || "text";
18984     var sortType = this.sortType;
18985     var fs = this.folderSort;
18986     var cs = this.caseSensitive === true;
18987     var leafAttr = this.leafAttr || 'leaf';
18988
18989     this.sortFn = function(n1, n2){
18990         if(fs){
18991             if(n1.attributes[leafAttr] && !n2.attributes[leafAttr]){
18992                 return 1;
18993             }
18994             if(!n1.attributes[leafAttr] && n2.attributes[leafAttr]){
18995                 return -1;
18996             }
18997         }
18998         var v1 = sortType ? sortType(n1) : (cs ? n1.attributes[p] : n1.attributes[p].toUpperCase());
18999         var v2 = sortType ? sortType(n2) : (cs ? n2.attributes[p] : n2.attributes[p].toUpperCase());
19000         if(v1 < v2){
19001                         return dsc ? +1 : -1;
19002                 }else if(v1 > v2){
19003                         return dsc ? -1 : +1;
19004         }else{
19005                 return 0;
19006         }
19007     };
19008 };
19009
19010 Roo.tree.TreeSorter.prototype = {
19011     doSort : function(node){
19012         node.sort(this.sortFn);
19013     },
19014     
19015     compareNodes : function(n1, n2){
19016         return (n1.text.toUpperCase() > n2.text.toUpperCase() ? 1 : -1);
19017     },
19018     
19019     updateSort : function(tree, node){
19020         if(node.childrenRendered){
19021             this.doSort.defer(1, this, [node]);
19022         }
19023     }
19024 };/*
19025  * Based on:
19026  * Ext JS Library 1.1.1
19027  * Copyright(c) 2006-2007, Ext JS, LLC.
19028  *
19029  * Originally Released Under LGPL - original licence link has changed is not relivant.
19030  *
19031  * Fork - LGPL
19032  * <script type="text/javascript">
19033  */
19034
19035 if(Roo.dd.DropZone){
19036     
19037 Roo.tree.TreeDropZone = function(tree, config){
19038     this.allowParentInsert = false;
19039     this.allowContainerDrop = false;
19040     this.appendOnly = false;
19041     Roo.tree.TreeDropZone.superclass.constructor.call(this, tree.innerCt, config);
19042     this.tree = tree;
19043     this.lastInsertClass = "x-tree-no-status";
19044     this.dragOverData = {};
19045 };
19046
19047 Roo.extend(Roo.tree.TreeDropZone, Roo.dd.DropZone, {
19048     ddGroup : "TreeDD",
19049     scroll:  true,
19050     
19051     expandDelay : 1000,
19052     
19053     expandNode : function(node){
19054         if(node.hasChildNodes() && !node.isExpanded()){
19055             node.expand(false, null, this.triggerCacheRefresh.createDelegate(this));
19056         }
19057     },
19058     
19059     queueExpand : function(node){
19060         this.expandProcId = this.expandNode.defer(this.expandDelay, this, [node]);
19061     },
19062     
19063     cancelExpand : function(){
19064         if(this.expandProcId){
19065             clearTimeout(this.expandProcId);
19066             this.expandProcId = false;
19067         }
19068     },
19069     
19070     isValidDropPoint : function(n, pt, dd, e, data){
19071         if(!n || !data){ return false; }
19072         var targetNode = n.node;
19073         var dropNode = data.node;
19074         // default drop rules
19075         if(!(targetNode && targetNode.isTarget && pt)){
19076             return false;
19077         }
19078         if(pt == "append" && targetNode.allowChildren === false){
19079             return false;
19080         }
19081         if((pt == "above" || pt == "below") && (targetNode.parentNode && targetNode.parentNode.allowChildren === false)){
19082             return false;
19083         }
19084         if(dropNode && (targetNode == dropNode || dropNode.contains(targetNode))){
19085             return false;
19086         }
19087         // reuse the object
19088         var overEvent = this.dragOverData;
19089         overEvent.tree = this.tree;
19090         overEvent.target = targetNode;
19091         overEvent.data = data;
19092         overEvent.point = pt;
19093         overEvent.source = dd;
19094         overEvent.rawEvent = e;
19095         overEvent.dropNode = dropNode;
19096         overEvent.cancel = false;  
19097         var result = this.tree.fireEvent("nodedragover", overEvent);
19098         return overEvent.cancel === false && result !== false;
19099     },
19100     
19101     getDropPoint : function(e, n, dd)
19102     {
19103         var tn = n.node;
19104         if(tn.isRoot){
19105             return tn.allowChildren !== false ? "append" : false; // always append for root
19106         }
19107         var dragEl = n.ddel;
19108         var t = Roo.lib.Dom.getY(dragEl), b = t + dragEl.offsetHeight;
19109         var y = Roo.lib.Event.getPageY(e);
19110         //var noAppend = tn.allowChildren === false || tn.isLeaf();
19111         
19112         // we may drop nodes anywhere, as long as allowChildren has not been set to false..
19113         var noAppend = tn.allowChildren === false;
19114         if(this.appendOnly || tn.parentNode.allowChildren === false){
19115             return noAppend ? false : "append";
19116         }
19117         var noBelow = false;
19118         if(!this.allowParentInsert){
19119             noBelow = tn.hasChildNodes() && tn.isExpanded();
19120         }
19121         var q = (b - t) / (noAppend ? 2 : 3);
19122         if(y >= t && y < (t + q)){
19123             return "above";
19124         }else if(!noBelow && (noAppend || y >= b-q && y <= b)){
19125             return "below";
19126         }else{
19127             return "append";
19128         }
19129     },
19130     
19131     onNodeEnter : function(n, dd, e, data)
19132     {
19133         this.cancelExpand();
19134     },
19135     
19136     onNodeOver : function(n, dd, e, data)
19137     {
19138        
19139         var pt = this.getDropPoint(e, n, dd);
19140         var node = n.node;
19141         
19142         // auto node expand check
19143         if(!this.expandProcId && pt == "append" && node.hasChildNodes() && !n.node.isExpanded()){
19144             this.queueExpand(node);
19145         }else if(pt != "append"){
19146             this.cancelExpand();
19147         }
19148         
19149         // set the insert point style on the target node
19150         var returnCls = this.dropNotAllowed;
19151         if(this.isValidDropPoint(n, pt, dd, e, data)){
19152            if(pt){
19153                var el = n.ddel;
19154                var cls;
19155                if(pt == "above"){
19156                    returnCls = n.node.isFirst() ? "x-tree-drop-ok-above" : "x-tree-drop-ok-between";
19157                    cls = "x-tree-drag-insert-above";
19158                }else if(pt == "below"){
19159                    returnCls = n.node.isLast() ? "x-tree-drop-ok-below" : "x-tree-drop-ok-between";
19160                    cls = "x-tree-drag-insert-below";
19161                }else{
19162                    returnCls = "x-tree-drop-ok-append";
19163                    cls = "x-tree-drag-append";
19164                }
19165                if(this.lastInsertClass != cls){
19166                    Roo.fly(el).replaceClass(this.lastInsertClass, cls);
19167                    this.lastInsertClass = cls;
19168                }
19169            }
19170        }
19171        return returnCls;
19172     },
19173     
19174     onNodeOut : function(n, dd, e, data){
19175         
19176         this.cancelExpand();
19177         this.removeDropIndicators(n);
19178     },
19179     
19180     onNodeDrop : function(n, dd, e, data){
19181         var point = this.getDropPoint(e, n, dd);
19182         var targetNode = n.node;
19183         targetNode.ui.startDrop();
19184         if(!this.isValidDropPoint(n, point, dd, e, data)){
19185             targetNode.ui.endDrop();
19186             return false;
19187         }
19188         // first try to find the drop node
19189         var dropNode = data.node || (dd.getTreeNode ? dd.getTreeNode(data, targetNode, point, e) : null);
19190         var dropEvent = {
19191             tree : this.tree,
19192             target: targetNode,
19193             data: data,
19194             point: point,
19195             source: dd,
19196             rawEvent: e,
19197             dropNode: dropNode,
19198             cancel: !dropNode   
19199         };
19200         var retval = this.tree.fireEvent("beforenodedrop", dropEvent);
19201         if(retval === false || dropEvent.cancel === true || !dropEvent.dropNode){
19202             targetNode.ui.endDrop();
19203             return false;
19204         }
19205         // allow target changing
19206         targetNode = dropEvent.target;
19207         if(point == "append" && !targetNode.isExpanded()){
19208             targetNode.expand(false, null, function(){
19209                 this.completeDrop(dropEvent);
19210             }.createDelegate(this));
19211         }else{
19212             this.completeDrop(dropEvent);
19213         }
19214         return true;
19215     },
19216     
19217     completeDrop : function(de){
19218         var ns = de.dropNode, p = de.point, t = de.target;
19219         if(!(ns instanceof Array)){
19220             ns = [ns];
19221         }
19222         var n;
19223         for(var i = 0, len = ns.length; i < len; i++){
19224             n = ns[i];
19225             if(p == "above"){
19226                 t.parentNode.insertBefore(n, t);
19227             }else if(p == "below"){
19228                 t.parentNode.insertBefore(n, t.nextSibling);
19229             }else{
19230                 t.appendChild(n);
19231             }
19232         }
19233         n.ui.focus();
19234         if(this.tree.hlDrop){
19235             n.ui.highlight();
19236         }
19237         t.ui.endDrop();
19238         this.tree.fireEvent("nodedrop", de);
19239     },
19240     
19241     afterNodeMoved : function(dd, data, e, targetNode, dropNode){
19242         if(this.tree.hlDrop){
19243             dropNode.ui.focus();
19244             dropNode.ui.highlight();
19245         }
19246         this.tree.fireEvent("nodedrop", this.tree, targetNode, data, dd, e);
19247     },
19248     
19249     getTree : function(){
19250         return this.tree;
19251     },
19252     
19253     removeDropIndicators : function(n){
19254         if(n && n.ddel){
19255             var el = n.ddel;
19256             Roo.fly(el).removeClass([
19257                     "x-tree-drag-insert-above",
19258                     "x-tree-drag-insert-below",
19259                     "x-tree-drag-append"]);
19260             this.lastInsertClass = "_noclass";
19261         }
19262     },
19263     
19264     beforeDragDrop : function(target, e, id){
19265         this.cancelExpand();
19266         return true;
19267     },
19268     
19269     afterRepair : function(data){
19270         if(data && Roo.enableFx){
19271             data.node.ui.highlight();
19272         }
19273         this.hideProxy();
19274     } 
19275     
19276 });
19277
19278 }
19279 /*
19280  * Based on:
19281  * Ext JS Library 1.1.1
19282  * Copyright(c) 2006-2007, Ext JS, LLC.
19283  *
19284  * Originally Released Under LGPL - original licence link has changed is not relivant.
19285  *
19286  * Fork - LGPL
19287  * <script type="text/javascript">
19288  */
19289  
19290
19291 if(Roo.dd.DragZone){
19292 Roo.tree.TreeDragZone = function(tree, config){
19293     Roo.tree.TreeDragZone.superclass.constructor.call(this, tree.getTreeEl(), config);
19294     this.tree = tree;
19295 };
19296
19297 Roo.extend(Roo.tree.TreeDragZone, Roo.dd.DragZone, {
19298     ddGroup : "TreeDD",
19299    
19300     onBeforeDrag : function(data, e){
19301         var n = data.node;
19302         return n && n.draggable && !n.disabled;
19303     },
19304      
19305     
19306     onInitDrag : function(e){
19307         var data = this.dragData;
19308         this.tree.getSelectionModel().select(data.node);
19309         this.proxy.update("");
19310         data.node.ui.appendDDGhost(this.proxy.ghost.dom);
19311         this.tree.fireEvent("startdrag", this.tree, data.node, e);
19312     },
19313     
19314     getRepairXY : function(e, data){
19315         return data.node.ui.getDDRepairXY();
19316     },
19317     
19318     onEndDrag : function(data, e){
19319         this.tree.fireEvent("enddrag", this.tree, data.node, e);
19320         
19321         
19322     },
19323     
19324     onValidDrop : function(dd, e, id){
19325         this.tree.fireEvent("dragdrop", this.tree, this.dragData.node, dd, e);
19326         this.hideProxy();
19327     },
19328     
19329     beforeInvalidDrop : function(e, id){
19330         // this scrolls the original position back into view
19331         var sm = this.tree.getSelectionModel();
19332         sm.clearSelections();
19333         sm.select(this.dragData.node);
19334     }
19335 });
19336 }/*
19337  * Based on:
19338  * Ext JS Library 1.1.1
19339  * Copyright(c) 2006-2007, Ext JS, LLC.
19340  *
19341  * Originally Released Under LGPL - original licence link has changed is not relivant.
19342  *
19343  * Fork - LGPL
19344  * <script type="text/javascript">
19345  */
19346 /**
19347  * @class Roo.tree.TreeEditor
19348  * @extends Roo.Editor
19349  * Provides editor functionality for inline tree node editing.  Any valid {@link Roo.form.Field} can be used
19350  * as the editor field.
19351  * @constructor
19352  * @param {Object} config (used to be the tree panel.)
19353  * @param {Object} oldconfig DEPRECIATED Either a prebuilt {@link Roo.form.Field} instance or a Field config object
19354  * 
19355  * @cfg {Roo.tree.TreePanel} tree The tree to bind to.
19356  * @cfg {Roo.form.TextField|Object} field The field configuration
19357  *
19358  * 
19359  */
19360 Roo.tree.TreeEditor = function(config, oldconfig) { // was -- (tree, config){
19361     var tree = config;
19362     var field;
19363     if (oldconfig) { // old style..
19364         field = oldconfig.events ? oldconfig : new Roo.form.TextField(oldconfig);
19365     } else {
19366         // new style..
19367         tree = config.tree;
19368         config.field = config.field  || {};
19369         config.field.xtype = 'TextField';
19370         field = Roo.factory(config.field, Roo.form);
19371     }
19372     config = config || {};
19373     
19374     
19375     this.addEvents({
19376         /**
19377          * @event beforenodeedit
19378          * Fires when editing is initiated, but before the value changes.  Editing can be canceled by returning
19379          * false from the handler of this event.
19380          * @param {Editor} this
19381          * @param {Roo.tree.Node} node 
19382          */
19383         "beforenodeedit" : true
19384     });
19385     
19386     //Roo.log(config);
19387     Roo.tree.TreeEditor.superclass.constructor.call(this, field, config);
19388
19389     this.tree = tree;
19390
19391     tree.on('beforeclick', this.beforeNodeClick, this);
19392     tree.getTreeEl().on('mousedown', this.hide, this);
19393     this.on('complete', this.updateNode, this);
19394     this.on('beforestartedit', this.fitToTree, this);
19395     this.on('startedit', this.bindScroll, this, {delay:10});
19396     this.on('specialkey', this.onSpecialKey, this);
19397 };
19398
19399 Roo.extend(Roo.tree.TreeEditor, Roo.Editor, {
19400     /**
19401      * @cfg {String} alignment
19402      * The position to align to (see {@link Roo.Element#alignTo} for more details, defaults to "l-l").
19403      */
19404     alignment: "l-l",
19405     // inherit
19406     autoSize: false,
19407     /**
19408      * @cfg {Boolean} hideEl
19409      * True to hide the bound element while the editor is displayed (defaults to false)
19410      */
19411     hideEl : false,
19412     /**
19413      * @cfg {String} cls
19414      * CSS class to apply to the editor (defaults to "x-small-editor x-tree-editor")
19415      */
19416     cls: "x-small-editor x-tree-editor",
19417     /**
19418      * @cfg {Boolean} shim
19419      * True to shim the editor if selects/iframes could be displayed beneath it (defaults to false)
19420      */
19421     shim:false,
19422     // inherit
19423     shadow:"frame",
19424     /**
19425      * @cfg {Number} maxWidth
19426      * The maximum width in pixels of the editor field (defaults to 250).  Note that if the maxWidth would exceed
19427      * the containing tree element's size, it will be automatically limited for you to the container width, taking
19428      * scroll and client offsets into account prior to each edit.
19429      */
19430     maxWidth: 250,
19431
19432     editDelay : 350,
19433
19434     // private
19435     fitToTree : function(ed, el){
19436         var td = this.tree.getTreeEl().dom, nd = el.dom;
19437         if(td.scrollLeft >  nd.offsetLeft){ // ensure the node left point is visible
19438             td.scrollLeft = nd.offsetLeft;
19439         }
19440         var w = Math.min(
19441                 this.maxWidth,
19442                 (td.clientWidth > 20 ? td.clientWidth : td.offsetWidth) - Math.max(0, nd.offsetLeft-td.scrollLeft) - /*cushion*/5);
19443         this.setSize(w, '');
19444         
19445         return this.fireEvent('beforenodeedit', this, this.editNode);
19446         
19447     },
19448
19449     // private
19450     triggerEdit : function(node){
19451         this.completeEdit();
19452         this.editNode = node;
19453         this.startEdit(node.ui.textNode, node.text);
19454     },
19455
19456     // private
19457     bindScroll : function(){
19458         this.tree.getTreeEl().on('scroll', this.cancelEdit, this);
19459     },
19460
19461     // private
19462     beforeNodeClick : function(node, e){
19463         var sinceLast = (this.lastClick ? this.lastClick.getElapsed() : 0);
19464         this.lastClick = new Date();
19465         if(sinceLast > this.editDelay && this.tree.getSelectionModel().isSelected(node)){
19466             e.stopEvent();
19467             this.triggerEdit(node);
19468             return false;
19469         }
19470         return true;
19471     },
19472
19473     // private
19474     updateNode : function(ed, value){
19475         this.tree.getTreeEl().un('scroll', this.cancelEdit, this);
19476         this.editNode.setText(value);
19477     },
19478
19479     // private
19480     onHide : function(){
19481         Roo.tree.TreeEditor.superclass.onHide.call(this);
19482         if(this.editNode){
19483             this.editNode.ui.focus();
19484         }
19485     },
19486
19487     // private
19488     onSpecialKey : function(field, e){
19489         var k = e.getKey();
19490         if(k == e.ESC){
19491             e.stopEvent();
19492             this.cancelEdit();
19493         }else if(k == e.ENTER && !e.hasModifier()){
19494             e.stopEvent();
19495             this.completeEdit();
19496         }
19497     }
19498 });//<Script type="text/javascript">
19499 /*
19500  * Based on:
19501  * Ext JS Library 1.1.1
19502  * Copyright(c) 2006-2007, Ext JS, LLC.
19503  *
19504  * Originally Released Under LGPL - original licence link has changed is not relivant.
19505  *
19506  * Fork - LGPL
19507  * <script type="text/javascript">
19508  */
19509  
19510 /**
19511  * Not documented??? - probably should be...
19512  */
19513
19514 Roo.tree.ColumnNodeUI = Roo.extend(Roo.tree.TreeNodeUI, {
19515     //focus: Roo.emptyFn, // prevent odd scrolling behavior
19516     
19517     renderElements : function(n, a, targetNode, bulkRender){
19518         //consel.log("renderElements?");
19519         this.indentMarkup = n.parentNode ? n.parentNode.ui.getChildIndent() : '';
19520
19521         var t = n.getOwnerTree();
19522         var tid = Pman.Tab.Document_TypesTree.tree.el.id;
19523         
19524         var cols = t.columns;
19525         var bw = t.borderWidth;
19526         var c = cols[0];
19527         var href = a.href ? a.href : Roo.isGecko ? "" : "#";
19528          var cb = typeof a.checked == "boolean";
19529         var tx = String.format('{0}',n.text || (c.renderer ? c.renderer(a[c.dataIndex], n, a) : a[c.dataIndex]));
19530         var colcls = 'x-t-' + tid + '-c0';
19531         var buf = [
19532             '<li class="x-tree-node">',
19533             
19534                 
19535                 '<div class="x-tree-node-el ', a.cls,'">',
19536                     // extran...
19537                     '<div class="x-tree-col ', colcls, '" style="width:', c.width-bw, 'px;">',
19538                 
19539                 
19540                         '<span class="x-tree-node-indent">',this.indentMarkup,'</span>',
19541                         '<img src="', this.emptyIcon, '" class="x-tree-ec-icon  " />',
19542                         '<img src="', a.icon || this.emptyIcon, '" class="x-tree-node-icon',
19543                            (a.icon ? ' x-tree-node-inline-icon' : ''),
19544                            (a.iconCls ? ' '+a.iconCls : ''),
19545                            '" unselectable="on" />',
19546                         (cb ? ('<input class="x-tree-node-cb" type="checkbox" ' + 
19547                              (a.checked ? 'checked="checked" />' : ' />')) : ''),
19548                              
19549                         '<a class="x-tree-node-anchor" hidefocus="on" href="',href,'" tabIndex="1" ',
19550                             (a.hrefTarget ? ' target="' +a.hrefTarget + '"' : ''), '>',
19551                             '<span unselectable="on" qtip="' + tx + '">',
19552                              tx,
19553                              '</span></a>' ,
19554                     '</div>',
19555                      '<a class="x-tree-node-anchor" hidefocus="on" href="',href,'" tabIndex="1" ',
19556                             (a.hrefTarget ? ' target="' +a.hrefTarget + '"' : ''), '>'
19557                  ];
19558         for(var i = 1, len = cols.length; i < len; i++){
19559             c = cols[i];
19560             colcls = 'x-t-' + tid + '-c' +i;
19561             tx = String.format('{0}', (c.renderer ? c.renderer(a[c.dataIndex], n, a) : a[c.dataIndex]));
19562             buf.push('<div class="x-tree-col ', colcls, ' ' ,(c.cls?c.cls:''),'" style="width:',c.width-bw,'px;">',
19563                         '<div class="x-tree-col-text" qtip="' + tx +'">',tx,"</div>",
19564                       "</div>");
19565          }
19566          
19567          buf.push(
19568             '</a>',
19569             '<div class="x-clear"></div></div>',
19570             '<ul class="x-tree-node-ct" style="display:none;"></ul>',
19571             "</li>");
19572         
19573         if(bulkRender !== true && n.nextSibling && n.nextSibling.ui.getEl()){
19574             this.wrap = Roo.DomHelper.insertHtml("beforeBegin",
19575                                 n.nextSibling.ui.getEl(), buf.join(""));
19576         }else{
19577             this.wrap = Roo.DomHelper.insertHtml("beforeEnd", targetNode, buf.join(""));
19578         }
19579         var el = this.wrap.firstChild;
19580         this.elRow = el;
19581         this.elNode = el.firstChild;
19582         this.ranchor = el.childNodes[1];
19583         this.ctNode = this.wrap.childNodes[1];
19584         var cs = el.firstChild.childNodes;
19585         this.indentNode = cs[0];
19586         this.ecNode = cs[1];
19587         this.iconNode = cs[2];
19588         var index = 3;
19589         if(cb){
19590             this.checkbox = cs[3];
19591             index++;
19592         }
19593         this.anchor = cs[index];
19594         
19595         this.textNode = cs[index].firstChild;
19596         
19597         //el.on("click", this.onClick, this);
19598         //el.on("dblclick", this.onDblClick, this);
19599         
19600         
19601        // console.log(this);
19602     },
19603     initEvents : function(){
19604         Roo.tree.ColumnNodeUI.superclass.initEvents.call(this);
19605         
19606             
19607         var a = this.ranchor;
19608
19609         var el = Roo.get(a);
19610
19611         if(Roo.isOpera){ // opera render bug ignores the CSS
19612             el.setStyle("text-decoration", "none");
19613         }
19614
19615         el.on("click", this.onClick, this);
19616         el.on("dblclick", this.onDblClick, this);
19617         el.on("contextmenu", this.onContextMenu, this);
19618         
19619     },
19620     
19621     /*onSelectedChange : function(state){
19622         if(state){
19623             this.focus();
19624             this.addClass("x-tree-selected");
19625         }else{
19626             //this.blur();
19627             this.removeClass("x-tree-selected");
19628         }
19629     },*/
19630     addClass : function(cls){
19631         if(this.elRow){
19632             Roo.fly(this.elRow).addClass(cls);
19633         }
19634         
19635     },
19636     
19637     
19638     removeClass : function(cls){
19639         if(this.elRow){
19640             Roo.fly(this.elRow).removeClass(cls);
19641         }
19642     }
19643
19644     
19645     
19646 });//<Script type="text/javascript">
19647
19648 /*
19649  * Based on:
19650  * Ext JS Library 1.1.1
19651  * Copyright(c) 2006-2007, Ext JS, LLC.
19652  *
19653  * Originally Released Under LGPL - original licence link has changed is not relivant.
19654  *
19655  * Fork - LGPL
19656  * <script type="text/javascript">
19657  */
19658  
19659
19660 /**
19661  * @class Roo.tree.ColumnTree
19662  * @extends Roo.data.TreePanel
19663  * @cfg {Object} columns  Including width, header, renderer, cls, dataIndex 
19664  * @cfg {int} borderWidth  compined right/left border allowance
19665  * @constructor
19666  * @param {String/HTMLElement/Element} el The container element
19667  * @param {Object} config
19668  */
19669 Roo.tree.ColumnTree =  function(el, config)
19670 {
19671    Roo.tree.ColumnTree.superclass.constructor.call(this, el , config);
19672    this.addEvents({
19673         /**
19674         * @event resize
19675         * Fire this event on a container when it resizes
19676         * @param {int} w Width
19677         * @param {int} h Height
19678         */
19679        "resize" : true
19680     });
19681     this.on('resize', this.onResize, this);
19682 };
19683
19684 Roo.extend(Roo.tree.ColumnTree, Roo.tree.TreePanel, {
19685     //lines:false,
19686     
19687     
19688     borderWidth: Roo.isBorderBox ? 0 : 2, 
19689     headEls : false,
19690     
19691     render : function(){
19692         // add the header.....
19693        
19694         Roo.tree.ColumnTree.superclass.render.apply(this);
19695         
19696         this.el.addClass('x-column-tree');
19697         
19698         this.headers = this.el.createChild(
19699             {cls:'x-tree-headers'},this.innerCt.dom);
19700    
19701         var cols = this.columns, c;
19702         var totalWidth = 0;
19703         this.headEls = [];
19704         var  len = cols.length;
19705         for(var i = 0; i < len; i++){
19706              c = cols[i];
19707              totalWidth += c.width;
19708             this.headEls.push(this.headers.createChild({
19709                  cls:'x-tree-hd ' + (c.cls?c.cls+'-hd':''),
19710                  cn: {
19711                      cls:'x-tree-hd-text',
19712                      html: c.header
19713                  },
19714                  style:'width:'+(c.width-this.borderWidth)+'px;'
19715              }));
19716         }
19717         this.headers.createChild({cls:'x-clear'});
19718         // prevent floats from wrapping when clipped
19719         this.headers.setWidth(totalWidth);
19720         //this.innerCt.setWidth(totalWidth);
19721         this.innerCt.setStyle({ overflow: 'auto' });
19722         this.onResize(this.width, this.height);
19723              
19724         
19725     },
19726     onResize : function(w,h)
19727     {
19728         this.height = h;
19729         this.width = w;
19730         // resize cols..
19731         this.innerCt.setWidth(this.width);
19732         this.innerCt.setHeight(this.height-20);
19733         
19734         // headers...
19735         var cols = this.columns, c;
19736         var totalWidth = 0;
19737         var expEl = false;
19738         var len = cols.length;
19739         for(var i = 0; i < len; i++){
19740             c = cols[i];
19741             if (this.autoExpandColumn !== false && c.dataIndex == this.autoExpandColumn) {
19742                 // it's the expander..
19743                 expEl  = this.headEls[i];
19744                 continue;
19745             }
19746             totalWidth += c.width;
19747             
19748         }
19749         if (expEl) {
19750             expEl.setWidth(  ((w - totalWidth)-this.borderWidth - 20));
19751         }
19752         this.headers.setWidth(w-20);
19753
19754         
19755         
19756         
19757     }
19758 });
19759 /*
19760  * Based on:
19761  * Ext JS Library 1.1.1
19762  * Copyright(c) 2006-2007, Ext JS, LLC.
19763  *
19764  * Originally Released Under LGPL - original licence link has changed is not relivant.
19765  *
19766  * Fork - LGPL
19767  * <script type="text/javascript">
19768  */
19769  
19770 /**
19771  * @class Roo.menu.Menu
19772  * @extends Roo.util.Observable
19773  * A menu object.  This is the container to which you add all other menu items.  Menu can also serve a as a base class
19774  * when you want a specialzed menu based off of another component (like {@link Roo.menu.DateMenu} for example).
19775  * @constructor
19776  * Creates a new Menu
19777  * @param {Object} config Configuration options
19778  */
19779 Roo.menu.Menu = function(config){
19780     Roo.apply(this, config);
19781     this.id = this.id || Roo.id();
19782     this.addEvents({
19783         /**
19784          * @event beforeshow
19785          * Fires before this menu is displayed
19786          * @param {Roo.menu.Menu} this
19787          */
19788         beforeshow : true,
19789         /**
19790          * @event beforehide
19791          * Fires before this menu is hidden
19792          * @param {Roo.menu.Menu} this
19793          */
19794         beforehide : true,
19795         /**
19796          * @event show
19797          * Fires after this menu is displayed
19798          * @param {Roo.menu.Menu} this
19799          */
19800         show : true,
19801         /**
19802          * @event hide
19803          * Fires after this menu is hidden
19804          * @param {Roo.menu.Menu} this
19805          */
19806         hide : true,
19807         /**
19808          * @event click
19809          * Fires when this menu is clicked (or when the enter key is pressed while it is active)
19810          * @param {Roo.menu.Menu} this
19811          * @param {Roo.menu.Item} menuItem The menu item that was clicked
19812          * @param {Roo.EventObject} e
19813          */
19814         click : true,
19815         /**
19816          * @event mouseover
19817          * Fires when the mouse is hovering over this menu
19818          * @param {Roo.menu.Menu} this
19819          * @param {Roo.EventObject} e
19820          * @param {Roo.menu.Item} menuItem The menu item that was clicked
19821          */
19822         mouseover : true,
19823         /**
19824          * @event mouseout
19825          * Fires when the mouse exits this menu
19826          * @param {Roo.menu.Menu} this
19827          * @param {Roo.EventObject} e
19828          * @param {Roo.menu.Item} menuItem The menu item that was clicked
19829          */
19830         mouseout : true,
19831         /**
19832          * @event itemclick
19833          * Fires when a menu item contained in this menu is clicked
19834          * @param {Roo.menu.BaseItem} baseItem The BaseItem that was clicked
19835          * @param {Roo.EventObject} e
19836          */
19837         itemclick: true
19838     });
19839     if (this.registerMenu) {
19840         Roo.menu.MenuMgr.register(this);
19841     }
19842     
19843     var mis = this.items;
19844     this.items = new Roo.util.MixedCollection();
19845     if(mis){
19846         this.add.apply(this, mis);
19847     }
19848 };
19849
19850 Roo.extend(Roo.menu.Menu, Roo.util.Observable, {
19851     /**
19852      * @cfg {Number} minWidth The minimum width of the menu in pixels (defaults to 120)
19853      */
19854     minWidth : 120,
19855     /**
19856      * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop"
19857      * for bottom-right shadow (defaults to "sides")
19858      */
19859     shadow : "sides",
19860     /**
19861      * @cfg {String} subMenuAlign The {@link Roo.Element#alignTo} anchor position value to use for submenus of
19862      * this menu (defaults to "tl-tr?")
19863      */
19864     subMenuAlign : "tl-tr?",
19865     /**
19866      * @cfg {String} defaultAlign The default {@link Roo.Element#alignTo) anchor position value for this menu
19867      * relative to its element of origin (defaults to "tl-bl?")
19868      */
19869     defaultAlign : "tl-bl?",
19870     /**
19871      * @cfg {Boolean} allowOtherMenus True to allow multiple menus to be displayed at the same time (defaults to false)
19872      */
19873     allowOtherMenus : false,
19874     /**
19875      * @cfg {Boolean} registerMenu True (default) - means that clicking on screen etc. hides it.
19876      */
19877     registerMenu : true,
19878
19879     hidden:true,
19880
19881     // private
19882     render : function(){
19883         if(this.el){
19884             return;
19885         }
19886         var el = this.el = new Roo.Layer({
19887             cls: "x-menu",
19888             shadow:this.shadow,
19889             constrain: false,
19890             parentEl: this.parentEl || document.body,
19891             zindex:15000
19892         });
19893
19894         this.keyNav = new Roo.menu.MenuNav(this);
19895
19896         if(this.plain){
19897             el.addClass("x-menu-plain");
19898         }
19899         if(this.cls){
19900             el.addClass(this.cls);
19901         }
19902         // generic focus element
19903         this.focusEl = el.createChild({
19904             tag: "a", cls: "x-menu-focus", href: "#", onclick: "return false;", tabIndex:"-1"
19905         });
19906         var ul = el.createChild({tag: "ul", cls: "x-menu-list"});
19907         ul.on("click", this.onClick, this);
19908         ul.on("mouseover", this.onMouseOver, this);
19909         ul.on("mouseout", this.onMouseOut, this);
19910         this.items.each(function(item){
19911             if (item.hidden) {
19912                 return;
19913             }
19914             
19915             var li = document.createElement("li");
19916             li.className = "x-menu-list-item";
19917             ul.dom.appendChild(li);
19918             item.render(li, this);
19919         }, this);
19920         this.ul = ul;
19921         this.autoWidth();
19922     },
19923
19924     // private
19925     autoWidth : function(){
19926         var el = this.el, ul = this.ul;
19927         if(!el){
19928             return;
19929         }
19930         var w = this.width;
19931         if(w){
19932             el.setWidth(w);
19933         }else if(Roo.isIE){
19934             el.setWidth(this.minWidth);
19935             var t = el.dom.offsetWidth; // force recalc
19936             el.setWidth(ul.getWidth()+el.getFrameWidth("lr"));
19937         }
19938     },
19939
19940     // private
19941     delayAutoWidth : function(){
19942         if(this.rendered){
19943             if(!this.awTask){
19944                 this.awTask = new Roo.util.DelayedTask(this.autoWidth, this);
19945             }
19946             this.awTask.delay(20);
19947         }
19948     },
19949
19950     // private
19951     findTargetItem : function(e){
19952         var t = e.getTarget(".x-menu-list-item", this.ul,  true);
19953         if(t && t.menuItemId){
19954             return this.items.get(t.menuItemId);
19955         }
19956     },
19957
19958     // private
19959     onClick : function(e){
19960         var t;
19961         if(t = this.findTargetItem(e)){
19962             t.onClick(e);
19963             this.fireEvent("click", this, t, e);
19964         }
19965     },
19966
19967     // private
19968     setActiveItem : function(item, autoExpand){
19969         if(item != this.activeItem){
19970             if(this.activeItem){
19971                 this.activeItem.deactivate();
19972             }
19973             this.activeItem = item;
19974             item.activate(autoExpand);
19975         }else if(autoExpand){
19976             item.expandMenu();
19977         }
19978     },
19979
19980     // private
19981     tryActivate : function(start, step){
19982         var items = this.items;
19983         for(var i = start, len = items.length; i >= 0 && i < len; i+= step){
19984             var item = items.get(i);
19985             if(!item.disabled && item.canActivate){
19986                 this.setActiveItem(item, false);
19987                 return item;
19988             }
19989         }
19990         return false;
19991     },
19992
19993     // private
19994     onMouseOver : function(e){
19995         var t;
19996         if(t = this.findTargetItem(e)){
19997             if(t.canActivate && !t.disabled){
19998                 this.setActiveItem(t, true);
19999             }
20000         }
20001         this.fireEvent("mouseover", this, e, t);
20002     },
20003
20004     // private
20005     onMouseOut : function(e){
20006         var t;
20007         if(t = this.findTargetItem(e)){
20008             if(t == this.activeItem && t.shouldDeactivate(e)){
20009                 this.activeItem.deactivate();
20010                 delete this.activeItem;
20011             }
20012         }
20013         this.fireEvent("mouseout", this, e, t);
20014     },
20015
20016     /**
20017      * Read-only.  Returns true if the menu is currently displayed, else false.
20018      * @type Boolean
20019      */
20020     isVisible : function(){
20021         return this.el && !this.hidden;
20022     },
20023
20024     /**
20025      * Displays this menu relative to another element
20026      * @param {String/HTMLElement/Roo.Element} element The element to align to
20027      * @param {String} position (optional) The {@link Roo.Element#alignTo} anchor position to use in aligning to
20028      * the element (defaults to this.defaultAlign)
20029      * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
20030      */
20031     show : function(el, pos, parentMenu){
20032         this.parentMenu = parentMenu;
20033         if(!this.el){
20034             this.render();
20035         }
20036         this.fireEvent("beforeshow", this);
20037         this.showAt(this.el.getAlignToXY(el, pos || this.defaultAlign), parentMenu, false);
20038     },
20039
20040     /**
20041      * Displays this menu at a specific xy position
20042      * @param {Array} xyPosition Contains X & Y [x, y] values for the position at which to show the menu (coordinates are page-based)
20043      * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
20044      */
20045     showAt : function(xy, parentMenu, /* private: */_e){
20046         this.parentMenu = parentMenu;
20047         if(!this.el){
20048             this.render();
20049         }
20050         if(_e !== false){
20051             this.fireEvent("beforeshow", this);
20052             xy = this.el.adjustForConstraints(xy);
20053         }
20054         this.el.setXY(xy);
20055         this.el.show();
20056         this.hidden = false;
20057         this.focus();
20058         this.fireEvent("show", this);
20059     },
20060
20061     focus : function(){
20062         if(!this.hidden){
20063             this.doFocus.defer(50, this);
20064         }
20065     },
20066
20067     doFocus : function(){
20068         if(!this.hidden){
20069             this.focusEl.focus();
20070         }
20071     },
20072
20073     /**
20074      * Hides this menu and optionally all parent menus
20075      * @param {Boolean} deep (optional) True to hide all parent menus recursively, if any (defaults to false)
20076      */
20077     hide : function(deep){
20078         if(this.el && this.isVisible()){
20079             this.fireEvent("beforehide", this);
20080             if(this.activeItem){
20081                 this.activeItem.deactivate();
20082                 this.activeItem = null;
20083             }
20084             this.el.hide();
20085             this.hidden = true;
20086             this.fireEvent("hide", this);
20087         }
20088         if(deep === true && this.parentMenu){
20089             this.parentMenu.hide(true);
20090         }
20091     },
20092
20093     /**
20094      * Addds one or more items of any type supported by the Menu class, or that can be converted into menu items.
20095      * Any of the following are valid:
20096      * <ul>
20097      * <li>Any menu item object based on {@link Roo.menu.Item}</li>
20098      * <li>An HTMLElement object which will be converted to a menu item</li>
20099      * <li>A menu item config object that will be created as a new menu item</li>
20100      * <li>A string, which can either be '-' or 'separator' to add a menu separator, otherwise
20101      * it will be converted into a {@link Roo.menu.TextItem} and added</li>
20102      * </ul>
20103      * Usage:
20104      * <pre><code>
20105 // Create the menu
20106 var menu = new Roo.menu.Menu();
20107
20108 // Create a menu item to add by reference
20109 var menuItem = new Roo.menu.Item({ text: 'New Item!' });
20110
20111 // Add a bunch of items at once using different methods.
20112 // Only the last item added will be returned.
20113 var item = menu.add(
20114     menuItem,                // add existing item by ref
20115     'Dynamic Item',          // new TextItem
20116     '-',                     // new separator
20117     { text: 'Config Item' }  // new item by config
20118 );
20119 </code></pre>
20120      * @param {Mixed} args One or more menu items, menu item configs or other objects that can be converted to menu items
20121      * @return {Roo.menu.Item} The menu item that was added, or the last one if multiple items were added
20122      */
20123     add : function(){
20124         var a = arguments, l = a.length, item;
20125         for(var i = 0; i < l; i++){
20126             var el = a[i];
20127             if ((typeof(el) == "object") && el.xtype && el.xns) {
20128                 el = Roo.factory(el, Roo.menu);
20129             }
20130             
20131             if(el.render){ // some kind of Item
20132                 item = this.addItem(el);
20133             }else if(typeof el == "string"){ // string
20134                 if(el == "separator" || el == "-"){
20135                     item = this.addSeparator();
20136                 }else{
20137                     item = this.addText(el);
20138                 }
20139             }else if(el.tagName || el.el){ // element
20140                 item = this.addElement(el);
20141             }else if(typeof el == "object"){ // must be menu item config?
20142                 item = this.addMenuItem(el);
20143             }
20144         }
20145         return item;
20146     },
20147
20148     /**
20149      * Returns this menu's underlying {@link Roo.Element} object
20150      * @return {Roo.Element} The element
20151      */
20152     getEl : function(){
20153         if(!this.el){
20154             this.render();
20155         }
20156         return this.el;
20157     },
20158
20159     /**
20160      * Adds a separator bar to the menu
20161      * @return {Roo.menu.Item} The menu item that was added
20162      */
20163     addSeparator : function(){
20164         return this.addItem(new Roo.menu.Separator());
20165     },
20166
20167     /**
20168      * Adds an {@link Roo.Element} object to the menu
20169      * @param {String/HTMLElement/Roo.Element} el The element or DOM node to add, or its id
20170      * @return {Roo.menu.Item} The menu item that was added
20171      */
20172     addElement : function(el){
20173         return this.addItem(new Roo.menu.BaseItem(el));
20174     },
20175
20176     /**
20177      * Adds an existing object based on {@link Roo.menu.Item} to the menu
20178      * @param {Roo.menu.Item} item The menu item to add
20179      * @return {Roo.menu.Item} The menu item that was added
20180      */
20181     addItem : function(item){
20182         this.items.add(item);
20183         if(this.ul){
20184             var li = document.createElement("li");
20185             li.className = "x-menu-list-item";
20186             this.ul.dom.appendChild(li);
20187             item.render(li, this);
20188             this.delayAutoWidth();
20189         }
20190         return item;
20191     },
20192
20193     /**
20194      * Creates a new {@link Roo.menu.Item} based an the supplied config object and adds it to the menu
20195      * @param {Object} config A MenuItem config object
20196      * @return {Roo.menu.Item} The menu item that was added
20197      */
20198     addMenuItem : function(config){
20199         if(!(config instanceof Roo.menu.Item)){
20200             if(typeof config.checked == "boolean"){ // must be check menu item config?
20201                 config = new Roo.menu.CheckItem(config);
20202             }else{
20203                 config = new Roo.menu.Item(config);
20204             }
20205         }
20206         return this.addItem(config);
20207     },
20208
20209     /**
20210      * Creates a new {@link Roo.menu.TextItem} with the supplied text and adds it to the menu
20211      * @param {String} text The text to display in the menu item
20212      * @return {Roo.menu.Item} The menu item that was added
20213      */
20214     addText : function(text){
20215         return this.addItem(new Roo.menu.TextItem({ text : text }));
20216     },
20217
20218     /**
20219      * Inserts an existing object based on {@link Roo.menu.Item} to the menu at a specified index
20220      * @param {Number} index The index in the menu's list of current items where the new item should be inserted
20221      * @param {Roo.menu.Item} item The menu item to add
20222      * @return {Roo.menu.Item} The menu item that was added
20223      */
20224     insert : function(index, item){
20225         this.items.insert(index, item);
20226         if(this.ul){
20227             var li = document.createElement("li");
20228             li.className = "x-menu-list-item";
20229             this.ul.dom.insertBefore(li, this.ul.dom.childNodes[index]);
20230             item.render(li, this);
20231             this.delayAutoWidth();
20232         }
20233         return item;
20234     },
20235
20236     /**
20237      * Removes an {@link Roo.menu.Item} from the menu and destroys the object
20238      * @param {Roo.menu.Item} item The menu item to remove
20239      */
20240     remove : function(item){
20241         this.items.removeKey(item.id);
20242         item.destroy();
20243     },
20244
20245     /**
20246      * Removes and destroys all items in the menu
20247      */
20248     removeAll : function(){
20249         var f;
20250         while(f = this.items.first()){
20251             this.remove(f);
20252         }
20253     }
20254 });
20255
20256 // MenuNav is a private utility class used internally by the Menu
20257 Roo.menu.MenuNav = function(menu){
20258     Roo.menu.MenuNav.superclass.constructor.call(this, menu.el);
20259     this.scope = this.menu = menu;
20260 };
20261
20262 Roo.extend(Roo.menu.MenuNav, Roo.KeyNav, {
20263     doRelay : function(e, h){
20264         var k = e.getKey();
20265         if(!this.menu.activeItem && e.isNavKeyPress() && k != e.SPACE && k != e.RETURN){
20266             this.menu.tryActivate(0, 1);
20267             return false;
20268         }
20269         return h.call(this.scope || this, e, this.menu);
20270     },
20271
20272     up : function(e, m){
20273         if(!m.tryActivate(m.items.indexOf(m.activeItem)-1, -1)){
20274             m.tryActivate(m.items.length-1, -1);
20275         }
20276     },
20277
20278     down : function(e, m){
20279         if(!m.tryActivate(m.items.indexOf(m.activeItem)+1, 1)){
20280             m.tryActivate(0, 1);
20281         }
20282     },
20283
20284     right : function(e, m){
20285         if(m.activeItem){
20286             m.activeItem.expandMenu(true);
20287         }
20288     },
20289
20290     left : function(e, m){
20291         m.hide();
20292         if(m.parentMenu && m.parentMenu.activeItem){
20293             m.parentMenu.activeItem.activate();
20294         }
20295     },
20296
20297     enter : function(e, m){
20298         if(m.activeItem){
20299             e.stopPropagation();
20300             m.activeItem.onClick(e);
20301             m.fireEvent("click", this, m.activeItem);
20302             return true;
20303         }
20304     }
20305 });/*
20306  * Based on:
20307  * Ext JS Library 1.1.1
20308  * Copyright(c) 2006-2007, Ext JS, LLC.
20309  *
20310  * Originally Released Under LGPL - original licence link has changed is not relivant.
20311  *
20312  * Fork - LGPL
20313  * <script type="text/javascript">
20314  */
20315  
20316 /**
20317  * @class Roo.menu.MenuMgr
20318  * Provides a common registry of all menu items on a page so that they can be easily accessed by id.
20319  * @singleton
20320  */
20321 Roo.menu.MenuMgr = function(){
20322    var menus, active, groups = {}, attached = false, lastShow = new Date();
20323
20324    // private - called when first menu is created
20325    function init(){
20326        menus = {};
20327        active = new Roo.util.MixedCollection();
20328        Roo.get(document).addKeyListener(27, function(){
20329            if(active.length > 0){
20330                hideAll();
20331            }
20332        });
20333    }
20334
20335    // private
20336    function hideAll(){
20337        if(active && active.length > 0){
20338            var c = active.clone();
20339            c.each(function(m){
20340                m.hide();
20341            });
20342        }
20343    }
20344
20345    // private
20346    function onHide(m){
20347        active.remove(m);
20348        if(active.length < 1){
20349            Roo.get(document).un("mousedown", onMouseDown);
20350            attached = false;
20351        }
20352    }
20353
20354    // private
20355    function onShow(m){
20356        var last = active.last();
20357        lastShow = new Date();
20358        active.add(m);
20359        if(!attached){
20360            Roo.get(document).on("mousedown", onMouseDown);
20361            attached = true;
20362        }
20363        if(m.parentMenu){
20364           m.getEl().setZIndex(parseInt(m.parentMenu.getEl().getStyle("z-index"), 10) + 3);
20365           m.parentMenu.activeChild = m;
20366        }else if(last && last.isVisible()){
20367           m.getEl().setZIndex(parseInt(last.getEl().getStyle("z-index"), 10) + 3);
20368        }
20369    }
20370
20371    // private
20372    function onBeforeHide(m){
20373        if(m.activeChild){
20374            m.activeChild.hide();
20375        }
20376        if(m.autoHideTimer){
20377            clearTimeout(m.autoHideTimer);
20378            delete m.autoHideTimer;
20379        }
20380    }
20381
20382    // private
20383    function onBeforeShow(m){
20384        var pm = m.parentMenu;
20385        if(!pm && !m.allowOtherMenus){
20386            hideAll();
20387        }else if(pm && pm.activeChild && active != m){
20388            pm.activeChild.hide();
20389        }
20390    }
20391
20392    // private
20393    function onMouseDown(e){
20394        if(lastShow.getElapsed() > 50 && active.length > 0 && !e.getTarget(".x-menu")){
20395            hideAll();
20396        }
20397    }
20398
20399    // private
20400    function onBeforeCheck(mi, state){
20401        if(state){
20402            var g = groups[mi.group];
20403            for(var i = 0, l = g.length; i < l; i++){
20404                if(g[i] != mi){
20405                    g[i].setChecked(false);
20406                }
20407            }
20408        }
20409    }
20410
20411    return {
20412
20413        /**
20414         * Hides all menus that are currently visible
20415         */
20416        hideAll : function(){
20417             hideAll();  
20418        },
20419
20420        // private
20421        register : function(menu){
20422            if(!menus){
20423                init();
20424            }
20425            menus[menu.id] = menu;
20426            menu.on("beforehide", onBeforeHide);
20427            menu.on("hide", onHide);
20428            menu.on("beforeshow", onBeforeShow);
20429            menu.on("show", onShow);
20430            var g = menu.group;
20431            if(g && menu.events["checkchange"]){
20432                if(!groups[g]){
20433                    groups[g] = [];
20434                }
20435                groups[g].push(menu);
20436                menu.on("checkchange", onCheck);
20437            }
20438        },
20439
20440         /**
20441          * Returns a {@link Roo.menu.Menu} object
20442          * @param {String/Object} menu The string menu id, an existing menu object reference, or a Menu config that will
20443          * be used to generate and return a new Menu instance.
20444          */
20445        get : function(menu){
20446            if(typeof menu == "string"){ // menu id
20447                return menus[menu];
20448            }else if(menu.events){  // menu instance
20449                return menu;
20450            }else if(typeof menu.length == 'number'){ // array of menu items?
20451                return new Roo.menu.Menu({items:menu});
20452            }else{ // otherwise, must be a config
20453                return new Roo.menu.Menu(menu);
20454            }
20455        },
20456
20457        // private
20458        unregister : function(menu){
20459            delete menus[menu.id];
20460            menu.un("beforehide", onBeforeHide);
20461            menu.un("hide", onHide);
20462            menu.un("beforeshow", onBeforeShow);
20463            menu.un("show", onShow);
20464            var g = menu.group;
20465            if(g && menu.events["checkchange"]){
20466                groups[g].remove(menu);
20467                menu.un("checkchange", onCheck);
20468            }
20469        },
20470
20471        // private
20472        registerCheckable : function(menuItem){
20473            var g = menuItem.group;
20474            if(g){
20475                if(!groups[g]){
20476                    groups[g] = [];
20477                }
20478                groups[g].push(menuItem);
20479                menuItem.on("beforecheckchange", onBeforeCheck);
20480            }
20481        },
20482
20483        // private
20484        unregisterCheckable : function(menuItem){
20485            var g = menuItem.group;
20486            if(g){
20487                groups[g].remove(menuItem);
20488                menuItem.un("beforecheckchange", onBeforeCheck);
20489            }
20490        }
20491    };
20492 }();/*
20493  * Based on:
20494  * Ext JS Library 1.1.1
20495  * Copyright(c) 2006-2007, Ext JS, LLC.
20496  *
20497  * Originally Released Under LGPL - original licence link has changed is not relivant.
20498  *
20499  * Fork - LGPL
20500  * <script type="text/javascript">
20501  */
20502  
20503
20504 /**
20505  * @class Roo.menu.BaseItem
20506  * @extends Roo.Component
20507  * The base class for all items that render into menus.  BaseItem provides default rendering, activated state
20508  * management and base configuration options shared by all menu components.
20509  * @constructor
20510  * Creates a new BaseItem
20511  * @param {Object} config Configuration options
20512  */
20513 Roo.menu.BaseItem = function(config){
20514     Roo.menu.BaseItem.superclass.constructor.call(this, config);
20515
20516     this.addEvents({
20517         /**
20518          * @event click
20519          * Fires when this item is clicked
20520          * @param {Roo.menu.BaseItem} this
20521          * @param {Roo.EventObject} e
20522          */
20523         click: true,
20524         /**
20525          * @event activate
20526          * Fires when this item is activated
20527          * @param {Roo.menu.BaseItem} this
20528          */
20529         activate : true,
20530         /**
20531          * @event deactivate
20532          * Fires when this item is deactivated
20533          * @param {Roo.menu.BaseItem} this
20534          */
20535         deactivate : true
20536     });
20537
20538     if(this.handler){
20539         this.on("click", this.handler, this.scope, true);
20540     }
20541 };
20542
20543 Roo.extend(Roo.menu.BaseItem, Roo.Component, {
20544     /**
20545      * @cfg {Function} handler
20546      * A function that will handle the click event of this menu item (defaults to undefined)
20547      */
20548     /**
20549      * @cfg {Boolean} canActivate True if this item can be visually activated (defaults to false)
20550      */
20551     canActivate : false,
20552     
20553      /**
20554      * @cfg {Boolean} hidden True to prevent creation of this menu item (defaults to false)
20555      */
20556     hidden: false,
20557     
20558     /**
20559      * @cfg {String} activeClass The CSS class to use when the item becomes activated (defaults to "x-menu-item-active")
20560      */
20561     activeClass : "x-menu-item-active",
20562     /**
20563      * @cfg {Boolean} hideOnClick True to hide the containing menu after this item is clicked (defaults to true)
20564      */
20565     hideOnClick : true,
20566     /**
20567      * @cfg {Number} hideDelay Length of time in milliseconds to wait before hiding after a click (defaults to 100)
20568      */
20569     hideDelay : 100,
20570
20571     // private
20572     ctype: "Roo.menu.BaseItem",
20573
20574     // private
20575     actionMode : "container",
20576
20577     // private
20578     render : function(container, parentMenu){
20579         this.parentMenu = parentMenu;
20580         Roo.menu.BaseItem.superclass.render.call(this, container);
20581         this.container.menuItemId = this.id;
20582     },
20583
20584     // private
20585     onRender : function(container, position){
20586         this.el = Roo.get(this.el);
20587         container.dom.appendChild(this.el.dom);
20588     },
20589
20590     // private
20591     onClick : function(e){
20592         if(!this.disabled && this.fireEvent("click", this, e) !== false
20593                 && this.parentMenu.fireEvent("itemclick", this, e) !== false){
20594             this.handleClick(e);
20595         }else{
20596             e.stopEvent();
20597         }
20598     },
20599
20600     // private
20601     activate : function(){
20602         if(this.disabled){
20603             return false;
20604         }
20605         var li = this.container;
20606         li.addClass(this.activeClass);
20607         this.region = li.getRegion().adjust(2, 2, -2, -2);
20608         this.fireEvent("activate", this);
20609         return true;
20610     },
20611
20612     // private
20613     deactivate : function(){
20614         this.container.removeClass(this.activeClass);
20615         this.fireEvent("deactivate", this);
20616     },
20617
20618     // private
20619     shouldDeactivate : function(e){
20620         return !this.region || !this.region.contains(e.getPoint());
20621     },
20622
20623     // private
20624     handleClick : function(e){
20625         if(this.hideOnClick){
20626             this.parentMenu.hide.defer(this.hideDelay, this.parentMenu, [true]);
20627         }
20628     },
20629
20630     // private
20631     expandMenu : function(autoActivate){
20632         // do nothing
20633     },
20634
20635     // private
20636     hideMenu : function(){
20637         // do nothing
20638     }
20639 });/*
20640  * Based on:
20641  * Ext JS Library 1.1.1
20642  * Copyright(c) 2006-2007, Ext JS, LLC.
20643  *
20644  * Originally Released Under LGPL - original licence link has changed is not relivant.
20645  *
20646  * Fork - LGPL
20647  * <script type="text/javascript">
20648  */
20649  
20650 /**
20651  * @class Roo.menu.Adapter
20652  * @extends Roo.menu.BaseItem
20653  * 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.
20654  * It provides basic rendering, activation management and enable/disable logic required to work in menus.
20655  * @constructor
20656  * Creates a new Adapter
20657  * @param {Object} config Configuration options
20658  */
20659 Roo.menu.Adapter = function(component, config){
20660     Roo.menu.Adapter.superclass.constructor.call(this, config);
20661     this.component = component;
20662 };
20663 Roo.extend(Roo.menu.Adapter, Roo.menu.BaseItem, {
20664     // private
20665     canActivate : true,
20666
20667     // private
20668     onRender : function(container, position){
20669         this.component.render(container);
20670         this.el = this.component.getEl();
20671     },
20672
20673     // private
20674     activate : function(){
20675         if(this.disabled){
20676             return false;
20677         }
20678         this.component.focus();
20679         this.fireEvent("activate", this);
20680         return true;
20681     },
20682
20683     // private
20684     deactivate : function(){
20685         this.fireEvent("deactivate", this);
20686     },
20687
20688     // private
20689     disable : function(){
20690         this.component.disable();
20691         Roo.menu.Adapter.superclass.disable.call(this);
20692     },
20693
20694     // private
20695     enable : function(){
20696         this.component.enable();
20697         Roo.menu.Adapter.superclass.enable.call(this);
20698     }
20699 });/*
20700  * Based on:
20701  * Ext JS Library 1.1.1
20702  * Copyright(c) 2006-2007, Ext JS, LLC.
20703  *
20704  * Originally Released Under LGPL - original licence link has changed is not relivant.
20705  *
20706  * Fork - LGPL
20707  * <script type="text/javascript">
20708  */
20709
20710 /**
20711  * @class Roo.menu.TextItem
20712  * @extends Roo.menu.BaseItem
20713  * Adds a static text string to a menu, usually used as either a heading or group separator.
20714  * Note: old style constructor with text is still supported.
20715  * 
20716  * @constructor
20717  * Creates a new TextItem
20718  * @param {Object} cfg Configuration
20719  */
20720 Roo.menu.TextItem = function(cfg){
20721     if (typeof(cfg) == 'string') {
20722         this.text = cfg;
20723     } else {
20724         Roo.apply(this,cfg);
20725     }
20726     
20727     Roo.menu.TextItem.superclass.constructor.call(this);
20728 };
20729
20730 Roo.extend(Roo.menu.TextItem, Roo.menu.BaseItem, {
20731     /**
20732      * @cfg {Boolean} text Text to show on item.
20733      */
20734     text : '',
20735     
20736     /**
20737      * @cfg {Boolean} hideOnClick True to hide the containing menu after this item is clicked (defaults to false)
20738      */
20739     hideOnClick : false,
20740     /**
20741      * @cfg {String} itemCls The default CSS class to use for text items (defaults to "x-menu-text")
20742      */
20743     itemCls : "x-menu-text",
20744
20745     // private
20746     onRender : function(){
20747         var s = document.createElement("span");
20748         s.className = this.itemCls;
20749         s.innerHTML = this.text;
20750         this.el = s;
20751         Roo.menu.TextItem.superclass.onRender.apply(this, arguments);
20752     }
20753 });/*
20754  * Based on:
20755  * Ext JS Library 1.1.1
20756  * Copyright(c) 2006-2007, Ext JS, LLC.
20757  *
20758  * Originally Released Under LGPL - original licence link has changed is not relivant.
20759  *
20760  * Fork - LGPL
20761  * <script type="text/javascript">
20762  */
20763
20764 /**
20765  * @class Roo.menu.Separator
20766  * @extends Roo.menu.BaseItem
20767  * Adds a separator bar to a menu, used to divide logical groups of menu items. Generally you will
20768  * add one of these by using "-" in you call to add() or in your items config rather than creating one directly.
20769  * @constructor
20770  * @param {Object} config Configuration options
20771  */
20772 Roo.menu.Separator = function(config){
20773     Roo.menu.Separator.superclass.constructor.call(this, config);
20774 };
20775
20776 Roo.extend(Roo.menu.Separator, Roo.menu.BaseItem, {
20777     /**
20778      * @cfg {String} itemCls The default CSS class to use for separators (defaults to "x-menu-sep")
20779      */
20780     itemCls : "x-menu-sep",
20781     /**
20782      * @cfg {Boolean} hideOnClick True to hide the containing menu after this item is clicked (defaults to false)
20783      */
20784     hideOnClick : false,
20785
20786     // private
20787     onRender : function(li){
20788         var s = document.createElement("span");
20789         s.className = this.itemCls;
20790         s.innerHTML = "&#160;";
20791         this.el = s;
20792         li.addClass("x-menu-sep-li");
20793         Roo.menu.Separator.superclass.onRender.apply(this, arguments);
20794     }
20795 });/*
20796  * Based on:
20797  * Ext JS Library 1.1.1
20798  * Copyright(c) 2006-2007, Ext JS, LLC.
20799  *
20800  * Originally Released Under LGPL - original licence link has changed is not relivant.
20801  *
20802  * Fork - LGPL
20803  * <script type="text/javascript">
20804  */
20805 /**
20806  * @class Roo.menu.Item
20807  * @extends Roo.menu.BaseItem
20808  * A base class for all menu items that require menu-related functionality (like sub-menus) and are not static
20809  * display items.  Item extends the base functionality of {@link Roo.menu.BaseItem} by adding menu-specific
20810  * activation and click handling.
20811  * @constructor
20812  * Creates a new Item
20813  * @param {Object} config Configuration options
20814  */
20815 Roo.menu.Item = function(config){
20816     Roo.menu.Item.superclass.constructor.call(this, config);
20817     if(this.menu){
20818         this.menu = Roo.menu.MenuMgr.get(this.menu);
20819     }
20820 };
20821 Roo.extend(Roo.menu.Item, Roo.menu.BaseItem, {
20822     
20823     /**
20824      * @cfg {String} text
20825      * The text to show on the menu item.
20826      */
20827     text: '',
20828      /**
20829      * @cfg {String} HTML to render in menu
20830      * The text to show on the menu item (HTML version).
20831      */
20832     html: '',
20833     /**
20834      * @cfg {String} icon
20835      * The path to an icon to display in this menu item (defaults to Roo.BLANK_IMAGE_URL)
20836      */
20837     icon: undefined,
20838     /**
20839      * @cfg {String} itemCls The default CSS class to use for menu items (defaults to "x-menu-item")
20840      */
20841     itemCls : "x-menu-item",
20842     /**
20843      * @cfg {Boolean} canActivate True if this item can be visually activated (defaults to true)
20844      */
20845     canActivate : true,
20846     /**
20847      * @cfg {Number} showDelay Length of time in milliseconds to wait before showing this item (defaults to 200)
20848      */
20849     showDelay: 200,
20850     // doc'd in BaseItem
20851     hideDelay: 200,
20852
20853     // private
20854     ctype: "Roo.menu.Item",
20855     
20856     // private
20857     onRender : function(container, position){
20858         var el = document.createElement("a");
20859         el.hideFocus = true;
20860         el.unselectable = "on";
20861         el.href = this.href || "#";
20862         if(this.hrefTarget){
20863             el.target = this.hrefTarget;
20864         }
20865         el.className = this.itemCls + (this.menu ?  " x-menu-item-arrow" : "") + (this.cls ?  " " + this.cls : "");
20866         
20867         var html = this.html.length ? this.html  : String.format('{0}',this.text);
20868         
20869         el.innerHTML = String.format(
20870                 '<img src="{0}" class="x-menu-item-icon {1}" />' + html,
20871                 this.icon || Roo.BLANK_IMAGE_URL, this.iconCls || '');
20872         this.el = el;
20873         Roo.menu.Item.superclass.onRender.call(this, container, position);
20874     },
20875
20876     /**
20877      * Sets the text to display in this menu item
20878      * @param {String} text The text to display
20879      * @param {Boolean} isHTML true to indicate text is pure html.
20880      */
20881     setText : function(text, isHTML){
20882         if (isHTML) {
20883             this.html = text;
20884         } else {
20885             this.text = text;
20886             this.html = '';
20887         }
20888         if(this.rendered){
20889             var html = this.html.length ? this.html  : String.format('{0}',this.text);
20890      
20891             this.el.update(String.format(
20892                 '<img src="{0}" class="x-menu-item-icon {2}">' + html,
20893                 this.icon || Roo.BLANK_IMAGE_URL, this.text, this.iconCls || ''));
20894             this.parentMenu.autoWidth();
20895         }
20896     },
20897
20898     // private
20899     handleClick : function(e){
20900         if(!this.href){ // if no link defined, stop the event automatically
20901             e.stopEvent();
20902         }
20903         Roo.menu.Item.superclass.handleClick.apply(this, arguments);
20904     },
20905
20906     // private
20907     activate : function(autoExpand){
20908         if(Roo.menu.Item.superclass.activate.apply(this, arguments)){
20909             this.focus();
20910             if(autoExpand){
20911                 this.expandMenu();
20912             }
20913         }
20914         return true;
20915     },
20916
20917     // private
20918     shouldDeactivate : function(e){
20919         if(Roo.menu.Item.superclass.shouldDeactivate.call(this, e)){
20920             if(this.menu && this.menu.isVisible()){
20921                 return !this.menu.getEl().getRegion().contains(e.getPoint());
20922             }
20923             return true;
20924         }
20925         return false;
20926     },
20927
20928     // private
20929     deactivate : function(){
20930         Roo.menu.Item.superclass.deactivate.apply(this, arguments);
20931         this.hideMenu();
20932     },
20933
20934     // private
20935     expandMenu : function(autoActivate){
20936         if(!this.disabled && this.menu){
20937             clearTimeout(this.hideTimer);
20938             delete this.hideTimer;
20939             if(!this.menu.isVisible() && !this.showTimer){
20940                 this.showTimer = this.deferExpand.defer(this.showDelay, this, [autoActivate]);
20941             }else if (this.menu.isVisible() && autoActivate){
20942                 this.menu.tryActivate(0, 1);
20943             }
20944         }
20945     },
20946
20947     // private
20948     deferExpand : function(autoActivate){
20949         delete this.showTimer;
20950         this.menu.show(this.container, this.parentMenu.subMenuAlign || "tl-tr?", this.parentMenu);
20951         if(autoActivate){
20952             this.menu.tryActivate(0, 1);
20953         }
20954     },
20955
20956     // private
20957     hideMenu : function(){
20958         clearTimeout(this.showTimer);
20959         delete this.showTimer;
20960         if(!this.hideTimer && this.menu && this.menu.isVisible()){
20961             this.hideTimer = this.deferHide.defer(this.hideDelay, this);
20962         }
20963     },
20964
20965     // private
20966     deferHide : function(){
20967         delete this.hideTimer;
20968         this.menu.hide();
20969     }
20970 });/*
20971  * Based on:
20972  * Ext JS Library 1.1.1
20973  * Copyright(c) 2006-2007, Ext JS, LLC.
20974  *
20975  * Originally Released Under LGPL - original licence link has changed is not relivant.
20976  *
20977  * Fork - LGPL
20978  * <script type="text/javascript">
20979  */
20980  
20981 /**
20982  * @class Roo.menu.CheckItem
20983  * @extends Roo.menu.Item
20984  * Adds a menu item that contains a checkbox by default, but can also be part of a radio group.
20985  * @constructor
20986  * Creates a new CheckItem
20987  * @param {Object} config Configuration options
20988  */
20989 Roo.menu.CheckItem = function(config){
20990     Roo.menu.CheckItem.superclass.constructor.call(this, config);
20991     this.addEvents({
20992         /**
20993          * @event beforecheckchange
20994          * Fires before the checked value is set, providing an opportunity to cancel if needed
20995          * @param {Roo.menu.CheckItem} this
20996          * @param {Boolean} checked The new checked value that will be set
20997          */
20998         "beforecheckchange" : true,
20999         /**
21000          * @event checkchange
21001          * Fires after the checked value has been set
21002          * @param {Roo.menu.CheckItem} this
21003          * @param {Boolean} checked The checked value that was set
21004          */
21005         "checkchange" : true
21006     });
21007     if(this.checkHandler){
21008         this.on('checkchange', this.checkHandler, this.scope);
21009     }
21010 };
21011 Roo.extend(Roo.menu.CheckItem, Roo.menu.Item, {
21012     /**
21013      * @cfg {String} group
21014      * All check items with the same group name will automatically be grouped into a single-select
21015      * radio button group (defaults to '')
21016      */
21017     /**
21018      * @cfg {String} itemCls The default CSS class to use for check items (defaults to "x-menu-item x-menu-check-item")
21019      */
21020     itemCls : "x-menu-item x-menu-check-item",
21021     /**
21022      * @cfg {String} groupClass The default CSS class to use for radio group check items (defaults to "x-menu-group-item")
21023      */
21024     groupClass : "x-menu-group-item",
21025
21026     /**
21027      * @cfg {Boolean} checked True to initialize this checkbox as checked (defaults to false).  Note that
21028      * if this checkbox is part of a radio group (group = true) only the last item in the group that is
21029      * initialized with checked = true will be rendered as checked.
21030      */
21031     checked: false,
21032
21033     // private
21034     ctype: "Roo.menu.CheckItem",
21035
21036     // private
21037     onRender : function(c){
21038         Roo.menu.CheckItem.superclass.onRender.apply(this, arguments);
21039         if(this.group){
21040             this.el.addClass(this.groupClass);
21041         }
21042         Roo.menu.MenuMgr.registerCheckable(this);
21043         if(this.checked){
21044             this.checked = false;
21045             this.setChecked(true, true);
21046         }
21047     },
21048
21049     // private
21050     destroy : function(){
21051         if(this.rendered){
21052             Roo.menu.MenuMgr.unregisterCheckable(this);
21053         }
21054         Roo.menu.CheckItem.superclass.destroy.apply(this, arguments);
21055     },
21056
21057     /**
21058      * Set the checked state of this item
21059      * @param {Boolean} checked The new checked value
21060      * @param {Boolean} suppressEvent (optional) True to prevent the checkchange event from firing (defaults to false)
21061      */
21062     setChecked : function(state, suppressEvent){
21063         if(this.checked != state && this.fireEvent("beforecheckchange", this, state) !== false){
21064             if(this.container){
21065                 this.container[state ? "addClass" : "removeClass"]("x-menu-item-checked");
21066             }
21067             this.checked = state;
21068             if(suppressEvent !== true){
21069                 this.fireEvent("checkchange", this, state);
21070             }
21071         }
21072     },
21073
21074     // private
21075     handleClick : function(e){
21076        if(!this.disabled && !(this.checked && this.group)){// disable unselect on radio item
21077            this.setChecked(!this.checked);
21078        }
21079        Roo.menu.CheckItem.superclass.handleClick.apply(this, arguments);
21080     }
21081 });/*
21082  * Based on:
21083  * Ext JS Library 1.1.1
21084  * Copyright(c) 2006-2007, Ext JS, LLC.
21085  *
21086  * Originally Released Under LGPL - original licence link has changed is not relivant.
21087  *
21088  * Fork - LGPL
21089  * <script type="text/javascript">
21090  */
21091  
21092 /**
21093  * @class Roo.menu.DateItem
21094  * @extends Roo.menu.Adapter
21095  * A menu item that wraps the {@link Roo.DatPicker} component.
21096  * @constructor
21097  * Creates a new DateItem
21098  * @param {Object} config Configuration options
21099  */
21100 Roo.menu.DateItem = function(config){
21101     Roo.menu.DateItem.superclass.constructor.call(this, new Roo.DatePicker(config), config);
21102     /** The Roo.DatePicker object @type Roo.DatePicker */
21103     this.picker = this.component;
21104     this.addEvents({select: true});
21105     
21106     this.picker.on("render", function(picker){
21107         picker.getEl().swallowEvent("click");
21108         picker.container.addClass("x-menu-date-item");
21109     });
21110
21111     this.picker.on("select", this.onSelect, this);
21112 };
21113
21114 Roo.extend(Roo.menu.DateItem, Roo.menu.Adapter, {
21115     // private
21116     onSelect : function(picker, date){
21117         this.fireEvent("select", this, date, picker);
21118         Roo.menu.DateItem.superclass.handleClick.call(this);
21119     }
21120 });/*
21121  * Based on:
21122  * Ext JS Library 1.1.1
21123  * Copyright(c) 2006-2007, Ext JS, LLC.
21124  *
21125  * Originally Released Under LGPL - original licence link has changed is not relivant.
21126  *
21127  * Fork - LGPL
21128  * <script type="text/javascript">
21129  */
21130  
21131 /**
21132  * @class Roo.menu.ColorItem
21133  * @extends Roo.menu.Adapter
21134  * A menu item that wraps the {@link Roo.ColorPalette} component.
21135  * @constructor
21136  * Creates a new ColorItem
21137  * @param {Object} config Configuration options
21138  */
21139 Roo.menu.ColorItem = function(config){
21140     Roo.menu.ColorItem.superclass.constructor.call(this, new Roo.ColorPalette(config), config);
21141     /** The Roo.ColorPalette object @type Roo.ColorPalette */
21142     this.palette = this.component;
21143     this.relayEvents(this.palette, ["select"]);
21144     if(this.selectHandler){
21145         this.on('select', this.selectHandler, this.scope);
21146     }
21147 };
21148 Roo.extend(Roo.menu.ColorItem, Roo.menu.Adapter);/*
21149  * Based on:
21150  * Ext JS Library 1.1.1
21151  * Copyright(c) 2006-2007, Ext JS, LLC.
21152  *
21153  * Originally Released Under LGPL - original licence link has changed is not relivant.
21154  *
21155  * Fork - LGPL
21156  * <script type="text/javascript">
21157  */
21158  
21159
21160 /**
21161  * @class Roo.menu.DateMenu
21162  * @extends Roo.menu.Menu
21163  * A menu containing a {@link Roo.menu.DateItem} component (which provides a date picker).
21164  * @constructor
21165  * Creates a new DateMenu
21166  * @param {Object} config Configuration options
21167  */
21168 Roo.menu.DateMenu = function(config){
21169     Roo.menu.DateMenu.superclass.constructor.call(this, config);
21170     this.plain = true;
21171     var di = new Roo.menu.DateItem(config);
21172     this.add(di);
21173     /**
21174      * The {@link Roo.DatePicker} instance for this DateMenu
21175      * @type DatePicker
21176      */
21177     this.picker = di.picker;
21178     /**
21179      * @event select
21180      * @param {DatePicker} picker
21181      * @param {Date} date
21182      */
21183     this.relayEvents(di, ["select"]);
21184     this.on('beforeshow', function(){
21185         if(this.picker){
21186             this.picker.hideMonthPicker(false);
21187         }
21188     }, this);
21189 };
21190 Roo.extend(Roo.menu.DateMenu, Roo.menu.Menu, {
21191     cls:'x-date-menu'
21192 });/*
21193  * Based on:
21194  * Ext JS Library 1.1.1
21195  * Copyright(c) 2006-2007, Ext JS, LLC.
21196  *
21197  * Originally Released Under LGPL - original licence link has changed is not relivant.
21198  *
21199  * Fork - LGPL
21200  * <script type="text/javascript">
21201  */
21202  
21203
21204 /**
21205  * @class Roo.menu.ColorMenu
21206  * @extends Roo.menu.Menu
21207  * A menu containing a {@link Roo.menu.ColorItem} component (which provides a basic color picker).
21208  * @constructor
21209  * Creates a new ColorMenu
21210  * @param {Object} config Configuration options
21211  */
21212 Roo.menu.ColorMenu = function(config){
21213     Roo.menu.ColorMenu.superclass.constructor.call(this, config);
21214     this.plain = true;
21215     var ci = new Roo.menu.ColorItem(config);
21216     this.add(ci);
21217     /**
21218      * The {@link Roo.ColorPalette} instance for this ColorMenu
21219      * @type ColorPalette
21220      */
21221     this.palette = ci.palette;
21222     /**
21223      * @event select
21224      * @param {ColorPalette} palette
21225      * @param {String} color
21226      */
21227     this.relayEvents(ci, ["select"]);
21228 };
21229 Roo.extend(Roo.menu.ColorMenu, Roo.menu.Menu);/*
21230  * Based on:
21231  * Ext JS Library 1.1.1
21232  * Copyright(c) 2006-2007, Ext JS, LLC.
21233  *
21234  * Originally Released Under LGPL - original licence link has changed is not relivant.
21235  *
21236  * Fork - LGPL
21237  * <script type="text/javascript">
21238  */
21239  
21240 /**
21241  * @class Roo.form.Field
21242  * @extends Roo.BoxComponent
21243  * Base class for form fields that provides default event handling, sizing, value handling and other functionality.
21244  * @constructor
21245  * Creates a new Field
21246  * @param {Object} config Configuration options
21247  */
21248 Roo.form.Field = function(config){
21249     Roo.form.Field.superclass.constructor.call(this, config);
21250 };
21251
21252 Roo.extend(Roo.form.Field, Roo.BoxComponent,  {
21253     /**
21254      * @cfg {String} fieldLabel Label to use when rendering a form.
21255      */
21256        /**
21257      * @cfg {String} qtip Mouse over tip
21258      */
21259      
21260     /**
21261      * @cfg {String} invalidClass The CSS class to use when marking a field invalid (defaults to "x-form-invalid")
21262      */
21263     invalidClass : "x-form-invalid",
21264     /**
21265      * @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")
21266      */
21267     invalidText : "The value in this field is invalid",
21268     /**
21269      * @cfg {String} focusClass The CSS class to use when the field receives focus (defaults to "x-form-focus")
21270      */
21271     focusClass : "x-form-focus",
21272     /**
21273      * @cfg {String/Boolean} validationEvent The event that should initiate field validation. Set to false to disable
21274       automatic validation (defaults to "keyup").
21275      */
21276     validationEvent : "keyup",
21277     /**
21278      * @cfg {Boolean} validateOnBlur Whether the field should validate when it loses focus (defaults to true).
21279      */
21280     validateOnBlur : true,
21281     /**
21282      * @cfg {Number} validationDelay The length of time in milliseconds after user input begins until validation is initiated (defaults to 250)
21283      */
21284     validationDelay : 250,
21285     /**
21286      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
21287      * {tag: "input", type: "text", size: "20", autocomplete: "off"})
21288      */
21289     defaultAutoCreate : {tag: "input", type: "text", size: "20", autocomplete: "off"},
21290     /**
21291      * @cfg {String} fieldClass The default CSS class for the field (defaults to "x-form-field")
21292      */
21293     fieldClass : "x-form-field",
21294     /**
21295      * @cfg {String} msgTarget The location where error text should display.  Should be one of the following values (defaults to 'qtip'):
21296      *<pre>
21297 Value         Description
21298 -----------   ----------------------------------------------------------------------
21299 qtip          Display a quick tip when the user hovers over the field
21300 title         Display a default browser title attribute popup
21301 under         Add a block div beneath the field containing the error text
21302 side          Add an error icon to the right of the field with a popup on hover
21303 [element id]  Add the error text directly to the innerHTML of the specified element
21304 </pre>
21305      */
21306     msgTarget : 'qtip',
21307     /**
21308      * @cfg {String} msgFx <b>Experimental</b> The effect used when displaying a validation message under the field (defaults to 'normal').
21309      */
21310     msgFx : 'normal',
21311
21312     /**
21313      * @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.
21314      */
21315     readOnly : false,
21316
21317     /**
21318      * @cfg {Boolean} disabled True to disable the field (defaults to false).
21319      */
21320     disabled : false,
21321
21322     /**
21323      * @cfg {String} inputType The type attribute for input fields -- e.g. radio, text, password (defaults to "text").
21324      */
21325     inputType : undefined,
21326     
21327     /**
21328      * @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).
21329          */
21330         tabIndex : undefined,
21331         
21332     // private
21333     isFormField : true,
21334
21335     // private
21336     hasFocus : false,
21337     /**
21338      * @property {Roo.Element} fieldEl
21339      * Element Containing the rendered Field (with label etc.)
21340      */
21341     /**
21342      * @cfg {Mixed} value A value to initialize this field with.
21343      */
21344     value : undefined,
21345
21346     /**
21347      * @cfg {String} name The field's HTML name attribute.
21348      */
21349     /**
21350      * @cfg {String} cls A CSS class to apply to the field's underlying element.
21351      */
21352
21353         // private ??
21354         initComponent : function(){
21355         Roo.form.Field.superclass.initComponent.call(this);
21356         this.addEvents({
21357             /**
21358              * @event focus
21359              * Fires when this field receives input focus.
21360              * @param {Roo.form.Field} this
21361              */
21362             focus : true,
21363             /**
21364              * @event blur
21365              * Fires when this field loses input focus.
21366              * @param {Roo.form.Field} this
21367              */
21368             blur : true,
21369             /**
21370              * @event specialkey
21371              * Fires when any key related to navigation (arrows, tab, enter, esc, etc.) is pressed.  You can check
21372              * {@link Roo.EventObject#getKey} to determine which key was pressed.
21373              * @param {Roo.form.Field} this
21374              * @param {Roo.EventObject} e The event object
21375              */
21376             specialkey : true,
21377             /**
21378              * @event change
21379              * Fires just before the field blurs if the field value has changed.
21380              * @param {Roo.form.Field} this
21381              * @param {Mixed} newValue The new value
21382              * @param {Mixed} oldValue The original value
21383              */
21384             change : true,
21385             /**
21386              * @event invalid
21387              * Fires after the field has been marked as invalid.
21388              * @param {Roo.form.Field} this
21389              * @param {String} msg The validation message
21390              */
21391             invalid : true,
21392             /**
21393              * @event valid
21394              * Fires after the field has been validated with no errors.
21395              * @param {Roo.form.Field} this
21396              */
21397             valid : true,
21398              /**
21399              * @event keyup
21400              * Fires after the key up
21401              * @param {Roo.form.Field} this
21402              * @param {Roo.EventObject}  e The event Object
21403              */
21404             keyup : true
21405         });
21406     },
21407
21408     /**
21409      * Returns the name attribute of the field if available
21410      * @return {String} name The field name
21411      */
21412     getName: function(){
21413          return this.rendered && this.el.dom.name ? this.el.dom.name : (this.hiddenName || '');
21414     },
21415
21416     // private
21417     onRender : function(ct, position){
21418         Roo.form.Field.superclass.onRender.call(this, ct, position);
21419         if(!this.el){
21420             var cfg = this.getAutoCreate();
21421             if(!cfg.name){
21422                 cfg.name = typeof(this.name) == 'undefined' ? this.id : this.name;
21423             }
21424             if (!cfg.name.length) {
21425                 delete cfg.name;
21426             }
21427             if(this.inputType){
21428                 cfg.type = this.inputType;
21429             }
21430             this.el = ct.createChild(cfg, position);
21431         }
21432         var type = this.el.dom.type;
21433         if(type){
21434             if(type == 'password'){
21435                 type = 'text';
21436             }
21437             this.el.addClass('x-form-'+type);
21438         }
21439         if(this.readOnly){
21440             this.el.dom.readOnly = true;
21441         }
21442         if(this.tabIndex !== undefined){
21443             this.el.dom.setAttribute('tabIndex', this.tabIndex);
21444         }
21445
21446         this.el.addClass([this.fieldClass, this.cls]);
21447         this.initValue();
21448     },
21449
21450     /**
21451      * Apply the behaviors of this component to an existing element. <b>This is used instead of render().</b>
21452      * @param {String/HTMLElement/Element} el The id of the node, a DOM node or an existing Element
21453      * @return {Roo.form.Field} this
21454      */
21455     applyTo : function(target){
21456         this.allowDomMove = false;
21457         this.el = Roo.get(target);
21458         this.render(this.el.dom.parentNode);
21459         return this;
21460     },
21461
21462     // private
21463     initValue : function(){
21464         if(this.value !== undefined){
21465             this.setValue(this.value);
21466         }else if(this.el.dom.value.length > 0){
21467             this.setValue(this.el.dom.value);
21468         }
21469     },
21470
21471     /**
21472      * Returns true if this field has been changed since it was originally loaded and is not disabled.
21473      */
21474     isDirty : function() {
21475         if(this.disabled) {
21476             return false;
21477         }
21478         return String(this.getValue()) !== String(this.originalValue);
21479     },
21480
21481     // private
21482     afterRender : function(){
21483         Roo.form.Field.superclass.afterRender.call(this);
21484         this.initEvents();
21485     },
21486
21487     // private
21488     fireKey : function(e){
21489         //Roo.log('field ' + e.getKey());
21490         if(e.isNavKeyPress()){
21491             this.fireEvent("specialkey", this, e);
21492         }
21493     },
21494
21495     /**
21496      * Resets the current field value to the originally loaded value and clears any validation messages
21497      */
21498     reset : function(){
21499         this.setValue(this.originalValue);
21500         this.clearInvalid();
21501     },
21502
21503     // private
21504     initEvents : function(){
21505         // safari killled keypress - so keydown is now used..
21506         this.el.on("keydown" , this.fireKey,  this);
21507         this.el.on("focus", this.onFocus,  this);
21508         this.el.on("blur", this.onBlur,  this);
21509         this.el.relayEvent('keyup', this);
21510
21511         // reference to original value for reset
21512         this.originalValue = this.getValue();
21513     },
21514
21515     // private
21516     onFocus : function(){
21517         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
21518             this.el.addClass(this.focusClass);
21519         }
21520         if(!this.hasFocus){
21521             this.hasFocus = true;
21522             this.startValue = this.getValue();
21523             this.fireEvent("focus", this);
21524         }
21525     },
21526
21527     beforeBlur : Roo.emptyFn,
21528
21529     // private
21530     onBlur : function(){
21531         this.beforeBlur();
21532         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
21533             this.el.removeClass(this.focusClass);
21534         }
21535         this.hasFocus = false;
21536         if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
21537             this.validate();
21538         }
21539         var v = this.getValue();
21540         if(String(v) !== String(this.startValue)){
21541             this.fireEvent('change', this, v, this.startValue);
21542         }
21543         this.fireEvent("blur", this);
21544     },
21545
21546     /**
21547      * Returns whether or not the field value is currently valid
21548      * @param {Boolean} preventMark True to disable marking the field invalid
21549      * @return {Boolean} True if the value is valid, else false
21550      */
21551     isValid : function(preventMark){
21552         if(this.disabled){
21553             return true;
21554         }
21555         var restore = this.preventMark;
21556         this.preventMark = preventMark === true;
21557         var v = this.validateValue(this.processValue(this.getRawValue()));
21558         this.preventMark = restore;
21559         return v;
21560     },
21561
21562     /**
21563      * Validates the field value
21564      * @return {Boolean} True if the value is valid, else false
21565      */
21566     validate : function(){
21567         if(this.disabled || this.validateValue(this.processValue(this.getRawValue()))){
21568             this.clearInvalid();
21569             return true;
21570         }
21571         return false;
21572     },
21573
21574     processValue : function(value){
21575         return value;
21576     },
21577
21578     // private
21579     // Subclasses should provide the validation implementation by overriding this
21580     validateValue : function(value){
21581         return true;
21582     },
21583
21584     /**
21585      * Mark this field as invalid
21586      * @param {String} msg The validation message
21587      */
21588     markInvalid : function(msg){
21589         if(!this.rendered || this.preventMark){ // not rendered
21590             return;
21591         }
21592         this.el.addClass(this.invalidClass);
21593         msg = msg || this.invalidText;
21594         switch(this.msgTarget){
21595             case 'qtip':
21596                 this.el.dom.qtip = msg;
21597                 this.el.dom.qclass = 'x-form-invalid-tip';
21598                 if(Roo.QuickTips){ // fix for floating editors interacting with DND
21599                     Roo.QuickTips.enable();
21600                 }
21601                 break;
21602             case 'title':
21603                 this.el.dom.title = msg;
21604                 break;
21605             case 'under':
21606                 if(!this.errorEl){
21607                     var elp = this.el.findParent('.x-form-element', 5, true);
21608                     this.errorEl = elp.createChild({cls:'x-form-invalid-msg'});
21609                     this.errorEl.setWidth(elp.getWidth(true)-20);
21610                 }
21611                 this.errorEl.update(msg);
21612                 Roo.form.Field.msgFx[this.msgFx].show(this.errorEl, this);
21613                 break;
21614             case 'side':
21615                 if(!this.errorIcon){
21616                     var elp = this.el.findParent('.x-form-element', 5, true);
21617                     this.errorIcon = elp.createChild({cls:'x-form-invalid-icon'});
21618                 }
21619                 this.alignErrorIcon();
21620                 this.errorIcon.dom.qtip = msg;
21621                 this.errorIcon.dom.qclass = 'x-form-invalid-tip';
21622                 this.errorIcon.show();
21623                 this.on('resize', this.alignErrorIcon, this);
21624                 break;
21625             default:
21626                 var t = Roo.getDom(this.msgTarget);
21627                 t.innerHTML = msg;
21628                 t.style.display = this.msgDisplay;
21629                 break;
21630         }
21631         this.fireEvent('invalid', this, msg);
21632     },
21633
21634     // private
21635     alignErrorIcon : function(){
21636         this.errorIcon.alignTo(this.el, 'tl-tr', [2, 0]);
21637     },
21638
21639     /**
21640      * Clear any invalid styles/messages for this field
21641      */
21642     clearInvalid : function(){
21643         if(!this.rendered || this.preventMark){ // not rendered
21644             return;
21645         }
21646         this.el.removeClass(this.invalidClass);
21647         switch(this.msgTarget){
21648             case 'qtip':
21649                 this.el.dom.qtip = '';
21650                 break;
21651             case 'title':
21652                 this.el.dom.title = '';
21653                 break;
21654             case 'under':
21655                 if(this.errorEl){
21656                     Roo.form.Field.msgFx[this.msgFx].hide(this.errorEl, this);
21657                 }
21658                 break;
21659             case 'side':
21660                 if(this.errorIcon){
21661                     this.errorIcon.dom.qtip = '';
21662                     this.errorIcon.hide();
21663                     this.un('resize', this.alignErrorIcon, this);
21664                 }
21665                 break;
21666             default:
21667                 var t = Roo.getDom(this.msgTarget);
21668                 t.innerHTML = '';
21669                 t.style.display = 'none';
21670                 break;
21671         }
21672         this.fireEvent('valid', this);
21673     },
21674
21675     /**
21676      * Returns the raw data value which may or may not be a valid, defined value.  To return a normalized value see {@link #getValue}.
21677      * @return {Mixed} value The field value
21678      */
21679     getRawValue : function(){
21680         var v = this.el.getValue();
21681         
21682         return v;
21683     },
21684
21685     /**
21686      * Returns the normalized data value (undefined or emptyText will be returned as '').  To return the raw value see {@link #getRawValue}.
21687      * @return {Mixed} value The field value
21688      */
21689     getValue : function(){
21690         var v = this.el.getValue();
21691          
21692         return v;
21693     },
21694
21695     /**
21696      * Sets the underlying DOM field's value directly, bypassing validation.  To set the value with validation see {@link #setValue}.
21697      * @param {Mixed} value The value to set
21698      */
21699     setRawValue : function(v){
21700         return this.el.dom.value = (v === null || v === undefined ? '' : v);
21701     },
21702
21703     /**
21704      * Sets a data value into the field and validates it.  To set the value directly without validation see {@link #setRawValue}.
21705      * @param {Mixed} value The value to set
21706      */
21707     setValue : function(v){
21708         this.value = v;
21709         if(this.rendered){
21710             this.el.dom.value = (v === null || v === undefined ? '' : v);
21711              this.validate();
21712         }
21713     },
21714
21715     adjustSize : function(w, h){
21716         var s = Roo.form.Field.superclass.adjustSize.call(this, w, h);
21717         s.width = this.adjustWidth(this.el.dom.tagName, s.width);
21718         return s;
21719     },
21720
21721     adjustWidth : function(tag, w){
21722         tag = tag.toLowerCase();
21723         if(typeof w == 'number' && Roo.isStrict && !Roo.isSafari){
21724             if(Roo.isIE && (tag == 'input' || tag == 'textarea')){
21725                 if(tag == 'input'){
21726                     return w + 2;
21727                 }
21728                 if(tag == 'textarea'){
21729                     return w-2;
21730                 }
21731             }else if(Roo.isOpera){
21732                 if(tag == 'input'){
21733                     return w + 2;
21734                 }
21735                 if(tag == 'textarea'){
21736                     return w-2;
21737                 }
21738             }
21739         }
21740         return w;
21741     }
21742 });
21743
21744
21745 // anything other than normal should be considered experimental
21746 Roo.form.Field.msgFx = {
21747     normal : {
21748         show: function(msgEl, f){
21749             msgEl.setDisplayed('block');
21750         },
21751
21752         hide : function(msgEl, f){
21753             msgEl.setDisplayed(false).update('');
21754         }
21755     },
21756
21757     slide : {
21758         show: function(msgEl, f){
21759             msgEl.slideIn('t', {stopFx:true});
21760         },
21761
21762         hide : function(msgEl, f){
21763             msgEl.slideOut('t', {stopFx:true,useDisplay:true});
21764         }
21765     },
21766
21767     slideRight : {
21768         show: function(msgEl, f){
21769             msgEl.fixDisplay();
21770             msgEl.alignTo(f.el, 'tl-tr');
21771             msgEl.slideIn('l', {stopFx:true});
21772         },
21773
21774         hide : function(msgEl, f){
21775             msgEl.slideOut('l', {stopFx:true,useDisplay:true});
21776         }
21777     }
21778 };/*
21779  * Based on:
21780  * Ext JS Library 1.1.1
21781  * Copyright(c) 2006-2007, Ext JS, LLC.
21782  *
21783  * Originally Released Under LGPL - original licence link has changed is not relivant.
21784  *
21785  * Fork - LGPL
21786  * <script type="text/javascript">
21787  */
21788  
21789
21790 /**
21791  * @class Roo.form.TextField
21792  * @extends Roo.form.Field
21793  * Basic text field.  Can be used as a direct replacement for traditional text inputs, or as the base
21794  * class for more sophisticated input controls (like {@link Roo.form.TextArea} and {@link Roo.form.ComboBox}).
21795  * @constructor
21796  * Creates a new TextField
21797  * @param {Object} config Configuration options
21798  */
21799 Roo.form.TextField = function(config){
21800     Roo.form.TextField.superclass.constructor.call(this, config);
21801     this.addEvents({
21802         /**
21803          * @event autosize
21804          * Fires when the autosize function is triggered.  The field may or may not have actually changed size
21805          * according to the default logic, but this event provides a hook for the developer to apply additional
21806          * logic at runtime to resize the field if needed.
21807              * @param {Roo.form.Field} this This text field
21808              * @param {Number} width The new field width
21809              */
21810         autosize : true
21811     });
21812 };
21813
21814 Roo.extend(Roo.form.TextField, Roo.form.Field,  {
21815     /**
21816      * @cfg {Boolean} grow True if this field should automatically grow and shrink to its content
21817      */
21818     grow : false,
21819     /**
21820      * @cfg {Number} growMin The minimum width to allow when grow = true (defaults to 30)
21821      */
21822     growMin : 30,
21823     /**
21824      * @cfg {Number} growMax The maximum width to allow when grow = true (defaults to 800)
21825      */
21826     growMax : 800,
21827     /**
21828      * @cfg {String} vtype A validation type name as defined in {@link Roo.form.VTypes} (defaults to null)
21829      */
21830     vtype : null,
21831     /**
21832      * @cfg {String} maskRe An input mask regular expression that will be used to filter keystrokes that don't match (defaults to null)
21833      */
21834     maskRe : null,
21835     /**
21836      * @cfg {Boolean} disableKeyFilter True to disable input keystroke filtering (defaults to false)
21837      */
21838     disableKeyFilter : false,
21839     /**
21840      * @cfg {Boolean} allowBlank False to validate that the value length > 0 (defaults to true)
21841      */
21842     allowBlank : true,
21843     /**
21844      * @cfg {Number} minLength Minimum input field length required (defaults to 0)
21845      */
21846     minLength : 0,
21847     /**
21848      * @cfg {Number} maxLength Maximum input field length allowed (defaults to Number.MAX_VALUE)
21849      */
21850     maxLength : Number.MAX_VALUE,
21851     /**
21852      * @cfg {String} minLengthText Error text to display if the minimum length validation fails (defaults to "The minimum length for this field is {minLength}")
21853      */
21854     minLengthText : "The minimum length for this field is {0}",
21855     /**
21856      * @cfg {String} maxLengthText Error text to display if the maximum length validation fails (defaults to "The maximum length for this field is {maxLength}")
21857      */
21858     maxLengthText : "The maximum length for this field is {0}",
21859     /**
21860      * @cfg {Boolean} selectOnFocus True to automatically select any existing field text when the field receives input focus (defaults to false)
21861      */
21862     selectOnFocus : false,
21863     /**
21864      * @cfg {String} blankText Error text to display if the allow blank validation fails (defaults to "This field is required")
21865      */
21866     blankText : "This field is required",
21867     /**
21868      * @cfg {Function} validator A custom validation function to be called during field validation (defaults to null).
21869      * If available, this function will be called only after the basic validators all return true, and will be passed the
21870      * current field value and expected to return boolean true if the value is valid or a string error message if invalid.
21871      */
21872     validator : null,
21873     /**
21874      * @cfg {RegExp} regex A JavaScript RegExp object to be tested against the field value during validation (defaults to null).
21875      * If available, this regex will be evaluated only after the basic validators all return true, and will be passed the
21876      * current field value.  If the test fails, the field will be marked invalid using {@link #regexText}.
21877      */
21878     regex : null,
21879     /**
21880      * @cfg {String} regexText The error text to display if {@link #regex} is used and the test fails during validation (defaults to "")
21881      */
21882     regexText : "",
21883     /**
21884      * @cfg {String} emptyText The default text to display in an empty field - placeholder... (defaults to null).
21885      */
21886     emptyText : null,
21887    
21888
21889     // private
21890     initEvents : function()
21891     {
21892         if (this.emptyText) {
21893             this.el.attr('placeholder', this.emptyText);
21894         }
21895         
21896         Roo.form.TextField.superclass.initEvents.call(this);
21897         if(this.validationEvent == 'keyup'){
21898             this.validationTask = new Roo.util.DelayedTask(this.validate, this);
21899             this.el.on('keyup', this.filterValidation, this);
21900         }
21901         else if(this.validationEvent !== false){
21902             this.el.on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
21903         }
21904         
21905         if(this.selectOnFocus){
21906             this.on("focus", this.preFocus, this);
21907             
21908         }
21909         if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
21910             this.el.on("keypress", this.filterKeys, this);
21911         }
21912         if(this.grow){
21913             this.el.on("keyup", this.onKeyUp,  this, {buffer:50});
21914             this.el.on("click", this.autoSize,  this);
21915         }
21916         if(this.el.is('input[type=password]') && Roo.isSafari){
21917             this.el.on('keydown', this.SafariOnKeyDown, this);
21918         }
21919     },
21920
21921     processValue : function(value){
21922         if(this.stripCharsRe){
21923             var newValue = value.replace(this.stripCharsRe, '');
21924             if(newValue !== value){
21925                 this.setRawValue(newValue);
21926                 return newValue;
21927             }
21928         }
21929         return value;
21930     },
21931
21932     filterValidation : function(e){
21933         if(!e.isNavKeyPress()){
21934             this.validationTask.delay(this.validationDelay);
21935         }
21936     },
21937
21938     // private
21939     onKeyUp : function(e){
21940         if(!e.isNavKeyPress()){
21941             this.autoSize();
21942         }
21943     },
21944
21945     /**
21946      * Resets the current field value to the originally-loaded value and clears any validation messages.
21947      *  
21948      */
21949     reset : function(){
21950         Roo.form.TextField.superclass.reset.call(this);
21951        
21952     },
21953
21954     
21955     // private
21956     preFocus : function(){
21957         
21958         if(this.selectOnFocus){
21959             this.el.dom.select();
21960         }
21961     },
21962
21963     
21964     // private
21965     filterKeys : function(e){
21966         var k = e.getKey();
21967         if(!Roo.isIE && (e.isNavKeyPress() || k == e.BACKSPACE || (k == e.DELETE && e.button == -1))){
21968             return;
21969         }
21970         var c = e.getCharCode(), cc = String.fromCharCode(c);
21971         if(Roo.isIE && (e.isSpecialKey() || !cc)){
21972             return;
21973         }
21974         if(!this.maskRe.test(cc)){
21975             e.stopEvent();
21976         }
21977     },
21978
21979     setValue : function(v){
21980         
21981         Roo.form.TextField.superclass.setValue.apply(this, arguments);
21982         
21983         this.autoSize();
21984     },
21985
21986     /**
21987      * Validates a value according to the field's validation rules and marks the field as invalid
21988      * if the validation fails
21989      * @param {Mixed} value The value to validate
21990      * @return {Boolean} True if the value is valid, else false
21991      */
21992     validateValue : function(value){
21993         if(value.length < 1)  { // if it's blank
21994              if(this.allowBlank){
21995                 this.clearInvalid();
21996                 return true;
21997              }else{
21998                 this.markInvalid(this.blankText);
21999                 return false;
22000              }
22001         }
22002         if(value.length < this.minLength){
22003             this.markInvalid(String.format(this.minLengthText, this.minLength));
22004             return false;
22005         }
22006         if(value.length > this.maxLength){
22007             this.markInvalid(String.format(this.maxLengthText, this.maxLength));
22008             return false;
22009         }
22010         if(this.vtype){
22011             var vt = Roo.form.VTypes;
22012             if(!vt[this.vtype](value, this)){
22013                 this.markInvalid(this.vtypeText || vt[this.vtype +'Text']);
22014                 return false;
22015             }
22016         }
22017         if(typeof this.validator == "function"){
22018             var msg = this.validator(value);
22019             if(msg !== true){
22020                 this.markInvalid(msg);
22021                 return false;
22022             }
22023         }
22024         if(this.regex && !this.regex.test(value)){
22025             this.markInvalid(this.regexText);
22026             return false;
22027         }
22028         return true;
22029     },
22030
22031     /**
22032      * Selects text in this field
22033      * @param {Number} start (optional) The index where the selection should start (defaults to 0)
22034      * @param {Number} end (optional) The index where the selection should end (defaults to the text length)
22035      */
22036     selectText : function(start, end){
22037         var v = this.getRawValue();
22038         if(v.length > 0){
22039             start = start === undefined ? 0 : start;
22040             end = end === undefined ? v.length : end;
22041             var d = this.el.dom;
22042             if(d.setSelectionRange){
22043                 d.setSelectionRange(start, end);
22044             }else if(d.createTextRange){
22045                 var range = d.createTextRange();
22046                 range.moveStart("character", start);
22047                 range.moveEnd("character", v.length-end);
22048                 range.select();
22049             }
22050         }
22051     },
22052
22053     /**
22054      * Automatically grows the field to accomodate the width of the text up to the maximum field width allowed.
22055      * This only takes effect if grow = true, and fires the autosize event.
22056      */
22057     autoSize : function(){
22058         if(!this.grow || !this.rendered){
22059             return;
22060         }
22061         if(!this.metrics){
22062             this.metrics = Roo.util.TextMetrics.createInstance(this.el);
22063         }
22064         var el = this.el;
22065         var v = el.dom.value;
22066         var d = document.createElement('div');
22067         d.appendChild(document.createTextNode(v));
22068         v = d.innerHTML;
22069         d = null;
22070         v += "&#160;";
22071         var w = Math.min(this.growMax, Math.max(this.metrics.getWidth(v) + /* add extra padding */ 10, this.growMin));
22072         this.el.setWidth(w);
22073         this.fireEvent("autosize", this, w);
22074     },
22075     
22076     // private
22077     SafariOnKeyDown : function(event)
22078     {
22079         // this is a workaround for a password hang bug on chrome/ webkit.
22080         
22081         var isSelectAll = false;
22082         
22083         if(this.el.dom.selectionEnd > 0){
22084             isSelectAll = (this.el.dom.selectionEnd - this.el.dom.selectionStart - this.getValue().length == 0) ? true : false;
22085         }
22086         if(((event.getKey() == 8 || event.getKey() == 46) && this.getValue().length ==1)){ // backspace and delete key
22087             event.preventDefault();
22088             this.setValue('');
22089             return;
22090         }
22091         
22092         if(isSelectAll){ // backspace and delete key
22093             
22094             event.preventDefault();
22095             // this is very hacky as keydown always get's upper case.
22096             //
22097             var cc = String.fromCharCode(event.getCharCode());
22098             this.setValue( event.shiftKey ?  cc : cc.toLowerCase());
22099             
22100         }
22101         
22102         
22103     }
22104 });/*
22105  * Based on:
22106  * Ext JS Library 1.1.1
22107  * Copyright(c) 2006-2007, Ext JS, LLC.
22108  *
22109  * Originally Released Under LGPL - original licence link has changed is not relivant.
22110  *
22111  * Fork - LGPL
22112  * <script type="text/javascript">
22113  */
22114  
22115 /**
22116  * @class Roo.form.Hidden
22117  * @extends Roo.form.TextField
22118  * Simple Hidden element used on forms 
22119  * 
22120  * usage: form.add(new Roo.form.HiddenField({ 'name' : 'test1' }));
22121  * 
22122  * @constructor
22123  * Creates a new Hidden form element.
22124  * @param {Object} config Configuration options
22125  */
22126
22127
22128
22129 // easy hidden field...
22130 Roo.form.Hidden = function(config){
22131     Roo.form.Hidden.superclass.constructor.call(this, config);
22132 };
22133   
22134 Roo.extend(Roo.form.Hidden, Roo.form.TextField, {
22135     fieldLabel:      '',
22136     inputType:      'hidden',
22137     width:          50,
22138     allowBlank:     true,
22139     labelSeparator: '',
22140     hidden:         true,
22141     itemCls :       'x-form-item-display-none'
22142
22143
22144 });
22145
22146
22147 /*
22148  * Based on:
22149  * Ext JS Library 1.1.1
22150  * Copyright(c) 2006-2007, Ext JS, LLC.
22151  *
22152  * Originally Released Under LGPL - original licence link has changed is not relivant.
22153  *
22154  * Fork - LGPL
22155  * <script type="text/javascript">
22156  */
22157  
22158 /**
22159  * @class Roo.form.TriggerField
22160  * @extends Roo.form.TextField
22161  * Provides a convenient wrapper for TextFields that adds a clickable trigger button (looks like a combobox by default).
22162  * The trigger has no default action, so you must assign a function to implement the trigger click handler by
22163  * overriding {@link #onTriggerClick}. You can create a TriggerField directly, as it renders exactly like a combobox
22164  * for which you can provide a custom implementation.  For example:
22165  * <pre><code>
22166 var trigger = new Roo.form.TriggerField();
22167 trigger.onTriggerClick = myTriggerFn;
22168 trigger.applyTo('my-field');
22169 </code></pre>
22170  *
22171  * However, in general you will most likely want to use TriggerField as the base class for a reusable component.
22172  * {@link Roo.form.DateField} and {@link Roo.form.ComboBox} are perfect examples of this.
22173  * @cfg {String} triggerClass An additional CSS class used to style the trigger button.  The trigger will always get the
22174  * class 'x-form-trigger' by default and triggerClass will be <b>appended</b> if specified.
22175  * @constructor
22176  * Create a new TriggerField.
22177  * @param {Object} config Configuration options (valid {@Roo.form.TextField} config options will also be applied
22178  * to the base TextField)
22179  */
22180 Roo.form.TriggerField = function(config){
22181     this.mimicing = false;
22182     Roo.form.TriggerField.superclass.constructor.call(this, config);
22183 };
22184
22185 Roo.extend(Roo.form.TriggerField, Roo.form.TextField,  {
22186     /**
22187      * @cfg {String} triggerClass A CSS class to apply to the trigger
22188      */
22189     /**
22190      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
22191      * {tag: "input", type: "text", size: "16", autocomplete: "off"})
22192      */
22193     defaultAutoCreate : {tag: "input", type: "text", size: "16", autocomplete: "off"},
22194     /**
22195      * @cfg {Boolean} hideTrigger True to hide the trigger element and display only the base text field (defaults to false)
22196      */
22197     hideTrigger:false,
22198
22199     /** @cfg {Boolean} grow @hide */
22200     /** @cfg {Number} growMin @hide */
22201     /** @cfg {Number} growMax @hide */
22202
22203     /**
22204      * @hide 
22205      * @method
22206      */
22207     autoSize: Roo.emptyFn,
22208     // private
22209     monitorTab : true,
22210     // private
22211     deferHeight : true,
22212
22213     
22214     actionMode : 'wrap',
22215     // private
22216     onResize : function(w, h){
22217         Roo.form.TriggerField.superclass.onResize.apply(this, arguments);
22218         if(typeof w == 'number'){
22219             var x = w - this.trigger.getWidth();
22220             this.el.setWidth(this.adjustWidth('input', x));
22221             this.trigger.setStyle('left', x+'px');
22222         }
22223     },
22224
22225     // private
22226     adjustSize : Roo.BoxComponent.prototype.adjustSize,
22227
22228     // private
22229     getResizeEl : function(){
22230         return this.wrap;
22231     },
22232
22233     // private
22234     getPositionEl : function(){
22235         return this.wrap;
22236     },
22237
22238     // private
22239     alignErrorIcon : function(){
22240         this.errorIcon.alignTo(this.wrap, 'tl-tr', [2, 0]);
22241     },
22242
22243     // private
22244     onRender : function(ct, position){
22245         Roo.form.TriggerField.superclass.onRender.call(this, ct, position);
22246         this.wrap = this.el.wrap({cls: "x-form-field-wrap"});
22247         this.trigger = this.wrap.createChild(this.triggerConfig ||
22248                 {tag: "img", src: Roo.BLANK_IMAGE_URL, cls: "x-form-trigger " + this.triggerClass});
22249         if(this.hideTrigger){
22250             this.trigger.setDisplayed(false);
22251         }
22252         this.initTrigger();
22253         if(!this.width){
22254             this.wrap.setWidth(this.el.getWidth()+this.trigger.getWidth());
22255         }
22256     },
22257
22258     // private
22259     initTrigger : function(){
22260         this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
22261         this.trigger.addClassOnOver('x-form-trigger-over');
22262         this.trigger.addClassOnClick('x-form-trigger-click');
22263     },
22264
22265     // private
22266     onDestroy : function(){
22267         if(this.trigger){
22268             this.trigger.removeAllListeners();
22269             this.trigger.remove();
22270         }
22271         if(this.wrap){
22272             this.wrap.remove();
22273         }
22274         Roo.form.TriggerField.superclass.onDestroy.call(this);
22275     },
22276
22277     // private
22278     onFocus : function(){
22279         Roo.form.TriggerField.superclass.onFocus.call(this);
22280         if(!this.mimicing){
22281             this.wrap.addClass('x-trigger-wrap-focus');
22282             this.mimicing = true;
22283             Roo.get(Roo.isIE ? document.body : document).on("mousedown", this.mimicBlur, this);
22284             if(this.monitorTab){
22285                 this.el.on("keydown", this.checkTab, this);
22286             }
22287         }
22288     },
22289
22290     // private
22291     checkTab : function(e){
22292         if(e.getKey() == e.TAB){
22293             this.triggerBlur();
22294         }
22295     },
22296
22297     // private
22298     onBlur : function(){
22299         // do nothing
22300     },
22301
22302     // private
22303     mimicBlur : function(e, t){
22304         if(!this.wrap.contains(t) && this.validateBlur()){
22305             this.triggerBlur();
22306         }
22307     },
22308
22309     // private
22310     triggerBlur : function(){
22311         this.mimicing = false;
22312         Roo.get(Roo.isIE ? document.body : document).un("mousedown", this.mimicBlur);
22313         if(this.monitorTab){
22314             this.el.un("keydown", this.checkTab, this);
22315         }
22316         this.wrap.removeClass('x-trigger-wrap-focus');
22317         Roo.form.TriggerField.superclass.onBlur.call(this);
22318     },
22319
22320     // private
22321     // This should be overriden by any subclass that needs to check whether or not the field can be blurred.
22322     validateBlur : function(e, t){
22323         return true;
22324     },
22325
22326     // private
22327     onDisable : function(){
22328         Roo.form.TriggerField.superclass.onDisable.call(this);
22329         if(this.wrap){
22330             this.wrap.addClass('x-item-disabled');
22331         }
22332     },
22333
22334     // private
22335     onEnable : function(){
22336         Roo.form.TriggerField.superclass.onEnable.call(this);
22337         if(this.wrap){
22338             this.wrap.removeClass('x-item-disabled');
22339         }
22340     },
22341
22342     // private
22343     onShow : function(){
22344         var ae = this.getActionEl();
22345         
22346         if(ae){
22347             ae.dom.style.display = '';
22348             ae.dom.style.visibility = 'visible';
22349         }
22350     },
22351
22352     // private
22353     
22354     onHide : function(){
22355         var ae = this.getActionEl();
22356         ae.dom.style.display = 'none';
22357     },
22358
22359     /**
22360      * The function that should handle the trigger's click event.  This method does nothing by default until overridden
22361      * by an implementing function.
22362      * @method
22363      * @param {EventObject} e
22364      */
22365     onTriggerClick : Roo.emptyFn
22366 });
22367
22368 // TwinTriggerField is not a public class to be used directly.  It is meant as an abstract base class
22369 // to be extended by an implementing class.  For an example of implementing this class, see the custom
22370 // SearchField implementation here: http://extjs.com/deploy/ext/examples/form/custom.html
22371 Roo.form.TwinTriggerField = Roo.extend(Roo.form.TriggerField, {
22372     initComponent : function(){
22373         Roo.form.TwinTriggerField.superclass.initComponent.call(this);
22374
22375         this.triggerConfig = {
22376             tag:'span', cls:'x-form-twin-triggers', cn:[
22377             {tag: "img", src: Roo.BLANK_IMAGE_URL, cls: "x-form-trigger " + this.trigger1Class},
22378             {tag: "img", src: Roo.BLANK_IMAGE_URL, cls: "x-form-trigger " + this.trigger2Class}
22379         ]};
22380     },
22381
22382     getTrigger : function(index){
22383         return this.triggers[index];
22384     },
22385
22386     initTrigger : function(){
22387         var ts = this.trigger.select('.x-form-trigger', true);
22388         this.wrap.setStyle('overflow', 'hidden');
22389         var triggerField = this;
22390         ts.each(function(t, all, index){
22391             t.hide = function(){
22392                 var w = triggerField.wrap.getWidth();
22393                 this.dom.style.display = 'none';
22394                 triggerField.el.setWidth(w-triggerField.trigger.getWidth());
22395             };
22396             t.show = function(){
22397                 var w = triggerField.wrap.getWidth();
22398                 this.dom.style.display = '';
22399                 triggerField.el.setWidth(w-triggerField.trigger.getWidth());
22400             };
22401             var triggerIndex = 'Trigger'+(index+1);
22402
22403             if(this['hide'+triggerIndex]){
22404                 t.dom.style.display = 'none';
22405             }
22406             t.on("click", this['on'+triggerIndex+'Click'], this, {preventDefault:true});
22407             t.addClassOnOver('x-form-trigger-over');
22408             t.addClassOnClick('x-form-trigger-click');
22409         }, this);
22410         this.triggers = ts.elements;
22411     },
22412
22413     onTrigger1Click : Roo.emptyFn,
22414     onTrigger2Click : Roo.emptyFn
22415 });/*
22416  * Based on:
22417  * Ext JS Library 1.1.1
22418  * Copyright(c) 2006-2007, Ext JS, LLC.
22419  *
22420  * Originally Released Under LGPL - original licence link has changed is not relivant.
22421  *
22422  * Fork - LGPL
22423  * <script type="text/javascript">
22424  */
22425  
22426 /**
22427  * @class Roo.form.TextArea
22428  * @extends Roo.form.TextField
22429  * Multiline text field.  Can be used as a direct replacement for traditional textarea fields, plus adds
22430  * support for auto-sizing.
22431  * @constructor
22432  * Creates a new TextArea
22433  * @param {Object} config Configuration options
22434  */
22435 Roo.form.TextArea = function(config){
22436     Roo.form.TextArea.superclass.constructor.call(this, config);
22437     // these are provided exchanges for backwards compat
22438     // minHeight/maxHeight were replaced by growMin/growMax to be
22439     // compatible with TextField growing config values
22440     if(this.minHeight !== undefined){
22441         this.growMin = this.minHeight;
22442     }
22443     if(this.maxHeight !== undefined){
22444         this.growMax = this.maxHeight;
22445     }
22446 };
22447
22448 Roo.extend(Roo.form.TextArea, Roo.form.TextField,  {
22449     /**
22450      * @cfg {Number} growMin The minimum height to allow when grow = true (defaults to 60)
22451      */
22452     growMin : 60,
22453     /**
22454      * @cfg {Number} growMax The maximum height to allow when grow = true (defaults to 1000)
22455      */
22456     growMax: 1000,
22457     /**
22458      * @cfg {Boolean} preventScrollbars True to prevent scrollbars from appearing regardless of how much text is
22459      * in the field (equivalent to setting overflow: hidden, defaults to false)
22460      */
22461     preventScrollbars: false,
22462     /**
22463      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
22464      * {tag: "textarea", style: "width:300px;height:60px;", autocomplete: "off"})
22465      */
22466
22467     // private
22468     onRender : function(ct, position){
22469         if(!this.el){
22470             this.defaultAutoCreate = {
22471                 tag: "textarea",
22472                 style:"width:300px;height:60px;",
22473                 autocomplete: "off"
22474             };
22475         }
22476         Roo.form.TextArea.superclass.onRender.call(this, ct, position);
22477         if(this.grow){
22478             this.textSizeEl = Roo.DomHelper.append(document.body, {
22479                 tag: "pre", cls: "x-form-grow-sizer"
22480             });
22481             if(this.preventScrollbars){
22482                 this.el.setStyle("overflow", "hidden");
22483             }
22484             this.el.setHeight(this.growMin);
22485         }
22486     },
22487
22488     onDestroy : function(){
22489         if(this.textSizeEl){
22490             this.textSizeEl.parentNode.removeChild(this.textSizeEl);
22491         }
22492         Roo.form.TextArea.superclass.onDestroy.call(this);
22493     },
22494
22495     // private
22496     onKeyUp : function(e){
22497         if(!e.isNavKeyPress() || e.getKey() == e.ENTER){
22498             this.autoSize();
22499         }
22500     },
22501
22502     /**
22503      * Automatically grows the field to accomodate the height of the text up to the maximum field height allowed.
22504      * This only takes effect if grow = true, and fires the autosize event if the height changes.
22505      */
22506     autoSize : function(){
22507         if(!this.grow || !this.textSizeEl){
22508             return;
22509         }
22510         var el = this.el;
22511         var v = el.dom.value;
22512         var ts = this.textSizeEl;
22513
22514         ts.innerHTML = '';
22515         ts.appendChild(document.createTextNode(v));
22516         v = ts.innerHTML;
22517
22518         Roo.fly(ts).setWidth(this.el.getWidth());
22519         if(v.length < 1){
22520             v = "&#160;&#160;";
22521         }else{
22522             if(Roo.isIE){
22523                 v = v.replace(/\n/g, '<p>&#160;</p>');
22524             }
22525             v += "&#160;\n&#160;";
22526         }
22527         ts.innerHTML = v;
22528         var h = Math.min(this.growMax, Math.max(ts.offsetHeight, this.growMin));
22529         if(h != this.lastHeight){
22530             this.lastHeight = h;
22531             this.el.setHeight(h);
22532             this.fireEvent("autosize", this, h);
22533         }
22534     }
22535 });/*
22536  * Based on:
22537  * Ext JS Library 1.1.1
22538  * Copyright(c) 2006-2007, Ext JS, LLC.
22539  *
22540  * Originally Released Under LGPL - original licence link has changed is not relivant.
22541  *
22542  * Fork - LGPL
22543  * <script type="text/javascript">
22544  */
22545  
22546
22547 /**
22548  * @class Roo.form.NumberField
22549  * @extends Roo.form.TextField
22550  * Numeric text field that provides automatic keystroke filtering and numeric validation.
22551  * @constructor
22552  * Creates a new NumberField
22553  * @param {Object} config Configuration options
22554  */
22555 Roo.form.NumberField = function(config){
22556     Roo.form.NumberField.superclass.constructor.call(this, config);
22557 };
22558
22559 Roo.extend(Roo.form.NumberField, Roo.form.TextField,  {
22560     /**
22561      * @cfg {String} fieldClass The default CSS class for the field (defaults to "x-form-field x-form-num-field")
22562      */
22563     fieldClass: "x-form-field x-form-num-field",
22564     /**
22565      * @cfg {Boolean} allowDecimals False to disallow decimal values (defaults to true)
22566      */
22567     allowDecimals : true,
22568     /**
22569      * @cfg {String} decimalSeparator Character(s) to allow as the decimal separator (defaults to '.')
22570      */
22571     decimalSeparator : ".",
22572     /**
22573      * @cfg {Number} decimalPrecision The maximum precision to display after the decimal separator (defaults to 2)
22574      */
22575     decimalPrecision : 2,
22576     /**
22577      * @cfg {Boolean} allowNegative False to prevent entering a negative sign (defaults to true)
22578      */
22579     allowNegative : true,
22580     /**
22581      * @cfg {Number} minValue The minimum allowed value (defaults to Number.NEGATIVE_INFINITY)
22582      */
22583     minValue : Number.NEGATIVE_INFINITY,
22584     /**
22585      * @cfg {Number} maxValue The maximum allowed value (defaults to Number.MAX_VALUE)
22586      */
22587     maxValue : Number.MAX_VALUE,
22588     /**
22589      * @cfg {String} minText Error text to display if the minimum value validation fails (defaults to "The minimum value for this field is {minValue}")
22590      */
22591     minText : "The minimum value for this field is {0}",
22592     /**
22593      * @cfg {String} maxText Error text to display if the maximum value validation fails (defaults to "The maximum value for this field is {maxValue}")
22594      */
22595     maxText : "The maximum value for this field is {0}",
22596     /**
22597      * @cfg {String} nanText Error text to display if the value is not a valid number.  For example, this can happen
22598      * if a valid character like '.' or '-' is left in the field with no number (defaults to "{value} is not a valid number")
22599      */
22600     nanText : "{0} is not a valid number",
22601
22602     // private
22603     initEvents : function(){
22604         Roo.form.NumberField.superclass.initEvents.call(this);
22605         var allowed = "0123456789";
22606         if(this.allowDecimals){
22607             allowed += this.decimalSeparator;
22608         }
22609         if(this.allowNegative){
22610             allowed += "-";
22611         }
22612         this.stripCharsRe = new RegExp('[^'+allowed+']', 'gi');
22613         var keyPress = function(e){
22614             var k = e.getKey();
22615             if(!Roo.isIE && (e.isSpecialKey() || k == e.BACKSPACE || k == e.DELETE)){
22616                 return;
22617             }
22618             var c = e.getCharCode();
22619             if(allowed.indexOf(String.fromCharCode(c)) === -1){
22620                 e.stopEvent();
22621             }
22622         };
22623         this.el.on("keypress", keyPress, this);
22624     },
22625
22626     // private
22627     validateValue : function(value){
22628         if(!Roo.form.NumberField.superclass.validateValue.call(this, value)){
22629             return false;
22630         }
22631         if(value.length < 1){ // if it's blank and textfield didn't flag it then it's valid
22632              return true;
22633         }
22634         var num = this.parseValue(value);
22635         if(isNaN(num)){
22636             this.markInvalid(String.format(this.nanText, value));
22637             return false;
22638         }
22639         if(num < this.minValue){
22640             this.markInvalid(String.format(this.minText, this.minValue));
22641             return false;
22642         }
22643         if(num > this.maxValue){
22644             this.markInvalid(String.format(this.maxText, this.maxValue));
22645             return false;
22646         }
22647         return true;
22648     },
22649
22650     getValue : function(){
22651         return this.fixPrecision(this.parseValue(Roo.form.NumberField.superclass.getValue.call(this)));
22652     },
22653
22654     // private
22655     parseValue : function(value){
22656         value = parseFloat(String(value).replace(this.decimalSeparator, "."));
22657         return isNaN(value) ? '' : value;
22658     },
22659
22660     // private
22661     fixPrecision : function(value){
22662         var nan = isNaN(value);
22663         if(!this.allowDecimals || this.decimalPrecision == -1 || nan || !value){
22664             return nan ? '' : value;
22665         }
22666         return parseFloat(value).toFixed(this.decimalPrecision);
22667     },
22668
22669     setValue : function(v){
22670         v = this.fixPrecision(v);
22671         Roo.form.NumberField.superclass.setValue.call(this, String(v).replace(".", this.decimalSeparator));
22672     },
22673
22674     // private
22675     decimalPrecisionFcn : function(v){
22676         return Math.floor(v);
22677     },
22678
22679     beforeBlur : function(){
22680         var v = this.parseValue(this.getRawValue());
22681         if(v){
22682             this.setValue(v);
22683         }
22684     }
22685 });/*
22686  * Based on:
22687  * Ext JS Library 1.1.1
22688  * Copyright(c) 2006-2007, Ext JS, LLC.
22689  *
22690  * Originally Released Under LGPL - original licence link has changed is not relivant.
22691  *
22692  * Fork - LGPL
22693  * <script type="text/javascript">
22694  */
22695  
22696 /**
22697  * @class Roo.form.DateField
22698  * @extends Roo.form.TriggerField
22699  * Provides a date input field with a {@link Roo.DatePicker} dropdown and automatic date validation.
22700 * @constructor
22701 * Create a new DateField
22702 * @param {Object} config
22703  */
22704 Roo.form.DateField = function(config){
22705     Roo.form.DateField.superclass.constructor.call(this, config);
22706     
22707       this.addEvents({
22708          
22709         /**
22710          * @event select
22711          * Fires when a date is selected
22712              * @param {Roo.form.DateField} combo This combo box
22713              * @param {Date} date The date selected
22714              */
22715         'select' : true
22716          
22717     });
22718     
22719     
22720     if(typeof this.minValue == "string") this.minValue = this.parseDate(this.minValue);
22721     if(typeof this.maxValue == "string") this.maxValue = this.parseDate(this.maxValue);
22722     this.ddMatch = null;
22723     if(this.disabledDates){
22724         var dd = this.disabledDates;
22725         var re = "(?:";
22726         for(var i = 0; i < dd.length; i++){
22727             re += dd[i];
22728             if(i != dd.length-1) re += "|";
22729         }
22730         this.ddMatch = new RegExp(re + ")");
22731     }
22732 };
22733
22734 Roo.extend(Roo.form.DateField, Roo.form.TriggerField,  {
22735     /**
22736      * @cfg {String} format
22737      * The default date format string which can be overriden for localization support.  The format must be
22738      * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
22739      */
22740     format : "m/d/y",
22741     /**
22742      * @cfg {String} altFormats
22743      * Multiple date formats separated by "|" to try when parsing a user input value and it doesn't match the defined
22744      * format (defaults to 'm/d/Y|m-d-y|m-d-Y|m/d|m-d|d').
22745      */
22746     altFormats : "m/d/Y|m-d-y|m-d-Y|m/d|m-d|md|mdy|mdY|d",
22747     /**
22748      * @cfg {Array} disabledDays
22749      * An array of days to disable, 0 based. For example, [0, 6] disables Sunday and Saturday (defaults to null).
22750      */
22751     disabledDays : null,
22752     /**
22753      * @cfg {String} disabledDaysText
22754      * The tooltip to display when the date falls on a disabled day (defaults to 'Disabled')
22755      */
22756     disabledDaysText : "Disabled",
22757     /**
22758      * @cfg {Array} disabledDates
22759      * An array of "dates" to disable, as strings. These strings will be used to build a dynamic regular
22760      * expression so they are very powerful. Some examples:
22761      * <ul>
22762      * <li>["03/08/2003", "09/16/2003"] would disable those exact dates</li>
22763      * <li>["03/08", "09/16"] would disable those days for every year</li>
22764      * <li>["^03/08"] would only match the beginning (useful if you are using short years)</li>
22765      * <li>["03/../2006"] would disable every day in March 2006</li>
22766      * <li>["^03"] would disable every day in every March</li>
22767      * </ul>
22768      * In order to support regular expressions, if you are using a date format that has "." in it, you will have to
22769      * escape the dot when restricting dates. For example: ["03\\.08\\.03"].
22770      */
22771     disabledDates : null,
22772     /**
22773      * @cfg {String} disabledDatesText
22774      * The tooltip text to display when the date falls on a disabled date (defaults to 'Disabled')
22775      */
22776     disabledDatesText : "Disabled",
22777     /**
22778      * @cfg {Date/String} minValue
22779      * The minimum allowed date. Can be either a Javascript date object or a string date in a
22780      * valid format (defaults to null).
22781      */
22782     minValue : null,
22783     /**
22784      * @cfg {Date/String} maxValue
22785      * The maximum allowed date. Can be either a Javascript date object or a string date in a
22786      * valid format (defaults to null).
22787      */
22788     maxValue : null,
22789     /**
22790      * @cfg {String} minText
22791      * The error text to display when the date in the cell is before minValue (defaults to
22792      * 'The date in this field must be after {minValue}').
22793      */
22794     minText : "The date in this field must be equal to or after {0}",
22795     /**
22796      * @cfg {String} maxText
22797      * The error text to display when the date in the cell is after maxValue (defaults to
22798      * 'The date in this field must be before {maxValue}').
22799      */
22800     maxText : "The date in this field must be equal to or before {0}",
22801     /**
22802      * @cfg {String} invalidText
22803      * The error text to display when the date in the field is invalid (defaults to
22804      * '{value} is not a valid date - it must be in the format {format}').
22805      */
22806     invalidText : "{0} is not a valid date - it must be in the format {1}",
22807     /**
22808      * @cfg {String} triggerClass
22809      * An additional CSS class used to style the trigger button.  The trigger will always get the
22810      * class 'x-form-trigger' and triggerClass will be <b>appended</b> if specified (defaults to 'x-form-date-trigger'
22811      * which displays a calendar icon).
22812      */
22813     triggerClass : 'x-form-date-trigger',
22814     
22815
22816     /**
22817      * @cfg {Boolean} useIso
22818      * if enabled, then the date field will use a hidden field to store the 
22819      * real value as iso formated date. default (false)
22820      */ 
22821     useIso : false,
22822     /**
22823      * @cfg {String/Object} autoCreate
22824      * A DomHelper element spec, or true for a default element spec (defaults to
22825      * {tag: "input", type: "text", size: "10", autocomplete: "off"})
22826      */ 
22827     // private
22828     defaultAutoCreate : {tag: "input", type: "text", size: "10", autocomplete: "off"},
22829     
22830     // private
22831     hiddenField: false,
22832     
22833     onRender : function(ct, position)
22834     {
22835         Roo.form.DateField.superclass.onRender.call(this, ct, position);
22836         if (this.useIso) {
22837             //this.el.dom.removeAttribute('name'); 
22838             Roo.log("Changing name?");
22839             this.el.dom.setAttribute('name', this.name + '____hidden___' ); 
22840             this.hiddenField = this.el.insertSibling({ tag:'input', type:'hidden', name: this.name },
22841                     'before', true);
22842             this.hiddenField.value = this.value ? this.formatDate(this.value, 'Y-m-d') : '';
22843             // prevent input submission
22844             this.hiddenName = this.name;
22845         }
22846             
22847             
22848     },
22849     
22850     // private
22851     validateValue : function(value)
22852     {
22853         value = this.formatDate(value);
22854         if(!Roo.form.DateField.superclass.validateValue.call(this, value)){
22855             Roo.log('super failed');
22856             return false;
22857         }
22858         if(value.length < 1){ // if it's blank and textfield didn't flag it then it's valid
22859              return true;
22860         }
22861         var svalue = value;
22862         value = this.parseDate(value);
22863         if(!value){
22864             Roo.log('parse date failed' + svalue);
22865             this.markInvalid(String.format(this.invalidText, svalue, this.format));
22866             return false;
22867         }
22868         var time = value.getTime();
22869         if(this.minValue && time < this.minValue.getTime()){
22870             this.markInvalid(String.format(this.minText, this.formatDate(this.minValue)));
22871             return false;
22872         }
22873         if(this.maxValue && time > this.maxValue.getTime()){
22874             this.markInvalid(String.format(this.maxText, this.formatDate(this.maxValue)));
22875             return false;
22876         }
22877         if(this.disabledDays){
22878             var day = value.getDay();
22879             for(var i = 0; i < this.disabledDays.length; i++) {
22880                 if(day === this.disabledDays[i]){
22881                     this.markInvalid(this.disabledDaysText);
22882                     return false;
22883                 }
22884             }
22885         }
22886         var fvalue = this.formatDate(value);
22887         if(this.ddMatch && this.ddMatch.test(fvalue)){
22888             this.markInvalid(String.format(this.disabledDatesText, fvalue));
22889             return false;
22890         }
22891         return true;
22892     },
22893
22894     // private
22895     // Provides logic to override the default TriggerField.validateBlur which just returns true
22896     validateBlur : function(){
22897         return !this.menu || !this.menu.isVisible();
22898     },
22899     
22900     getName: function()
22901     {
22902         // returns hidden if it's set..
22903         if (!this.rendered) {return ''};
22904         return !this.hiddenName && this.el.dom.name  ? this.el.dom.name : (this.hiddenName || '');
22905         
22906     },
22907
22908     /**
22909      * Returns the current date value of the date field.
22910      * @return {Date} The date value
22911      */
22912     getValue : function(){
22913         
22914         return  this.hiddenField ?
22915                 this.hiddenField.value :
22916                 this.parseDate(Roo.form.DateField.superclass.getValue.call(this)) || "";
22917     },
22918
22919     /**
22920      * Sets the value of the date field.  You can pass a date object or any string that can be parsed into a valid
22921      * date, using DateField.format as the date format, according to the same rules as {@link Date#parseDate}
22922      * (the default format used is "m/d/y").
22923      * <br />Usage:
22924      * <pre><code>
22925 //All of these calls set the same date value (May 4, 2006)
22926
22927 //Pass a date object:
22928 var dt = new Date('5/4/06');
22929 dateField.setValue(dt);
22930
22931 //Pass a date string (default format):
22932 dateField.setValue('5/4/06');
22933
22934 //Pass a date string (custom format):
22935 dateField.format = 'Y-m-d';
22936 dateField.setValue('2006-5-4');
22937 </code></pre>
22938      * @param {String/Date} date The date or valid date string
22939      */
22940     setValue : function(date){
22941         if (this.hiddenField) {
22942             this.hiddenField.value = this.formatDate(this.parseDate(date), 'Y-m-d');
22943         }
22944         Roo.form.DateField.superclass.setValue.call(this, this.formatDate(this.parseDate(date)));
22945         // make sure the value field is always stored as a date..
22946         this.value = this.parseDate(date);
22947         
22948         
22949     },
22950
22951     // private
22952     parseDate : function(value){
22953         if(!value || value instanceof Date){
22954             return value;
22955         }
22956         var v = Date.parseDate(value, this.format);
22957          if (!v && this.useIso) {
22958             v = Date.parseDate(value, 'Y-m-d');
22959         }
22960         if(!v && this.altFormats){
22961             if(!this.altFormatsArray){
22962                 this.altFormatsArray = this.altFormats.split("|");
22963             }
22964             for(var i = 0, len = this.altFormatsArray.length; i < len && !v; i++){
22965                 v = Date.parseDate(value, this.altFormatsArray[i]);
22966             }
22967         }
22968         return v;
22969     },
22970
22971     // private
22972     formatDate : function(date, fmt){
22973         return (!date || !(date instanceof Date)) ?
22974                date : date.dateFormat(fmt || this.format);
22975     },
22976
22977     // private
22978     menuListeners : {
22979         select: function(m, d){
22980             
22981             this.setValue(d);
22982             this.fireEvent('select', this, d);
22983         },
22984         show : function(){ // retain focus styling
22985             this.onFocus();
22986         },
22987         hide : function(){
22988             this.focus.defer(10, this);
22989             var ml = this.menuListeners;
22990             this.menu.un("select", ml.select,  this);
22991             this.menu.un("show", ml.show,  this);
22992             this.menu.un("hide", ml.hide,  this);
22993         }
22994     },
22995
22996     // private
22997     // Implements the default empty TriggerField.onTriggerClick function to display the DatePicker
22998     onTriggerClick : function(){
22999         if(this.disabled){
23000             return;
23001         }
23002         if(this.menu == null){
23003             this.menu = new Roo.menu.DateMenu();
23004         }
23005         Roo.apply(this.menu.picker,  {
23006             showClear: this.allowBlank,
23007             minDate : this.minValue,
23008             maxDate : this.maxValue,
23009             disabledDatesRE : this.ddMatch,
23010             disabledDatesText : this.disabledDatesText,
23011             disabledDays : this.disabledDays,
23012             disabledDaysText : this.disabledDaysText,
23013             format : this.useIso ? 'Y-m-d' : this.format,
23014             minText : String.format(this.minText, this.formatDate(this.minValue)),
23015             maxText : String.format(this.maxText, this.formatDate(this.maxValue))
23016         });
23017         this.menu.on(Roo.apply({}, this.menuListeners, {
23018             scope:this
23019         }));
23020         this.menu.picker.setValue(this.getValue() || new Date());
23021         this.menu.show(this.el, "tl-bl?");
23022     },
23023
23024     beforeBlur : function(){
23025         var v = this.parseDate(this.getRawValue());
23026         if(v){
23027             this.setValue(v);
23028         }
23029     }
23030
23031     /** @cfg {Boolean} grow @hide */
23032     /** @cfg {Number} growMin @hide */
23033     /** @cfg {Number} growMax @hide */
23034     /**
23035      * @hide
23036      * @method autoSize
23037      */
23038 });/*
23039  * Based on:
23040  * Ext JS Library 1.1.1
23041  * Copyright(c) 2006-2007, Ext JS, LLC.
23042  *
23043  * Originally Released Under LGPL - original licence link has changed is not relivant.
23044  *
23045  * Fork - LGPL
23046  * <script type="text/javascript">
23047  */
23048  
23049 /**
23050  * @class Roo.form.MonthField
23051  * @extends Roo.form.TriggerField
23052  * Provides a date input field with a {@link Roo.DatePicker} dropdown and automatic date validation.
23053 * @constructor
23054 * Create a new MonthField
23055 * @param {Object} config
23056  */
23057 Roo.form.MonthField = function(config){
23058     
23059     Roo.form.MonthField.superclass.constructor.call(this, config);
23060     
23061       this.addEvents({
23062          
23063         /**
23064          * @event select
23065          * Fires when a date is selected
23066              * @param {Roo.form.MonthFieeld} combo This combo box
23067              * @param {Date} date The date selected
23068              */
23069         'select' : true
23070          
23071     });
23072     
23073     
23074     if(typeof this.minValue == "string") this.minValue = this.parseDate(this.minValue);
23075     if(typeof this.maxValue == "string") this.maxValue = this.parseDate(this.maxValue);
23076     this.ddMatch = null;
23077     if(this.disabledDates){
23078         var dd = this.disabledDates;
23079         var re = "(?:";
23080         for(var i = 0; i < dd.length; i++){
23081             re += dd[i];
23082             if(i != dd.length-1) re += "|";
23083         }
23084         this.ddMatch = new RegExp(re + ")");
23085     }
23086 };
23087
23088 Roo.extend(Roo.form.MonthField, Roo.form.TriggerField,  {
23089     /**
23090      * @cfg {String} format
23091      * The default date format string which can be overriden for localization support.  The format must be
23092      * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
23093      */
23094     format : "M Y",
23095     /**
23096      * @cfg {String} altFormats
23097      * Multiple date formats separated by "|" to try when parsing a user input value and it doesn't match the defined
23098      * format (defaults to 'm/d/Y|m-d-y|m-d-Y|m/d|m-d|d').
23099      */
23100     altFormats : "M Y|m/Y|m-y|m-Y|my|mY",
23101     /**
23102      * @cfg {Array} disabledDays
23103      * An array of days to disable, 0 based. For example, [0, 6] disables Sunday and Saturday (defaults to null).
23104      */
23105     disabledDays : [0,1,2,3,4,5,6],
23106     /**
23107      * @cfg {String} disabledDaysText
23108      * The tooltip to display when the date falls on a disabled day (defaults to 'Disabled')
23109      */
23110     disabledDaysText : "Disabled",
23111     /**
23112      * @cfg {Array} disabledDates
23113      * An array of "dates" to disable, as strings. These strings will be used to build a dynamic regular
23114      * expression so they are very powerful. Some examples:
23115      * <ul>
23116      * <li>["03/08/2003", "09/16/2003"] would disable those exact dates</li>
23117      * <li>["03/08", "09/16"] would disable those days for every year</li>
23118      * <li>["^03/08"] would only match the beginning (useful if you are using short years)</li>
23119      * <li>["03/../2006"] would disable every day in March 2006</li>
23120      * <li>["^03"] would disable every day in every March</li>
23121      * </ul>
23122      * In order to support regular expressions, if you are using a date format that has "." in it, you will have to
23123      * escape the dot when restricting dates. For example: ["03\\.08\\.03"].
23124      */
23125     disabledDates : null,
23126     /**
23127      * @cfg {String} disabledDatesText
23128      * The tooltip text to display when the date falls on a disabled date (defaults to 'Disabled')
23129      */
23130     disabledDatesText : "Disabled",
23131     /**
23132      * @cfg {Date/String} minValue
23133      * The minimum allowed date. Can be either a Javascript date object or a string date in a
23134      * valid format (defaults to null).
23135      */
23136     minValue : null,
23137     /**
23138      * @cfg {Date/String} maxValue
23139      * The maximum allowed date. Can be either a Javascript date object or a string date in a
23140      * valid format (defaults to null).
23141      */
23142     maxValue : null,
23143     /**
23144      * @cfg {String} minText
23145      * The error text to display when the date in the cell is before minValue (defaults to
23146      * 'The date in this field must be after {minValue}').
23147      */
23148     minText : "The date in this field must be equal to or after {0}",
23149     /**
23150      * @cfg {String} maxTextf
23151      * The error text to display when the date in the cell is after maxValue (defaults to
23152      * 'The date in this field must be before {maxValue}').
23153      */
23154     maxText : "The date in this field must be equal to or before {0}",
23155     /**
23156      * @cfg {String} invalidText
23157      * The error text to display when the date in the field is invalid (defaults to
23158      * '{value} is not a valid date - it must be in the format {format}').
23159      */
23160     invalidText : "{0} is not a valid date - it must be in the format {1}",
23161     /**
23162      * @cfg {String} triggerClass
23163      * An additional CSS class used to style the trigger button.  The trigger will always get the
23164      * class 'x-form-trigger' and triggerClass will be <b>appended</b> if specified (defaults to 'x-form-date-trigger'
23165      * which displays a calendar icon).
23166      */
23167     triggerClass : 'x-form-date-trigger',
23168     
23169
23170     /**
23171      * @cfg {Boolean} useIso
23172      * if enabled, then the date field will use a hidden field to store the 
23173      * real value as iso formated date. default (true)
23174      */ 
23175     useIso : true,
23176     /**
23177      * @cfg {String/Object} autoCreate
23178      * A DomHelper element spec, or true for a default element spec (defaults to
23179      * {tag: "input", type: "text", size: "10", autocomplete: "off"})
23180      */ 
23181     // private
23182     defaultAutoCreate : {tag: "input", type: "text", size: "10", autocomplete: "off"},
23183     
23184     // private
23185     hiddenField: false,
23186     
23187     hideMonthPicker : false,
23188     
23189     onRender : function(ct, position)
23190     {
23191         Roo.form.MonthField.superclass.onRender.call(this, ct, position);
23192         if (this.useIso) {
23193             this.el.dom.removeAttribute('name'); 
23194             this.hiddenField = this.el.insertSibling({ tag:'input', type:'hidden', name: this.name },
23195                     'before', true);
23196             this.hiddenField.value = this.value ? this.formatDate(this.value, 'Y-m-d') : '';
23197             // prevent input submission
23198             this.hiddenName = this.name;
23199         }
23200             
23201             
23202     },
23203     
23204     // private
23205     validateValue : function(value)
23206     {
23207         value = this.formatDate(value);
23208         if(!Roo.form.MonthField.superclass.validateValue.call(this, value)){
23209             return false;
23210         }
23211         if(value.length < 1){ // if it's blank and textfield didn't flag it then it's valid
23212              return true;
23213         }
23214         var svalue = value;
23215         value = this.parseDate(value);
23216         if(!value){
23217             this.markInvalid(String.format(this.invalidText, svalue, this.format));
23218             return false;
23219         }
23220         var time = value.getTime();
23221         if(this.minValue && time < this.minValue.getTime()){
23222             this.markInvalid(String.format(this.minText, this.formatDate(this.minValue)));
23223             return false;
23224         }
23225         if(this.maxValue && time > this.maxValue.getTime()){
23226             this.markInvalid(String.format(this.maxText, this.formatDate(this.maxValue)));
23227             return false;
23228         }
23229         /*if(this.disabledDays){
23230             var day = value.getDay();
23231             for(var i = 0; i < this.disabledDays.length; i++) {
23232                 if(day === this.disabledDays[i]){
23233                     this.markInvalid(this.disabledDaysText);
23234                     return false;
23235                 }
23236             }
23237         }
23238         */
23239         var fvalue = this.formatDate(value);
23240         /*if(this.ddMatch && this.ddMatch.test(fvalue)){
23241             this.markInvalid(String.format(this.disabledDatesText, fvalue));
23242             return false;
23243         }
23244         */
23245         return true;
23246     },
23247
23248     // private
23249     // Provides logic to override the default TriggerField.validateBlur which just returns true
23250     validateBlur : function(){
23251         return !this.menu || !this.menu.isVisible();
23252     },
23253
23254     /**
23255      * Returns the current date value of the date field.
23256      * @return {Date} The date value
23257      */
23258     getValue : function(){
23259         
23260         
23261         
23262         return  this.hiddenField ?
23263                 this.hiddenField.value :
23264                 this.parseDate(Roo.form.MonthField.superclass.getValue.call(this)) || "";
23265     },
23266
23267     /**
23268      * Sets the value of the date field.  You can pass a date object or any string that can be parsed into a valid
23269      * date, using MonthField.format as the date format, according to the same rules as {@link Date#parseDate}
23270      * (the default format used is "m/d/y").
23271      * <br />Usage:
23272      * <pre><code>
23273 //All of these calls set the same date value (May 4, 2006)
23274
23275 //Pass a date object:
23276 var dt = new Date('5/4/06');
23277 monthField.setValue(dt);
23278
23279 //Pass a date string (default format):
23280 monthField.setValue('5/4/06');
23281
23282 //Pass a date string (custom format):
23283 monthField.format = 'Y-m-d';
23284 monthField.setValue('2006-5-4');
23285 </code></pre>
23286      * @param {String/Date} date The date or valid date string
23287      */
23288     setValue : function(date){
23289         Roo.log('month setValue' + date);
23290         // can only be first of month..
23291         
23292         var val = this.parseDate(date);
23293         
23294         if (this.hiddenField) {
23295             this.hiddenField.value = this.formatDate(this.parseDate(date), 'Y-m-d');
23296         }
23297         Roo.form.MonthField.superclass.setValue.call(this, this.formatDate(this.parseDate(date)));
23298         this.value = this.parseDate(date);
23299     },
23300
23301     // private
23302     parseDate : function(value){
23303         if(!value || value instanceof Date){
23304             value = value ? Date.parseDate(value.format('Y-m') + '-01', 'Y-m-d') : null;
23305             return value;
23306         }
23307         var v = Date.parseDate(value, this.format);
23308         if (!v && this.useIso) {
23309             v = Date.parseDate(value, 'Y-m-d');
23310         }
23311         if (v) {
23312             // 
23313             v = Date.parseDate(v.format('Y-m') +'-01', 'Y-m-d');
23314         }
23315         
23316         
23317         if(!v && this.altFormats){
23318             if(!this.altFormatsArray){
23319                 this.altFormatsArray = this.altFormats.split("|");
23320             }
23321             for(var i = 0, len = this.altFormatsArray.length; i < len && !v; i++){
23322                 v = Date.parseDate(value, this.altFormatsArray[i]);
23323             }
23324         }
23325         return v;
23326     },
23327
23328     // private
23329     formatDate : function(date, fmt){
23330         return (!date || !(date instanceof Date)) ?
23331                date : date.dateFormat(fmt || this.format);
23332     },
23333
23334     // private
23335     menuListeners : {
23336         select: function(m, d){
23337             this.setValue(d);
23338             this.fireEvent('select', this, d);
23339         },
23340         show : function(){ // retain focus styling
23341             this.onFocus();
23342         },
23343         hide : function(){
23344             this.focus.defer(10, this);
23345             var ml = this.menuListeners;
23346             this.menu.un("select", ml.select,  this);
23347             this.menu.un("show", ml.show,  this);
23348             this.menu.un("hide", ml.hide,  this);
23349         }
23350     },
23351     // private
23352     // Implements the default empty TriggerField.onTriggerClick function to display the DatePicker
23353     onTriggerClick : function(){
23354         if(this.disabled){
23355             return;
23356         }
23357         if(this.menu == null){
23358             this.menu = new Roo.menu.DateMenu();
23359            
23360         }
23361         
23362         Roo.apply(this.menu.picker,  {
23363             
23364             showClear: this.allowBlank,
23365             minDate : this.minValue,
23366             maxDate : this.maxValue,
23367             disabledDatesRE : this.ddMatch,
23368             disabledDatesText : this.disabledDatesText,
23369             
23370             format : this.useIso ? 'Y-m-d' : this.format,
23371             minText : String.format(this.minText, this.formatDate(this.minValue)),
23372             maxText : String.format(this.maxText, this.formatDate(this.maxValue))
23373             
23374         });
23375          this.menu.on(Roo.apply({}, this.menuListeners, {
23376             scope:this
23377         }));
23378        
23379         
23380         var m = this.menu;
23381         var p = m.picker;
23382         
23383         // hide month picker get's called when we called by 'before hide';
23384         
23385         var ignorehide = true;
23386         p.hideMonthPicker  = function(disableAnim){
23387             if (ignorehide) {
23388                 return;
23389             }
23390              if(this.monthPicker){
23391                 Roo.log("hideMonthPicker called");
23392                 if(disableAnim === true){
23393                     this.monthPicker.hide();
23394                 }else{
23395                     this.monthPicker.slideOut('t', {duration:.2});
23396                     p.setValue(new Date(m.picker.mpSelYear, m.picker.mpSelMonth, 1));
23397                     p.fireEvent("select", this, this.value);
23398                     m.hide();
23399                 }
23400             }
23401         }
23402         
23403         Roo.log('picker set value');
23404         Roo.log(this.getValue());
23405         p.setValue(this.getValue() ? this.parseDate(this.getValue()) : new Date());
23406         m.show(this.el, 'tl-bl?');
23407         ignorehide  = false;
23408         // this will trigger hideMonthPicker..
23409         
23410         
23411         // hidden the day picker
23412         Roo.select('.x-date-picker table', true).first().dom.style.visibility = "hidden";
23413         
23414         
23415         
23416       
23417         
23418         p.showMonthPicker.defer(100, p);
23419     
23420         
23421        
23422     },
23423
23424     beforeBlur : function(){
23425         var v = this.parseDate(this.getRawValue());
23426         if(v){
23427             this.setValue(v);
23428         }
23429     }
23430
23431     /** @cfg {Boolean} grow @hide */
23432     /** @cfg {Number} growMin @hide */
23433     /** @cfg {Number} growMax @hide */
23434     /**
23435      * @hide
23436      * @method autoSize
23437      */
23438 });/*
23439  * Based on:
23440  * Ext JS Library 1.1.1
23441  * Copyright(c) 2006-2007, Ext JS, LLC.
23442  *
23443  * Originally Released Under LGPL - original licence link has changed is not relivant.
23444  *
23445  * Fork - LGPL
23446  * <script type="text/javascript">
23447  */
23448  
23449
23450 /**
23451  * @class Roo.form.ComboBox
23452  * @extends Roo.form.TriggerField
23453  * A combobox control with support for autocomplete, remote-loading, paging and many other features.
23454  * @constructor
23455  * Create a new ComboBox.
23456  * @param {Object} config Configuration options
23457  */
23458 Roo.form.ComboBox = function(config){
23459     Roo.form.ComboBox.superclass.constructor.call(this, config);
23460     this.addEvents({
23461         /**
23462          * @event expand
23463          * Fires when the dropdown list is expanded
23464              * @param {Roo.form.ComboBox} combo This combo box
23465              */
23466         'expand' : true,
23467         /**
23468          * @event collapse
23469          * Fires when the dropdown list is collapsed
23470              * @param {Roo.form.ComboBox} combo This combo box
23471              */
23472         'collapse' : true,
23473         /**
23474          * @event beforeselect
23475          * Fires before a list item is selected. Return false to cancel the selection.
23476              * @param {Roo.form.ComboBox} combo This combo box
23477              * @param {Roo.data.Record} record The data record returned from the underlying store
23478              * @param {Number} index The index of the selected item in the dropdown list
23479              */
23480         'beforeselect' : true,
23481         /**
23482          * @event select
23483          * Fires when a list item is selected
23484              * @param {Roo.form.ComboBox} combo This combo box
23485              * @param {Roo.data.Record} record The data record returned from the underlying store (or false on clear)
23486              * @param {Number} index The index of the selected item in the dropdown list
23487              */
23488         'select' : true,
23489         /**
23490          * @event beforequery
23491          * Fires before all queries are processed. Return false to cancel the query or set cancel to true.
23492          * The event object passed has these properties:
23493              * @param {Roo.form.ComboBox} combo This combo box
23494              * @param {String} query The query
23495              * @param {Boolean} forceAll true to force "all" query
23496              * @param {Boolean} cancel true to cancel the query
23497              * @param {Object} e The query event object
23498              */
23499         'beforequery': true,
23500          /**
23501          * @event add
23502          * Fires when the 'add' icon is pressed (add a listener to enable add button)
23503              * @param {Roo.form.ComboBox} combo This combo box
23504              */
23505         'add' : true,
23506         /**
23507          * @event edit
23508          * Fires when the 'edit' icon is pressed (add a listener to enable add button)
23509              * @param {Roo.form.ComboBox} combo This combo box
23510              * @param {Roo.data.Record|false} record The data record returned from the underlying store (or false on nothing selected)
23511              */
23512         'edit' : true
23513         
23514         
23515     });
23516     if(this.transform){
23517         this.allowDomMove = false;
23518         var s = Roo.getDom(this.transform);
23519         if(!this.hiddenName){
23520             this.hiddenName = s.name;
23521         }
23522         if(!this.store){
23523             this.mode = 'local';
23524             var d = [], opts = s.options;
23525             for(var i = 0, len = opts.length;i < len; i++){
23526                 var o = opts[i];
23527                 var value = (Roo.isIE ? o.getAttributeNode('value').specified : o.hasAttribute('value')) ? o.value : o.text;
23528                 if(o.selected) {
23529                     this.value = value;
23530                 }
23531                 d.push([value, o.text]);
23532             }
23533             this.store = new Roo.data.SimpleStore({
23534                 'id': 0,
23535                 fields: ['value', 'text'],
23536                 data : d
23537             });
23538             this.valueField = 'value';
23539             this.displayField = 'text';
23540         }
23541         s.name = Roo.id(); // wipe out the name in case somewhere else they have a reference
23542         if(!this.lazyRender){
23543             this.target = true;
23544             this.el = Roo.DomHelper.insertBefore(s, this.autoCreate || this.defaultAutoCreate);
23545             s.parentNode.removeChild(s); // remove it
23546             this.render(this.el.parentNode);
23547         }else{
23548             s.parentNode.removeChild(s); // remove it
23549         }
23550
23551     }
23552     if (this.store) {
23553         this.store = Roo.factory(this.store, Roo.data);
23554     }
23555     
23556     this.selectedIndex = -1;
23557     if(this.mode == 'local'){
23558         if(config.queryDelay === undefined){
23559             this.queryDelay = 10;
23560         }
23561         if(config.minChars === undefined){
23562             this.minChars = 0;
23563         }
23564     }
23565 };
23566
23567 Roo.extend(Roo.form.ComboBox, Roo.form.TriggerField, {
23568     /**
23569      * @cfg {String/HTMLElement/Element} transform The id, DOM node or element of an existing select to convert to a ComboBox
23570      */
23571     /**
23572      * @cfg {Boolean} lazyRender True to prevent the ComboBox from rendering until requested (should always be used when
23573      * rendering into an Roo.Editor, defaults to false)
23574      */
23575     /**
23576      * @cfg {Boolean/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to:
23577      * {tag: "input", type: "text", size: "24", autocomplete: "off"})
23578      */
23579     /**
23580      * @cfg {Roo.data.Store} store The data store to which this combo is bound (defaults to undefined)
23581      */
23582     /**
23583      * @cfg {String} title If supplied, a header element is created containing this text and added into the top of
23584      * the dropdown list (defaults to undefined, with no header element)
23585      */
23586
23587      /**
23588      * @cfg {String/Roo.Template} tpl The template to use to render the output
23589      */
23590      
23591     // private
23592     defaultAutoCreate : {tag: "input", type: "text", size: "24", autocomplete: "off"},
23593     /**
23594      * @cfg {Number} listWidth The width in pixels of the dropdown list (defaults to the width of the ComboBox field)
23595      */
23596     listWidth: undefined,
23597     /**
23598      * @cfg {String} displayField The underlying data field name to bind to this CombBox (defaults to undefined if
23599      * mode = 'remote' or 'text' if mode = 'local')
23600      */
23601     displayField: undefined,
23602     /**
23603      * @cfg {String} valueField The underlying data value name to bind to this CombBox (defaults to undefined if
23604      * mode = 'remote' or 'value' if mode = 'local'). 
23605      * Note: use of a valueField requires the user make a selection
23606      * in order for a value to be mapped.
23607      */
23608     valueField: undefined,
23609     
23610     
23611     /**
23612      * @cfg {String} hiddenName If specified, a hidden form field with this name is dynamically generated to store the
23613      * field's data value (defaults to the underlying DOM element's name)
23614      */
23615     hiddenName: undefined,
23616     /**
23617      * @cfg {String} listClass CSS class to apply to the dropdown list element (defaults to '')
23618      */
23619     listClass: '',
23620     /**
23621      * @cfg {String} selectedClass CSS class to apply to the selected item in the dropdown list (defaults to 'x-combo-selected')
23622      */
23623     selectedClass: 'x-combo-selected',
23624     /**
23625      * @cfg {String} triggerClass An additional CSS class used to style the trigger button.  The trigger will always get the
23626      * class 'x-form-trigger' and triggerClass will be <b>appended</b> if specified (defaults to 'x-form-arrow-trigger'
23627      * which displays a downward arrow icon).
23628      */
23629     triggerClass : 'x-form-arrow-trigger',
23630     /**
23631      * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
23632      */
23633     shadow:'sides',
23634     /**
23635      * @cfg {String} listAlign A valid anchor position value. See {@link Roo.Element#alignTo} for details on supported
23636      * anchor positions (defaults to 'tl-bl')
23637      */
23638     listAlign: 'tl-bl?',
23639     /**
23640      * @cfg {Number} maxHeight The maximum height in pixels of the dropdown list before scrollbars are shown (defaults to 300)
23641      */
23642     maxHeight: 300,
23643     /**
23644      * @cfg {String} triggerAction The action to execute when the trigger field is activated.  Use 'all' to run the
23645      * query specified by the allQuery config option (defaults to 'query')
23646      */
23647     triggerAction: 'query',
23648     /**
23649      * @cfg {Number} minChars The minimum number of characters the user must type before autocomplete and typeahead activate
23650      * (defaults to 4, does not apply if editable = false)
23651      */
23652     minChars : 4,
23653     /**
23654      * @cfg {Boolean} typeAhead True to populate and autoselect the remainder of the text being typed after a configurable
23655      * delay (typeAheadDelay) if it matches a known value (defaults to false)
23656      */
23657     typeAhead: false,
23658     /**
23659      * @cfg {Number} queryDelay The length of time in milliseconds to delay between the start of typing and sending the
23660      * query to filter the dropdown list (defaults to 500 if mode = 'remote' or 10 if mode = 'local')
23661      */
23662     queryDelay: 500,
23663     /**
23664      * @cfg {Number} pageSize If greater than 0, a paging toolbar is displayed in the footer of the dropdown list and the
23665      * filter queries will execute with page start and limit parameters.  Only applies when mode = 'remote' (defaults to 0)
23666      */
23667     pageSize: 0,
23668     /**
23669      * @cfg {Boolean} selectOnFocus True to select any existing text in the field immediately on focus.  Only applies
23670      * when editable = true (defaults to false)
23671      */
23672     selectOnFocus:false,
23673     /**
23674      * @cfg {String} queryParam Name of the query as it will be passed on the querystring (defaults to 'query')
23675      */
23676     queryParam: 'query',
23677     /**
23678      * @cfg {String} loadingText The text to display in the dropdown list while data is loading.  Only applies
23679      * when mode = 'remote' (defaults to 'Loading...')
23680      */
23681     loadingText: 'Loading...',
23682     /**
23683      * @cfg {Boolean} resizable True to add a resize handle to the bottom of the dropdown list (defaults to false)
23684      */
23685     resizable: false,
23686     /**
23687      * @cfg {Number} handleHeight The height in pixels of the dropdown list resize handle if resizable = true (defaults to 8)
23688      */
23689     handleHeight : 8,
23690     /**
23691      * @cfg {Boolean} editable False to prevent the user from typing text directly into the field, just like a
23692      * traditional select (defaults to true)
23693      */
23694     editable: true,
23695     /**
23696      * @cfg {String} allQuery The text query to send to the server to return all records for the list with no filtering (defaults to '')
23697      */
23698     allQuery: '',
23699     /**
23700      * @cfg {String} mode Set to 'local' if the ComboBox loads local data (defaults to 'remote' which loads from the server)
23701      */
23702     mode: 'remote',
23703     /**
23704      * @cfg {Number} minListWidth The minimum width of the dropdown list in pixels (defaults to 70, will be ignored if
23705      * listWidth has a higher value)
23706      */
23707     minListWidth : 70,
23708     /**
23709      * @cfg {Boolean} forceSelection True to restrict the selected value to one of the values in the list, false to
23710      * allow the user to set arbitrary text into the field (defaults to false)
23711      */
23712     forceSelection:false,
23713     /**
23714      * @cfg {Number} typeAheadDelay The length of time in milliseconds to wait until the typeahead text is displayed
23715      * if typeAhead = true (defaults to 250)
23716      */
23717     typeAheadDelay : 250,
23718     /**
23719      * @cfg {String} valueNotFoundText When using a name/value combo, if the value passed to setValue is not found in
23720      * the store, valueNotFoundText will be displayed as the field text if defined (defaults to undefined)
23721      */
23722     valueNotFoundText : undefined,
23723     /**
23724      * @cfg {Boolean} blockFocus Prevents all focus calls, so it can work with things like HTML edtor bar
23725      */
23726     blockFocus : false,
23727     
23728     /**
23729      * @cfg {Boolean} disableClear Disable showing of clear button.
23730      */
23731     disableClear : false,
23732     /**
23733      * @cfg {Boolean} alwaysQuery  Disable caching of results, and always send query
23734      */
23735     alwaysQuery : false,
23736     
23737     //private
23738     addicon : false,
23739     editicon: false,
23740     
23741     // element that contains real text value.. (when hidden is used..)
23742      
23743     // private
23744     onRender : function(ct, position){
23745         Roo.form.ComboBox.superclass.onRender.call(this, ct, position);
23746         if(this.hiddenName){
23747             this.hiddenField = this.el.insertSibling({tag:'input', type:'hidden', name: this.hiddenName, id:  (this.hiddenId||this.hiddenName)},
23748                     'before', true);
23749             this.hiddenField.value =
23750                 this.hiddenValue !== undefined ? this.hiddenValue :
23751                 this.value !== undefined ? this.value : '';
23752
23753             // prevent input submission
23754             this.el.dom.removeAttribute('name');
23755              
23756              
23757         }
23758         if(Roo.isGecko){
23759             this.el.dom.setAttribute('autocomplete', 'off');
23760         }
23761
23762         var cls = 'x-combo-list';
23763
23764         this.list = new Roo.Layer({
23765             shadow: this.shadow, cls: [cls, this.listClass].join(' '), constrain:false
23766         });
23767
23768         var lw = this.listWidth || Math.max(this.wrap.getWidth(), this.minListWidth);
23769         this.list.setWidth(lw);
23770         this.list.swallowEvent('mousewheel');
23771         this.assetHeight = 0;
23772
23773         if(this.title){
23774             this.header = this.list.createChild({cls:cls+'-hd', html: this.title});
23775             this.assetHeight += this.header.getHeight();
23776         }
23777
23778         this.innerList = this.list.createChild({cls:cls+'-inner'});
23779         this.innerList.on('mouseover', this.onViewOver, this);
23780         this.innerList.on('mousemove', this.onViewMove, this);
23781         this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
23782         
23783         if(this.allowBlank && !this.pageSize && !this.disableClear){
23784             this.footer = this.list.createChild({cls:cls+'-ft'});
23785             this.pageTb = new Roo.Toolbar(this.footer);
23786            
23787         }
23788         if(this.pageSize){
23789             this.footer = this.list.createChild({cls:cls+'-ft'});
23790             this.pageTb = new Roo.PagingToolbar(this.footer, this.store,
23791                     {pageSize: this.pageSize});
23792             
23793         }
23794         
23795         if (this.pageTb && this.allowBlank && !this.disableClear) {
23796             var _this = this;
23797             this.pageTb.add(new Roo.Toolbar.Fill(), {
23798                 cls: 'x-btn-icon x-btn-clear',
23799                 text: '&#160;',
23800                 handler: function()
23801                 {
23802                     _this.collapse();
23803                     _this.clearValue();
23804                     _this.onSelect(false, -1);
23805                 }
23806             });
23807         }
23808         if (this.footer) {
23809             this.assetHeight += this.footer.getHeight();
23810         }
23811         
23812
23813         if(!this.tpl){
23814             this.tpl = '<div class="'+cls+'-item">{' + this.displayField + '}</div>';
23815         }
23816
23817         this.view = new Roo.View(this.innerList, this.tpl, {
23818             singleSelect:true, store: this.store, selectedClass: this.selectedClass
23819         });
23820
23821         this.view.on('click', this.onViewClick, this);
23822
23823         this.store.on('beforeload', this.onBeforeLoad, this);
23824         this.store.on('load', this.onLoad, this);
23825         this.store.on('loadexception', this.onLoadException, this);
23826
23827         if(this.resizable){
23828             this.resizer = new Roo.Resizable(this.list,  {
23829                pinned:true, handles:'se'
23830             });
23831             this.resizer.on('resize', function(r, w, h){
23832                 this.maxHeight = h-this.handleHeight-this.list.getFrameWidth('tb')-this.assetHeight;
23833                 this.listWidth = w;
23834                 this.innerList.setWidth(w - this.list.getFrameWidth('lr'));
23835                 this.restrictHeight();
23836             }, this);
23837             this[this.pageSize?'footer':'innerList'].setStyle('margin-bottom', this.handleHeight+'px');
23838         }
23839         if(!this.editable){
23840             this.editable = true;
23841             this.setEditable(false);
23842         }  
23843         
23844         
23845         if (typeof(this.events.add.listeners) != 'undefined') {
23846             
23847             this.addicon = this.wrap.createChild(
23848                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-add' });  
23849        
23850             this.addicon.on('click', function(e) {
23851                 this.fireEvent('add', this);
23852             }, this);
23853         }
23854         if (typeof(this.events.edit.listeners) != 'undefined') {
23855             
23856             this.editicon = this.wrap.createChild(
23857                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-edit' });  
23858             if (this.addicon) {
23859                 this.editicon.setStyle('margin-left', '40px');
23860             }
23861             this.editicon.on('click', function(e) {
23862                 
23863                 // we fire even  if inothing is selected..
23864                 this.fireEvent('edit', this, this.lastData );
23865                 
23866             }, this);
23867         }
23868         
23869         
23870         
23871     },
23872
23873     // private
23874     initEvents : function(){
23875         Roo.form.ComboBox.superclass.initEvents.call(this);
23876
23877         this.keyNav = new Roo.KeyNav(this.el, {
23878             "up" : function(e){
23879                 this.inKeyMode = true;
23880                 this.selectPrev();
23881             },
23882
23883             "down" : function(e){
23884                 if(!this.isExpanded()){
23885                     this.onTriggerClick();
23886                 }else{
23887                     this.inKeyMode = true;
23888                     this.selectNext();
23889                 }
23890             },
23891
23892             "enter" : function(e){
23893                 this.onViewClick();
23894                 //return true;
23895             },
23896
23897             "esc" : function(e){
23898                 this.collapse();
23899             },
23900
23901             "tab" : function(e){
23902                 this.onViewClick(false);
23903                 this.fireEvent("specialkey", this, e);
23904                 return true;
23905             },
23906
23907             scope : this,
23908
23909             doRelay : function(foo, bar, hname){
23910                 if(hname == 'down' || this.scope.isExpanded()){
23911                    return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
23912                 }
23913                 return true;
23914             },
23915
23916             forceKeyDown: true
23917         });
23918         this.queryDelay = Math.max(this.queryDelay || 10,
23919                 this.mode == 'local' ? 10 : 250);
23920         this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
23921         if(this.typeAhead){
23922             this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
23923         }
23924         if(this.editable !== false){
23925             this.el.on("keyup", this.onKeyUp, this);
23926         }
23927         if(this.forceSelection){
23928             this.on('blur', this.doForce, this);
23929         }
23930     },
23931
23932     onDestroy : function(){
23933         if(this.view){
23934             this.view.setStore(null);
23935             this.view.el.removeAllListeners();
23936             this.view.el.remove();
23937             this.view.purgeListeners();
23938         }
23939         if(this.list){
23940             this.list.destroy();
23941         }
23942         if(this.store){
23943             this.store.un('beforeload', this.onBeforeLoad, this);
23944             this.store.un('load', this.onLoad, this);
23945             this.store.un('loadexception', this.onLoadException, this);
23946         }
23947         Roo.form.ComboBox.superclass.onDestroy.call(this);
23948     },
23949
23950     // private
23951     fireKey : function(e){
23952         if(e.isNavKeyPress() && !this.list.isVisible()){
23953             this.fireEvent("specialkey", this, e);
23954         }
23955     },
23956
23957     // private
23958     onResize: function(w, h){
23959         Roo.form.ComboBox.superclass.onResize.apply(this, arguments);
23960         
23961         if(typeof w != 'number'){
23962             // we do not handle it!?!?
23963             return;
23964         }
23965         var tw = this.trigger.getWidth();
23966         tw += this.addicon ? this.addicon.getWidth() : 0;
23967         tw += this.editicon ? this.editicon.getWidth() : 0;
23968         var x = w - tw;
23969         this.el.setWidth( this.adjustWidth('input', x));
23970             
23971         this.trigger.setStyle('left', x+'px');
23972         
23973         if(this.list && this.listWidth === undefined){
23974             var lw = Math.max(x + this.trigger.getWidth(), this.minListWidth);
23975             this.list.setWidth(lw);
23976             this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
23977         }
23978         
23979     
23980         
23981     },
23982
23983     /**
23984      * Allow or prevent the user from directly editing the field text.  If false is passed,
23985      * the user will only be able to select from the items defined in the dropdown list.  This method
23986      * is the runtime equivalent of setting the 'editable' config option at config time.
23987      * @param {Boolean} value True to allow the user to directly edit the field text
23988      */
23989     setEditable : function(value){
23990         if(value == this.editable){
23991             return;
23992         }
23993         this.editable = value;
23994         if(!value){
23995             this.el.dom.setAttribute('readOnly', true);
23996             this.el.on('mousedown', this.onTriggerClick,  this);
23997             this.el.addClass('x-combo-noedit');
23998         }else{
23999             this.el.dom.setAttribute('readOnly', false);
24000             this.el.un('mousedown', this.onTriggerClick,  this);
24001             this.el.removeClass('x-combo-noedit');
24002         }
24003     },
24004
24005     // private
24006     onBeforeLoad : function(){
24007         if(!this.hasFocus){
24008             return;
24009         }
24010         this.innerList.update(this.loadingText ?
24011                '<div class="loading-indicator">'+this.loadingText+'</div>' : '');
24012         this.restrictHeight();
24013         this.selectedIndex = -1;
24014     },
24015
24016     // private
24017     onLoad : function(){
24018         if(!this.hasFocus){
24019             return;
24020         }
24021         if(this.store.getCount() > 0){
24022             this.expand();
24023             this.restrictHeight();
24024             if(this.lastQuery == this.allQuery){
24025                 if(this.editable){
24026                     this.el.dom.select();
24027                 }
24028                 if(!this.selectByValue(this.value, true)){
24029                     this.select(0, true);
24030                 }
24031             }else{
24032                 this.selectNext();
24033                 if(this.typeAhead && this.lastKey != Roo.EventObject.BACKSPACE && this.lastKey != Roo.EventObject.DELETE){
24034                     this.taTask.delay(this.typeAheadDelay);
24035                 }
24036             }
24037         }else{
24038             this.onEmptyResults();
24039         }
24040         //this.el.focus();
24041     },
24042     // private
24043     onLoadException : function()
24044     {
24045         this.collapse();
24046         Roo.log(this.store.reader.jsonData);
24047         if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
24048             Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
24049         }
24050         
24051         
24052     },
24053     // private
24054     onTypeAhead : function(){
24055         if(this.store.getCount() > 0){
24056             var r = this.store.getAt(0);
24057             var newValue = r.data[this.displayField];
24058             var len = newValue.length;
24059             var selStart = this.getRawValue().length;
24060             if(selStart != len){
24061                 this.setRawValue(newValue);
24062                 this.selectText(selStart, newValue.length);
24063             }
24064         }
24065     },
24066
24067     // private
24068     onSelect : function(record, index){
24069         if(this.fireEvent('beforeselect', this, record, index) !== false){
24070             this.setFromData(index > -1 ? record.data : false);
24071             this.collapse();
24072             this.fireEvent('select', this, record, index);
24073         }
24074     },
24075
24076     /**
24077      * Returns the currently selected field value or empty string if no value is set.
24078      * @return {String} value The selected value
24079      */
24080     getValue : function(){
24081         if(this.valueField){
24082             return typeof this.value != 'undefined' ? this.value : '';
24083         }else{
24084             return Roo.form.ComboBox.superclass.getValue.call(this);
24085         }
24086     },
24087
24088     /**
24089      * Clears any text/value currently set in the field
24090      */
24091     clearValue : function(){
24092         if(this.hiddenField){
24093             this.hiddenField.value = '';
24094         }
24095         this.value = '';
24096         this.setRawValue('');
24097         this.lastSelectionText = '';
24098         
24099     },
24100
24101     /**
24102      * Sets the specified value into the field.  If the value finds a match, the corresponding record text
24103      * will be displayed in the field.  If the value does not match the data value of an existing item,
24104      * and the valueNotFoundText config option is defined, it will be displayed as the default field text.
24105      * Otherwise the field will be blank (although the value will still be set).
24106      * @param {String} value The value to match
24107      */
24108     setValue : function(v){
24109         var text = v;
24110         if(this.valueField){
24111             var r = this.findRecord(this.valueField, v);
24112             if(r){
24113                 text = r.data[this.displayField];
24114             }else if(this.valueNotFoundText !== undefined){
24115                 text = this.valueNotFoundText;
24116             }
24117         }
24118         this.lastSelectionText = text;
24119         if(this.hiddenField){
24120             this.hiddenField.value = v;
24121         }
24122         Roo.form.ComboBox.superclass.setValue.call(this, text);
24123         this.value = v;
24124     },
24125     /**
24126      * @property {Object} the last set data for the element
24127      */
24128     
24129     lastData : false,
24130     /**
24131      * Sets the value of the field based on a object which is related to the record format for the store.
24132      * @param {Object} value the value to set as. or false on reset?
24133      */
24134     setFromData : function(o){
24135         var dv = ''; // display value
24136         var vv = ''; // value value..
24137         this.lastData = o;
24138         if (this.displayField) {
24139             dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
24140         } else {
24141             // this is an error condition!!!
24142             Roo.log('no  displayField value set for '+ (this.name ? this.name : this.id));
24143         }
24144         
24145         if(this.valueField){
24146             vv = !o || typeof(o[this.valueField]) == 'undefined' ? dv : o[this.valueField];
24147         }
24148         if(this.hiddenField){
24149             this.hiddenField.value = vv;
24150             
24151             this.lastSelectionText = dv;
24152             Roo.form.ComboBox.superclass.setValue.call(this, dv);
24153             this.value = vv;
24154             return;
24155         }
24156         // no hidden field.. - we store the value in 'value', but still display
24157         // display field!!!!
24158         this.lastSelectionText = dv;
24159         Roo.form.ComboBox.superclass.setValue.call(this, dv);
24160         this.value = vv;
24161         
24162         
24163     },
24164     // private
24165     reset : function(){
24166         // overridden so that last data is reset..
24167         this.setValue(this.originalValue);
24168         this.clearInvalid();
24169         this.lastData = false;
24170         if (this.view) {
24171             this.view.clearSelections();
24172         }
24173     },
24174     // private
24175     findRecord : function(prop, value){
24176         var record;
24177         if(this.store.getCount() > 0){
24178             this.store.each(function(r){
24179                 if(r.data[prop] == value){
24180                     record = r;
24181                     return false;
24182                 }
24183                 return true;
24184             });
24185         }
24186         return record;
24187     },
24188     
24189     getName: function()
24190     {
24191         // returns hidden if it's set..
24192         if (!this.rendered) {return ''};
24193         return !this.hiddenName && this.el.dom.name  ? this.el.dom.name : (this.hiddenName || '');
24194         
24195     },
24196     // private
24197     onViewMove : function(e, t){
24198         this.inKeyMode = false;
24199     },
24200
24201     // private
24202     onViewOver : function(e, t){
24203         if(this.inKeyMode){ // prevent key nav and mouse over conflicts
24204             return;
24205         }
24206         var item = this.view.findItemFromChild(t);
24207         if(item){
24208             var index = this.view.indexOf(item);
24209             this.select(index, false);
24210         }
24211     },
24212
24213     // private
24214     onViewClick : function(doFocus)
24215     {
24216         var index = this.view.getSelectedIndexes()[0];
24217         var r = this.store.getAt(index);
24218         if(r){
24219             this.onSelect(r, index);
24220         }
24221         if(doFocus !== false && !this.blockFocus){
24222             this.el.focus();
24223         }
24224     },
24225
24226     // private
24227     restrictHeight : function(){
24228         this.innerList.dom.style.height = '';
24229         var inner = this.innerList.dom;
24230         var h = Math.max(inner.clientHeight, inner.offsetHeight, inner.scrollHeight);
24231         this.innerList.setHeight(h < this.maxHeight ? 'auto' : this.maxHeight);
24232         this.list.beginUpdate();
24233         this.list.setHeight(this.innerList.getHeight()+this.list.getFrameWidth('tb')+(this.resizable?this.handleHeight:0)+this.assetHeight);
24234         this.list.alignTo(this.el, this.listAlign);
24235         this.list.endUpdate();
24236     },
24237
24238     // private
24239     onEmptyResults : function(){
24240         this.collapse();
24241     },
24242
24243     /**
24244      * Returns true if the dropdown list is expanded, else false.
24245      */
24246     isExpanded : function(){
24247         return this.list.isVisible();
24248     },
24249
24250     /**
24251      * Select an item in the dropdown list by its data value. This function does NOT cause the select event to fire.
24252      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
24253      * @param {String} value The data value of the item to select
24254      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
24255      * selected item if it is not currently in view (defaults to true)
24256      * @return {Boolean} True if the value matched an item in the list, else false
24257      */
24258     selectByValue : function(v, scrollIntoView){
24259         if(v !== undefined && v !== null){
24260             var r = this.findRecord(this.valueField || this.displayField, v);
24261             if(r){
24262                 this.select(this.store.indexOf(r), scrollIntoView);
24263                 return true;
24264             }
24265         }
24266         return false;
24267     },
24268
24269     /**
24270      * Select an item in the dropdown list by its numeric index in the list. This function does NOT cause the select event to fire.
24271      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
24272      * @param {Number} index The zero-based index of the list item to select
24273      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
24274      * selected item if it is not currently in view (defaults to true)
24275      */
24276     select : function(index, scrollIntoView){
24277         this.selectedIndex = index;
24278         this.view.select(index);
24279         if(scrollIntoView !== false){
24280             var el = this.view.getNode(index);
24281             if(el){
24282                 this.innerList.scrollChildIntoView(el, false);
24283             }
24284         }
24285     },
24286
24287     // private
24288     selectNext : function(){
24289         var ct = this.store.getCount();
24290         if(ct > 0){
24291             if(this.selectedIndex == -1){
24292                 this.select(0);
24293             }else if(this.selectedIndex < ct-1){
24294                 this.select(this.selectedIndex+1);
24295             }
24296         }
24297     },
24298
24299     // private
24300     selectPrev : function(){
24301         var ct = this.store.getCount();
24302         if(ct > 0){
24303             if(this.selectedIndex == -1){
24304                 this.select(0);
24305             }else if(this.selectedIndex != 0){
24306                 this.select(this.selectedIndex-1);
24307             }
24308         }
24309     },
24310
24311     // private
24312     onKeyUp : function(e){
24313         if(this.editable !== false && !e.isSpecialKey()){
24314             this.lastKey = e.getKey();
24315             this.dqTask.delay(this.queryDelay);
24316         }
24317     },
24318
24319     // private
24320     validateBlur : function(){
24321         return !this.list || !this.list.isVisible();   
24322     },
24323
24324     // private
24325     initQuery : function(){
24326         this.doQuery(this.getRawValue());
24327     },
24328
24329     // private
24330     doForce : function(){
24331         if(this.el.dom.value.length > 0){
24332             this.el.dom.value =
24333                 this.lastSelectionText === undefined ? '' : this.lastSelectionText;
24334              
24335         }
24336     },
24337
24338     /**
24339      * Execute a query to filter the dropdown list.  Fires the beforequery event prior to performing the
24340      * query allowing the query action to be canceled if needed.
24341      * @param {String} query The SQL query to execute
24342      * @param {Boolean} forceAll True to force the query to execute even if there are currently fewer characters
24343      * in the field than the minimum specified by the minChars config option.  It also clears any filter previously
24344      * saved in the current store (defaults to false)
24345      */
24346     doQuery : function(q, forceAll){
24347         if(q === undefined || q === null){
24348             q = '';
24349         }
24350         var qe = {
24351             query: q,
24352             forceAll: forceAll,
24353             combo: this,
24354             cancel:false
24355         };
24356         if(this.fireEvent('beforequery', qe)===false || qe.cancel){
24357             return false;
24358         }
24359         q = qe.query;
24360         forceAll = qe.forceAll;
24361         if(forceAll === true || (q.length >= this.minChars)){
24362             if(this.lastQuery != q || this.alwaysQuery){
24363                 this.lastQuery = q;
24364                 if(this.mode == 'local'){
24365                     this.selectedIndex = -1;
24366                     if(forceAll){
24367                         this.store.clearFilter();
24368                     }else{
24369                         this.store.filter(this.displayField, q);
24370                     }
24371                     this.onLoad();
24372                 }else{
24373                     this.store.baseParams[this.queryParam] = q;
24374                     this.store.load({
24375                         params: this.getParams(q)
24376                     });
24377                     this.expand();
24378                 }
24379             }else{
24380                 this.selectedIndex = -1;
24381                 this.onLoad();   
24382             }
24383         }
24384     },
24385
24386     // private
24387     getParams : function(q){
24388         var p = {};
24389         //p[this.queryParam] = q;
24390         if(this.pageSize){
24391             p.start = 0;
24392             p.limit = this.pageSize;
24393         }
24394         return p;
24395     },
24396
24397     /**
24398      * Hides the dropdown list if it is currently expanded. Fires the 'collapse' event on completion.
24399      */
24400     collapse : function(){
24401         if(!this.isExpanded()){
24402             return;
24403         }
24404         this.list.hide();
24405         Roo.get(document).un('mousedown', this.collapseIf, this);
24406         Roo.get(document).un('mousewheel', this.collapseIf, this);
24407         if (!this.editable) {
24408             Roo.get(document).un('keydown', this.listKeyPress, this);
24409         }
24410         this.fireEvent('collapse', this);
24411     },
24412
24413     // private
24414     collapseIf : function(e){
24415         if(!e.within(this.wrap) && !e.within(this.list)){
24416             this.collapse();
24417         }
24418     },
24419
24420     /**
24421      * Expands the dropdown list if it is currently hidden. Fires the 'expand' event on completion.
24422      */
24423     expand : function(){
24424         if(this.isExpanded() || !this.hasFocus){
24425             return;
24426         }
24427         this.list.alignTo(this.el, this.listAlign);
24428         this.list.show();
24429         Roo.get(document).on('mousedown', this.collapseIf, this);
24430         Roo.get(document).on('mousewheel', this.collapseIf, this);
24431         if (!this.editable) {
24432             Roo.get(document).on('keydown', this.listKeyPress, this);
24433         }
24434         
24435         this.fireEvent('expand', this);
24436     },
24437
24438     // private
24439     // Implements the default empty TriggerField.onTriggerClick function
24440     onTriggerClick : function(){
24441         if(this.disabled){
24442             return;
24443         }
24444         if(this.isExpanded()){
24445             this.collapse();
24446             if (!this.blockFocus) {
24447                 this.el.focus();
24448             }
24449             
24450         }else {
24451             this.hasFocus = true;
24452             if(this.triggerAction == 'all') {
24453                 this.doQuery(this.allQuery, true);
24454             } else {
24455                 this.doQuery(this.getRawValue());
24456             }
24457             if (!this.blockFocus) {
24458                 this.el.focus();
24459             }
24460         }
24461     },
24462     listKeyPress : function(e)
24463     {
24464         //Roo.log('listkeypress');
24465         // scroll to first matching element based on key pres..
24466         if (e.isSpecialKey()) {
24467             return false;
24468         }
24469         var k = String.fromCharCode(e.getKey()).toUpperCase();
24470         //Roo.log(k);
24471         var match  = false;
24472         var csel = this.view.getSelectedNodes();
24473         var cselitem = false;
24474         if (csel.length) {
24475             var ix = this.view.indexOf(csel[0]);
24476             cselitem  = this.store.getAt(ix);
24477             if (!cselitem.get(this.displayField) || cselitem.get(this.displayField).substring(0,1).toUpperCase() != k) {
24478                 cselitem = false;
24479             }
24480             
24481         }
24482         
24483         this.store.each(function(v) { 
24484             if (cselitem) {
24485                 // start at existing selection.
24486                 if (cselitem.id == v.id) {
24487                     cselitem = false;
24488                 }
24489                 return;
24490             }
24491                 
24492             if (v.get(this.displayField) && v.get(this.displayField).substring(0,1).toUpperCase() == k) {
24493                 match = this.store.indexOf(v);
24494                 return false;
24495             }
24496         }, this);
24497         
24498         if (match === false) {
24499             return true; // no more action?
24500         }
24501         // scroll to?
24502         this.view.select(match);
24503         var sn = Roo.get(this.view.getSelectedNodes()[0])
24504         sn.scrollIntoView(sn.dom.parentNode, false);
24505     }
24506
24507     /** 
24508     * @cfg {Boolean} grow 
24509     * @hide 
24510     */
24511     /** 
24512     * @cfg {Number} growMin 
24513     * @hide 
24514     */
24515     /** 
24516     * @cfg {Number} growMax 
24517     * @hide 
24518     */
24519     /**
24520      * @hide
24521      * @method autoSize
24522      */
24523 });/*
24524  * Copyright(c) 2010-2012, Roo J Solutions Limited
24525  *
24526  * Licence LGPL
24527  *
24528  */
24529
24530 /**
24531  * @class Roo.form.ComboBoxArray
24532  * @extends Roo.form.TextField
24533  * A facebook style adder... for lists of email / people / countries  etc...
24534  * pick multiple items from a combo box, and shows each one.
24535  *
24536  *  Fred [x]  Brian [x]  [Pick another |v]
24537  *
24538  *
24539  *  For this to work: it needs various extra information
24540  *    - normal combo problay has
24541  *      name, hiddenName
24542  *    + displayField, valueField
24543  *
24544  *    For our purpose...
24545  *
24546  *
24547  *   If we change from 'extends' to wrapping...
24548  *   
24549  *  
24550  *
24551  
24552  
24553  * @constructor
24554  * Create a new ComboBoxArray.
24555  * @param {Object} config Configuration options
24556  */
24557  
24558
24559 Roo.form.ComboBoxArray = function(config)
24560 {
24561     
24562     Roo.form.ComboBoxArray.superclass.constructor.call(this, config);
24563     
24564     this.items = new Roo.util.MixedCollection(false);
24565     
24566     // construct the child combo...
24567     
24568     
24569     
24570     
24571    
24572     
24573 }
24574
24575  
24576 Roo.extend(Roo.form.ComboBoxArray, Roo.form.TextField,
24577
24578     /**
24579      * @cfg {Roo.form.Combo} combo The combo box that is wrapped
24580      */
24581     
24582     lastData : false,
24583     
24584     // behavies liek a hiddne field
24585     inputType:      'hidden',
24586     /**
24587      * @cfg {Number} width The width of the box that displays the selected element
24588      */ 
24589     width:          300,
24590
24591     
24592     
24593     /**
24594      * @cfg {String} name    The name of the visable items on this form (eg. titles not ids)
24595      */
24596     name : false,
24597     /**
24598      * @cfg {String} hiddenName    The hidden name of the field, often contains an comma seperated list of names
24599      */
24600     hiddenName : false,
24601     
24602     
24603     // private the array of items that are displayed..
24604     items  : false,
24605     // private - the hidden field el.
24606     hiddenEl : false,
24607     // private - the filed el..
24608     el : false,
24609     
24610     //validateValue : function() { return true; }, // all values are ok!
24611     //onAddClick: function() { },
24612     
24613     onRender : function(ct, position) 
24614     {
24615         
24616         // create the standard hidden element
24617         //Roo.form.ComboBoxArray.superclass.onRender.call(this, ct, position);
24618         
24619         
24620         // give fake names to child combo;
24621         this.combo.hiddenName = this.hiddenName ? (this.hiddenName+'-subcombo') : this.hiddenName;
24622         this.combo.name = this.name? (this.name+'-subcombo') : this.name;
24623         
24624         this.combo = Roo.factory(this.combo, Roo.form);
24625         this.combo.onRender(ct, position);
24626         if (typeof(this.combo.width) != 'undefined') {
24627             this.combo.onResize(this.combo.width,0);
24628         }
24629         
24630         this.combo.initEvents();
24631         
24632         // assigned so form know we need to do this..
24633         this.store          = this.combo.store;
24634         this.valueField     = this.combo.valueField;
24635         this.displayField   = this.combo.displayField ;
24636         
24637         
24638         this.combo.wrap.addClass('x-cbarray-grp');
24639         
24640         var cbwrap = this.combo.wrap.createChild(
24641             {tag: 'div', cls: 'x-cbarray-cb'},
24642             this.combo.el.dom
24643         );
24644         
24645              
24646         this.hiddenEl = this.combo.wrap.createChild({
24647             tag: 'input',  type:'hidden' , name: this.hiddenName, value : ''
24648         });
24649         this.el = this.combo.wrap.createChild({
24650             tag: 'input',  type:'hidden' , name: this.name, value : ''
24651         });
24652          //   this.el.dom.removeAttribute("name");
24653         
24654         
24655         this.outerWrap = this.combo.wrap;
24656         this.wrap = cbwrap;
24657         
24658         this.outerWrap.setWidth(this.width);
24659         this.outerWrap.dom.removeChild(this.el.dom);
24660         
24661         this.wrap.dom.appendChild(this.el.dom);
24662         this.outerWrap.dom.removeChild(this.combo.trigger.dom);
24663         this.combo.wrap.dom.appendChild(this.combo.trigger.dom);
24664         
24665         this.combo.trigger.setStyle('position','relative');
24666         this.combo.trigger.setStyle('left', '0px');
24667         this.combo.trigger.setStyle('top', '2px');
24668         
24669         this.combo.el.setStyle('vertical-align', 'text-bottom');
24670         
24671         //this.trigger.setStyle('vertical-align', 'top');
24672         
24673         // this should use the code from combo really... on('add' ....)
24674         if (this.adder) {
24675             
24676         
24677             this.adder = this.outerWrap.createChild(
24678                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-adder', style: 'margin-left:2px'});  
24679             var _t = this;
24680             this.adder.on('click', function(e) {
24681                 _t.fireEvent('adderclick', this, e);
24682             }, _t);
24683         }
24684         //var _t = this;
24685         //this.adder.on('click', this.onAddClick, _t);
24686         
24687         
24688         this.combo.on('select', function(cb, rec, ix) {
24689             this.addItem(rec.data);
24690             
24691             cb.setValue('');
24692             cb.el.dom.value = '';
24693             //cb.lastData = rec.data;
24694             // add to list
24695             
24696         }, this);
24697         
24698         
24699     },
24700     
24701     
24702     getName: function()
24703     {
24704         // returns hidden if it's set..
24705         if (!this.rendered) {return ''};
24706         return  this.hiddenName ? this.hiddenName : this.name;
24707         
24708     },
24709     
24710     
24711     onResize: function(w, h){
24712         
24713         return;
24714         // not sure if this is needed..
24715         //this.combo.onResize(w,h);
24716         
24717         if(typeof w != 'number'){
24718             // we do not handle it!?!?
24719             return;
24720         }
24721         var tw = this.combo.trigger.getWidth();
24722         tw += this.addicon ? this.addicon.getWidth() : 0;
24723         tw += this.editicon ? this.editicon.getWidth() : 0;
24724         var x = w - tw;
24725         this.combo.el.setWidth( this.combo.adjustWidth('input', x));
24726             
24727         this.combo.trigger.setStyle('left', '0px');
24728         
24729         if(this.list && this.listWidth === undefined){
24730             var lw = Math.max(x + this.combo.trigger.getWidth(), this.combo.minListWidth);
24731             this.list.setWidth(lw);
24732             this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
24733         }
24734         
24735     
24736         
24737     },
24738     
24739     addItem: function(rec)
24740     {
24741         var valueField = this.combo.valueField;
24742         var displayField = this.combo.displayField;
24743         if (this.items.indexOfKey(rec[valueField]) > -1) {
24744             //console.log("GOT " + rec.data.id);
24745             return;
24746         }
24747         
24748         var x = new Roo.form.ComboBoxArray.Item({
24749             //id : rec[this.idField],
24750             data : rec,
24751             displayField : displayField ,
24752             tipField : displayField ,
24753             cb : this
24754         });
24755         // use the 
24756         this.items.add(rec[valueField],x);
24757         // add it before the element..
24758         this.updateHiddenEl();
24759         x.render(this.outerWrap, this.wrap.dom);
24760         // add the image handler..
24761     },
24762     
24763     updateHiddenEl : function()
24764     {
24765         this.validate();
24766         if (!this.hiddenEl) {
24767             return;
24768         }
24769         var ar = [];
24770         var idField = this.combo.valueField;
24771         
24772         this.items.each(function(f) {
24773             ar.push(f.data[idField]);
24774            
24775         });
24776         this.hiddenEl.dom.value = ar.join(',');
24777         this.validate();
24778     },
24779     
24780     reset : function()
24781     {
24782         //Roo.form.ComboBoxArray.superclass.reset.call(this); 
24783         this.items.each(function(f) {
24784            f.remove(); 
24785         });
24786         this.el.dom.value = '';
24787         if (this.hiddenEl) {
24788             this.hiddenEl.dom.value = '';
24789         }
24790         
24791     },
24792     getValue: function()
24793     {
24794         return this.hiddenEl ? this.hiddenEl.dom.value : '';
24795     },
24796     setValue: function(v) // not a valid action - must use addItems..
24797     {
24798          
24799         this.reset();
24800         
24801         
24802         
24803         if (this.store.isLocal && (typeof(v) == 'string')) {
24804             // then we can use the store to find the values..
24805             // comma seperated at present.. this needs to allow JSON based encoding..
24806             this.hiddenEl.value  = v;
24807             var v_ar = [];
24808             Roo.each(v.split(','), function(k) {
24809                 Roo.log("CHECK " + this.valueField + ',' + k);
24810                 var li = this.store.query(this.valueField, k);
24811                 if (!li.length) {
24812                     return;
24813                 }
24814                 var add = {};
24815                 add[this.valueField] = k;
24816                 add[this.displayField] = li.item(0).data[this.displayField];
24817                 
24818                 this.addItem(add);
24819             }, this) 
24820              
24821         }
24822         if (typeof(v) == 'object') {
24823             // then let's assume it's an array of objects..
24824             Roo.each(v, function(l) {
24825                 this.addItem(l);
24826             }, this);
24827              
24828         }
24829         
24830         
24831     },
24832     setFromData: function(v)
24833     {
24834         // this recieves an object, if setValues is called.
24835         this.reset();
24836         this.el.dom.value = v[this.displayField];
24837         this.hiddenEl.dom.value = v[this.valueField];
24838         if (typeof(v[this.valueField]) != 'string' || !v[this.valueField].length) {
24839             return;
24840         }
24841         var kv = v[this.valueField];
24842         var dv = v[this.displayField];
24843         kv = typeof(kv) != 'string' ? '' : kv;
24844         dv = typeof(dv) != 'string' ? '' : dv;
24845         
24846         
24847         var keys = kv.split(',');
24848         var display = dv.split(',');
24849         for (var i = 0 ; i < keys.length; i++) {
24850             
24851             add = {};
24852             add[this.valueField] = keys[i];
24853             add[this.displayField] = display[i];
24854             this.addItem(add);
24855         }
24856       
24857         
24858     },
24859     
24860     
24861     validateValue : function(value){
24862         return Roo.form.ComboBoxArray.superclass.validateValue.call(this, this.getValue());
24863         
24864     }
24865     
24866 });
24867
24868
24869
24870 /**
24871  * @class Roo.form.ComboBoxArray.Item
24872  * @extends Roo.BoxComponent
24873  * A selected item in the list
24874  *  Fred [x]  Brian [x]  [Pick another |v]
24875  * 
24876  * @constructor
24877  * Create a new item.
24878  * @param {Object} config Configuration options
24879  */
24880  
24881 Roo.form.ComboBoxArray.Item = function(config) {
24882     config.id = Roo.id();
24883     Roo.form.ComboBoxArray.Item.superclass.constructor.call(this, config);
24884 }
24885
24886 Roo.extend(Roo.form.ComboBoxArray.Item, Roo.BoxComponent, {
24887     data : {},
24888     cb: false,
24889     displayField : false,
24890     tipField : false,
24891     
24892     
24893     defaultAutoCreate : {
24894         tag: 'div',
24895         cls: 'x-cbarray-item',
24896         cn : [ 
24897             { tag: 'div' },
24898             {
24899                 tag: 'img',
24900                 width:16,
24901                 height : 16,
24902                 src : Roo.BLANK_IMAGE_URL ,
24903                 align: 'center'
24904             }
24905         ]
24906         
24907     },
24908     
24909  
24910     onRender : function(ct, position)
24911     {
24912         Roo.form.Field.superclass.onRender.call(this, ct, position);
24913         
24914         if(!this.el){
24915             var cfg = this.getAutoCreate();
24916             this.el = ct.createChild(cfg, position);
24917         }
24918         
24919         this.el.child('img').dom.setAttribute('src', Roo.BLANK_IMAGE_URL);
24920         
24921         this.el.child('div').dom.innerHTML = this.cb.renderer ? 
24922             this.cb.renderer(this.data) :
24923             String.format('{0}',this.data[this.displayField]);
24924         
24925             
24926         this.el.child('div').dom.setAttribute('qtip',
24927                         String.format('{0}',this.data[this.tipField])
24928         );
24929         
24930         this.el.child('img').on('click', this.remove, this);
24931         
24932     },
24933    
24934     remove : function()
24935     {
24936         
24937         this.cb.items.remove(this);
24938         this.el.child('img').un('click', this.remove, this);
24939         this.el.remove();
24940         this.cb.updateHiddenEl();
24941     }
24942     
24943     
24944 });/*
24945  * Based on:
24946  * Ext JS Library 1.1.1
24947  * Copyright(c) 2006-2007, Ext JS, LLC.
24948  *
24949  * Originally Released Under LGPL - original licence link has changed is not relivant.
24950  *
24951  * Fork - LGPL
24952  * <script type="text/javascript">
24953  */
24954 /**
24955  * @class Roo.form.Checkbox
24956  * @extends Roo.form.Field
24957  * Single checkbox field.  Can be used as a direct replacement for traditional checkbox fields.
24958  * @constructor
24959  * Creates a new Checkbox
24960  * @param {Object} config Configuration options
24961  */
24962 Roo.form.Checkbox = function(config){
24963     Roo.form.Checkbox.superclass.constructor.call(this, config);
24964     this.addEvents({
24965         /**
24966          * @event check
24967          * Fires when the checkbox is checked or unchecked.
24968              * @param {Roo.form.Checkbox} this This checkbox
24969              * @param {Boolean} checked The new checked value
24970              */
24971         check : true
24972     });
24973 };
24974
24975 Roo.extend(Roo.form.Checkbox, Roo.form.Field,  {
24976     /**
24977      * @cfg {String} focusClass The CSS class to use when the checkbox receives focus (defaults to undefined)
24978      */
24979     focusClass : undefined,
24980     /**
24981      * @cfg {String} fieldClass The default CSS class for the checkbox (defaults to "x-form-field")
24982      */
24983     fieldClass: "x-form-field",
24984     /**
24985      * @cfg {Boolean} checked True if the the checkbox should render already checked (defaults to false)
24986      */
24987     checked: false,
24988     /**
24989      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
24990      * {tag: "input", type: "checkbox", autocomplete: "off"})
24991      */
24992     defaultAutoCreate : { tag: "input", type: 'hidden', autocomplete: "off"},
24993     /**
24994      * @cfg {String} boxLabel The text that appears beside the checkbox
24995      */
24996     boxLabel : "",
24997     /**
24998      * @cfg {String} inputValue The value that should go into the generated input element's value attribute
24999      */  
25000     inputValue : '1',
25001     /**
25002      * @cfg {String} valueOff The value that should go into the generated input element's value when unchecked.
25003      */
25004      valueOff: '0', // value when not checked..
25005
25006     actionMode : 'viewEl', 
25007     //
25008     // private
25009     itemCls : 'x-menu-check-item x-form-item',
25010     groupClass : 'x-menu-group-item',
25011     inputType : 'hidden',
25012     
25013     
25014     inSetChecked: false, // check that we are not calling self...
25015     
25016     inputElement: false, // real input element?
25017     basedOn: false, // ????
25018     
25019     isFormField: true, // not sure where this is needed!!!!
25020
25021     onResize : function(){
25022         Roo.form.Checkbox.superclass.onResize.apply(this, arguments);
25023         if(!this.boxLabel){
25024             this.el.alignTo(this.wrap, 'c-c');
25025         }
25026     },
25027
25028     initEvents : function(){
25029         Roo.form.Checkbox.superclass.initEvents.call(this);
25030         this.el.on("click", this.onClick,  this);
25031         this.el.on("change", this.onClick,  this);
25032     },
25033
25034
25035     getResizeEl : function(){
25036         return this.wrap;
25037     },
25038
25039     getPositionEl : function(){
25040         return this.wrap;
25041     },
25042
25043     // private
25044     onRender : function(ct, position){
25045         Roo.form.Checkbox.superclass.onRender.call(this, ct, position);
25046         /*
25047         if(this.inputValue !== undefined){
25048             this.el.dom.value = this.inputValue;
25049         }
25050         */
25051         //this.wrap = this.el.wrap({cls: "x-form-check-wrap"});
25052         this.wrap = this.el.wrap({cls: 'x-menu-check-item '});
25053         var viewEl = this.wrap.createChild({ 
25054             tag: 'img', cls: 'x-menu-item-icon', style: 'margin: 0px;' ,src : Roo.BLANK_IMAGE_URL });
25055         this.viewEl = viewEl;   
25056         this.wrap.on('click', this.onClick,  this); 
25057         
25058         this.el.on('DOMAttrModified', this.setFromHidden,  this); //ff
25059         this.el.on('propertychange', this.setFromHidden,  this);  //ie
25060         
25061         
25062         
25063         if(this.boxLabel){
25064             this.wrap.createChild({tag: 'label', htmlFor: this.el.id, cls: 'x-form-cb-label', html: this.boxLabel});
25065         //    viewEl.on('click', this.onClick,  this); 
25066         }
25067         //if(this.checked){
25068             this.setChecked(this.checked);
25069         //}else{
25070             //this.checked = this.el.dom;
25071         //}
25072
25073     },
25074
25075     // private
25076     initValue : Roo.emptyFn,
25077
25078     /**
25079      * Returns the checked state of the checkbox.
25080      * @return {Boolean} True if checked, else false
25081      */
25082     getValue : function(){
25083         if(this.el){
25084             return String(this.el.dom.value) == String(this.inputValue ) ? this.inputValue : this.valueOff;
25085         }
25086         return this.valueOff;
25087         
25088     },
25089
25090         // private
25091     onClick : function(){ 
25092         this.setChecked(!this.checked);
25093
25094         //if(this.el.dom.checked != this.checked){
25095         //    this.setValue(this.el.dom.checked);
25096        // }
25097     },
25098
25099     /**
25100      * Sets the checked state of the checkbox.
25101      * On is always based on a string comparison between inputValue and the param.
25102      * @param {Boolean/String} value - the value to set 
25103      * @param {Boolean/String} suppressEvent - whether to suppress the checkchange event.
25104      */
25105     setValue : function(v,suppressEvent){
25106         
25107         
25108         //this.checked = (v === true || v === 'true' || v == '1' || String(v).toLowerCase() == 'on');
25109         //if(this.el && this.el.dom){
25110         //    this.el.dom.checked = this.checked;
25111         //    this.el.dom.defaultChecked = this.checked;
25112         //}
25113         this.setChecked(String(v) === String(this.inputValue), suppressEvent);
25114         //this.fireEvent("check", this, this.checked);
25115     },
25116     // private..
25117     setChecked : function(state,suppressEvent)
25118     {
25119         if (this.inSetChecked) {
25120             this.checked = state;
25121             return;
25122         }
25123         
25124     
25125         if(this.wrap){
25126             this.wrap[state ? 'addClass' : 'removeClass']('x-menu-item-checked');
25127         }
25128         this.checked = state;
25129         if(suppressEvent !== true){
25130             this.fireEvent('check', this, state);
25131         }
25132         this.inSetChecked = true;
25133         this.el.dom.value = state ? this.inputValue : this.valueOff;
25134         this.inSetChecked = false;
25135         
25136     },
25137     // handle setting of hidden value by some other method!!?!?
25138     setFromHidden: function()
25139     {
25140         if(!this.el){
25141             return;
25142         }
25143         //console.log("SET FROM HIDDEN");
25144         //alert('setFrom hidden');
25145         this.setValue(this.el.dom.value);
25146     },
25147     
25148     onDestroy : function()
25149     {
25150         if(this.viewEl){
25151             Roo.get(this.viewEl).remove();
25152         }
25153          
25154         Roo.form.Checkbox.superclass.onDestroy.call(this);
25155     }
25156
25157 });/*
25158  * Based on:
25159  * Ext JS Library 1.1.1
25160  * Copyright(c) 2006-2007, Ext JS, LLC.
25161  *
25162  * Originally Released Under LGPL - original licence link has changed is not relivant.
25163  *
25164  * Fork - LGPL
25165  * <script type="text/javascript">
25166  */
25167  
25168 /**
25169  * @class Roo.form.Radio
25170  * @extends Roo.form.Checkbox
25171  * Single radio field.  Same as Checkbox, but provided as a convenience for automatically setting the input type.
25172  * Radio grouping is handled automatically by the browser if you give each radio in a group the same name.
25173  * @constructor
25174  * Creates a new Radio
25175  * @param {Object} config Configuration options
25176  */
25177 Roo.form.Radio = function(){
25178     Roo.form.Radio.superclass.constructor.apply(this, arguments);
25179 };
25180 Roo.extend(Roo.form.Radio, Roo.form.Checkbox, {
25181     inputType: 'radio',
25182
25183     /**
25184      * If this radio is part of a group, it will return the selected value
25185      * @return {String}
25186      */
25187     getGroupValue : function(){
25188         return this.el.up('form').child('input[name='+this.el.dom.name+']:checked', true).value;
25189     },
25190     
25191     
25192     onRender : function(ct, position){
25193         Roo.form.Checkbox.superclass.onRender.call(this, ct, position);
25194         
25195         if(this.inputValue !== undefined){
25196             this.el.dom.value = this.inputValue;
25197         }
25198          
25199         this.wrap = this.el.wrap({cls: "x-form-check-wrap"});
25200         //this.wrap = this.el.wrap({cls: 'x-menu-check-item '});
25201         //var viewEl = this.wrap.createChild({ 
25202         //    tag: 'img', cls: 'x-menu-item-icon', style: 'margin: 0px;' ,src : Roo.BLANK_IMAGE_URL });
25203         //this.viewEl = viewEl;   
25204         //this.wrap.on('click', this.onClick,  this); 
25205         
25206         //this.el.on('DOMAttrModified', this.setFromHidden,  this); //ff
25207         //this.el.on('propertychange', this.setFromHidden,  this);  //ie
25208         
25209         
25210         
25211         if(this.boxLabel){
25212             this.wrap.createChild({tag: 'label', htmlFor: this.el.id, cls: 'x-form-cb-label', html: this.boxLabel});
25213         //    viewEl.on('click', this.onClick,  this); 
25214         }
25215          if(this.checked){
25216             this.el.dom.checked =   'checked' ;
25217         }
25218          
25219     } 
25220     
25221     
25222 });//<script type="text/javascript">
25223
25224 /*
25225  * Ext JS Library 1.1.1
25226  * Copyright(c) 2006-2007, Ext JS, LLC.
25227  * licensing@extjs.com
25228  * 
25229  * http://www.extjs.com/license
25230  */
25231  
25232  /*
25233   * 
25234   * Known bugs:
25235   * Default CSS appears to render it as fixed text by default (should really be Sans-Serif)
25236   * - IE ? - no idea how much works there.
25237   * 
25238   * 
25239   * 
25240   */
25241  
25242
25243 /**
25244  * @class Ext.form.HtmlEditor
25245  * @extends Ext.form.Field
25246  * Provides a lightweight HTML Editor component.
25247  *
25248  * This has been tested on Fireforx / Chrome.. IE may not be so great..
25249  * 
25250  * <br><br><b>Note: The focus/blur and validation marking functionality inherited from Ext.form.Field is NOT
25251  * supported by this editor.</b><br/><br/>
25252  * An Editor is a sensitive component that can't be used in all spots standard fields can be used. Putting an Editor within
25253  * any element that has display set to 'none' can cause problems in Safari and Firefox.<br/><br/>
25254  */
25255 Roo.form.HtmlEditor = Roo.extend(Roo.form.Field, {
25256       /**
25257      * @cfg {Array} toolbars Array of toolbars. - defaults to just the Standard one
25258      */
25259     toolbars : false,
25260     /**
25261      * @cfg {String} createLinkText The default text for the create link prompt
25262      */
25263     createLinkText : 'Please enter the URL for the link:',
25264     /**
25265      * @cfg {String} defaultLinkValue The default value for the create link prompt (defaults to http:/ /)
25266      */
25267     defaultLinkValue : 'http:/'+'/',
25268    
25269      /**
25270      * @cfg {String} resizable  's' or 'se' or 'e' - wrapps the element in a
25271      *                        Roo.resizable.
25272      */
25273     resizable : false,
25274      /**
25275      * @cfg {Number} height (in pixels)
25276      */   
25277     height: 300,
25278    /**
25279      * @cfg {Number} width (in pixels)
25280      */   
25281     width: 500,
25282     
25283     /**
25284      * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
25285      * 
25286      */
25287     stylesheets: false,
25288     
25289     // id of frame..
25290     frameId: false,
25291     
25292     // private properties
25293     validationEvent : false,
25294     deferHeight: true,
25295     initialized : false,
25296     activated : false,
25297     sourceEditMode : false,
25298     onFocus : Roo.emptyFn,
25299     iframePad:3,
25300     hideMode:'offsets',
25301     
25302     defaultAutoCreate : { // modified by initCompnoent..
25303         tag: "textarea",
25304         style:"width:500px;height:300px;",
25305         autocomplete: "off"
25306     },
25307
25308     // private
25309     initComponent : function(){
25310         this.addEvents({
25311             /**
25312              * @event initialize
25313              * Fires when the editor is fully initialized (including the iframe)
25314              * @param {HtmlEditor} this
25315              */
25316             initialize: true,
25317             /**
25318              * @event activate
25319              * Fires when the editor is first receives the focus. Any insertion must wait
25320              * until after this event.
25321              * @param {HtmlEditor} this
25322              */
25323             activate: true,
25324              /**
25325              * @event beforesync
25326              * Fires before the textarea is updated with content from the editor iframe. Return false
25327              * to cancel the sync.
25328              * @param {HtmlEditor} this
25329              * @param {String} html
25330              */
25331             beforesync: true,
25332              /**
25333              * @event beforepush
25334              * Fires before the iframe editor is updated with content from the textarea. Return false
25335              * to cancel the push.
25336              * @param {HtmlEditor} this
25337              * @param {String} html
25338              */
25339             beforepush: true,
25340              /**
25341              * @event sync
25342              * Fires when the textarea is updated with content from the editor iframe.
25343              * @param {HtmlEditor} this
25344              * @param {String} html
25345              */
25346             sync: true,
25347              /**
25348              * @event push
25349              * Fires when the iframe editor is updated with content from the textarea.
25350              * @param {HtmlEditor} this
25351              * @param {String} html
25352              */
25353             push: true,
25354              /**
25355              * @event editmodechange
25356              * Fires when the editor switches edit modes
25357              * @param {HtmlEditor} this
25358              * @param {Boolean} sourceEdit True if source edit, false if standard editing.
25359              */
25360             editmodechange: true,
25361             /**
25362              * @event editorevent
25363              * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
25364              * @param {HtmlEditor} this
25365              */
25366             editorevent: true
25367         });
25368         this.defaultAutoCreate =  {
25369             tag: "textarea",
25370             style:'width: ' + this.width + 'px;height: ' + this.height + 'px;',
25371             autocomplete: "off"
25372         };
25373     },
25374
25375     /**
25376      * Protected method that will not generally be called directly. It
25377      * is called when the editor creates its toolbar. Override this method if you need to
25378      * add custom toolbar buttons.
25379      * @param {HtmlEditor} editor
25380      */
25381     createToolbar : function(editor){
25382         if (!editor.toolbars || !editor.toolbars.length) {
25383             editor.toolbars = [ new Roo.form.HtmlEditor.ToolbarStandard() ]; // can be empty?
25384         }
25385         
25386         for (var i =0 ; i < editor.toolbars.length;i++) {
25387             editor.toolbars[i] = Roo.factory(
25388                     typeof(editor.toolbars[i]) == 'string' ?
25389                         { xtype: editor.toolbars[i]} : editor.toolbars[i],
25390                 Roo.form.HtmlEditor);
25391             editor.toolbars[i].init(editor);
25392         }
25393          
25394         
25395     },
25396
25397     /**
25398      * Protected method that will not generally be called directly. It
25399      * is called when the editor initializes the iframe with HTML contents. Override this method if you
25400      * want to change the initialization markup of the iframe (e.g. to add stylesheets).
25401      */
25402     getDocMarkup : function(){
25403         // body styles..
25404         var st = '';
25405         if (this.stylesheets === false) {
25406             
25407             Roo.get(document.head).select('style').each(function(node) {
25408                 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
25409             });
25410             
25411             Roo.get(document.head).select('link').each(function(node) { 
25412                 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
25413             });
25414             
25415         } else if (!this.stylesheets.length) {
25416                 // simple..
25417                 st = '<style type="text/css">' +
25418                     'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
25419                    '</style>';
25420         } else {
25421             Roo.each(this.stylesheets, function(s) {
25422                 st += '<link rel="stylesheet" type="text/css" href="' + s +'" />'
25423             });
25424             
25425         }
25426         
25427         st +=  '<style type="text/css">' +
25428             'IMG { cursor: pointer } ' +
25429         '</style>';
25430
25431         
25432         return '<html><head>' + st  +
25433             //<style type="text/css">' +
25434             //'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
25435             //'</style>' +
25436             ' </head><body class="roo-htmleditor-body"></body></html>';
25437     },
25438
25439     // private
25440     onRender : function(ct, position)
25441     {
25442         var _t = this;
25443         Roo.form.HtmlEditor.superclass.onRender.call(this, ct, position);
25444         this.el.dom.style.border = '0 none';
25445         this.el.dom.setAttribute('tabIndex', -1);
25446         this.el.addClass('x-hidden');
25447         if(Roo.isIE){ // fix IE 1px bogus margin
25448             this.el.applyStyles('margin-top:-1px;margin-bottom:-1px;')
25449         }
25450         this.wrap = this.el.wrap({
25451             cls:'x-html-editor-wrap', cn:{cls:'x-html-editor-tb'}
25452         });
25453         
25454         if (this.resizable) {
25455             this.resizeEl = new Roo.Resizable(this.wrap, {
25456                 pinned : true,
25457                 wrap: true,
25458                 dynamic : true,
25459                 minHeight : this.height,
25460                 height: this.height,
25461                 handles : this.resizable,
25462                 width: this.width,
25463                 listeners : {
25464                     resize : function(r, w, h) {
25465                         _t.onResize(w,h); // -something
25466                     }
25467                 }
25468             });
25469             
25470         }
25471
25472         this.frameId = Roo.id();
25473         
25474         this.createToolbar(this);
25475         
25476       
25477         
25478         var iframe = this.wrap.createChild({
25479             tag: 'iframe',
25480             id: this.frameId,
25481             name: this.frameId,
25482             frameBorder : 'no',
25483             'src' : Roo.SSL_SECURE_URL ? Roo.SSL_SECURE_URL  :  "javascript:false"
25484         }, this.el
25485         );
25486         
25487        // console.log(iframe);
25488         //this.wrap.dom.appendChild(iframe);
25489
25490         this.iframe = iframe.dom;
25491
25492          this.assignDocWin();
25493         
25494         this.doc.designMode = 'on';
25495        
25496         this.doc.open();
25497         this.doc.write(this.getDocMarkup());
25498         this.doc.close();
25499
25500         
25501         var task = { // must defer to wait for browser to be ready
25502             run : function(){
25503                 //console.log("run task?" + this.doc.readyState);
25504                 this.assignDocWin();
25505                 if(this.doc.body || this.doc.readyState == 'complete'){
25506                     try {
25507                         this.doc.designMode="on";
25508                     } catch (e) {
25509                         return;
25510                     }
25511                     Roo.TaskMgr.stop(task);
25512                     this.initEditor.defer(10, this);
25513                 }
25514             },
25515             interval : 10,
25516             duration:10000,
25517             scope: this
25518         };
25519         Roo.TaskMgr.start(task);
25520
25521         if(!this.width){
25522             this.setSize(this.wrap.getSize());
25523         }
25524         if (this.resizeEl) {
25525             this.resizeEl.resizeTo.defer(100, this.resizeEl,[ this.width,this.height ] );
25526             // should trigger onReize..
25527         }
25528     },
25529
25530     // private
25531     onResize : function(w, h)
25532     {
25533         //Roo.log('resize: ' +w + ',' + h );
25534         Roo.form.HtmlEditor.superclass.onResize.apply(this, arguments);
25535         if(this.el && this.iframe){
25536             if(typeof w == 'number'){
25537                 var aw = w - this.wrap.getFrameWidth('lr');
25538                 this.el.setWidth(this.adjustWidth('textarea', aw));
25539                 this.iframe.style.width = aw + 'px';
25540             }
25541             if(typeof h == 'number'){
25542                 var tbh = 0;
25543                 for (var i =0; i < this.toolbars.length;i++) {
25544                     // fixme - ask toolbars for heights?
25545                     tbh += this.toolbars[i].tb.el.getHeight();
25546                     if (this.toolbars[i].footer) {
25547                         tbh += this.toolbars[i].footer.el.getHeight();
25548                     }
25549                 }
25550                 
25551                 
25552                 
25553                 
25554                 var ah = h - this.wrap.getFrameWidth('tb') - tbh;// this.tb.el.getHeight();
25555                 ah -= 5; // knock a few pixes off for look..
25556                 this.el.setHeight(this.adjustWidth('textarea', ah));
25557                 this.iframe.style.height = ah + 'px';
25558                 if(this.doc){
25559                     (this.doc.body || this.doc.documentElement).style.height = (ah - (this.iframePad*2)) + 'px';
25560                 }
25561             }
25562         }
25563     },
25564
25565     /**
25566      * Toggles the editor between standard and source edit mode.
25567      * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
25568      */
25569     toggleSourceEdit : function(sourceEditMode){
25570         
25571         this.sourceEditMode = sourceEditMode === true;
25572         
25573         if(this.sourceEditMode){
25574 //            Roo.log('in');
25575 //            Roo.log(this.syncValue());
25576             this.syncValue();
25577             this.iframe.className = 'x-hidden';
25578             this.el.removeClass('x-hidden');
25579             this.el.dom.removeAttribute('tabIndex');
25580             this.el.focus();
25581         }else{
25582 //            Roo.log('out')
25583 //            Roo.log(this.pushValue()); 
25584             this.pushValue();
25585             this.iframe.className = '';
25586             this.el.addClass('x-hidden');
25587             this.el.dom.setAttribute('tabIndex', -1);
25588             this.deferFocus();
25589         }
25590         this.setSize(this.wrap.getSize());
25591         this.fireEvent('editmodechange', this, this.sourceEditMode);
25592     },
25593
25594     // private used internally
25595     createLink : function(){
25596         var url = prompt(this.createLinkText, this.defaultLinkValue);
25597         if(url && url != 'http:/'+'/'){
25598             this.relayCmd('createlink', url);
25599         }
25600     },
25601
25602     // private (for BoxComponent)
25603     adjustSize : Roo.BoxComponent.prototype.adjustSize,
25604
25605     // private (for BoxComponent)
25606     getResizeEl : function(){
25607         return this.wrap;
25608     },
25609
25610     // private (for BoxComponent)
25611     getPositionEl : function(){
25612         return this.wrap;
25613     },
25614
25615     // private
25616     initEvents : function(){
25617         this.originalValue = this.getValue();
25618     },
25619
25620     /**
25621      * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
25622      * @method
25623      */
25624     markInvalid : Roo.emptyFn,
25625     /**
25626      * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
25627      * @method
25628      */
25629     clearInvalid : Roo.emptyFn,
25630
25631     setValue : function(v){
25632         Roo.form.HtmlEditor.superclass.setValue.call(this, v);
25633         this.pushValue();
25634     },
25635
25636     /**
25637      * Protected method that will not generally be called directly. If you need/want
25638      * custom HTML cleanup, this is the method you should override.
25639      * @param {String} html The HTML to be cleaned
25640      * return {String} The cleaned HTML
25641      */
25642     cleanHtml : function(html){
25643         html = String(html);
25644         if(html.length > 5){
25645             if(Roo.isSafari){ // strip safari nonsense
25646                 html = html.replace(/\sclass="(?:Apple-style-span|khtml-block-placeholder)"/gi, '');
25647             }
25648         }
25649         if(html == '&nbsp;'){
25650             html = '';
25651         }
25652         return html;
25653     },
25654
25655     /**
25656      * Protected method that will not generally be called directly. Syncs the contents
25657      * of the editor iframe with the textarea.
25658      */
25659     syncValue : function(){
25660         if(this.initialized){
25661             var bd = (this.doc.body || this.doc.documentElement);
25662             //this.cleanUpPaste(); -- this is done else where and causes havoc..
25663             var html = bd.innerHTML;
25664             if(Roo.isSafari){
25665                 var bs = bd.getAttribute('style'); // Safari puts text-align styles on the body element!
25666                 var m = bs.match(/text-align:(.*?);/i);
25667                 if(m && m[1]){
25668                     html = '<div style="'+m[0]+'">' + html + '</div>';
25669                 }
25670             }
25671             html = this.cleanHtml(html);
25672             // fix up the special chars.. normaly like back quotes in word...
25673             // however we do not want to do this with chinese..
25674             html = html.replace(/([\x80-\uffff])/g, function (a, b) {
25675                 var cc = b.charCodeAt();
25676                 if (
25677                     (cc >= 0x4E00 && cc < 0xA000 ) ||
25678                     (cc >= 0x3400 && cc < 0x4E00 ) ||
25679                     (cc >= 0xf900 && cc < 0xfb00 )
25680                 ) {
25681                         return b;
25682                 }
25683                 return "&#"+cc+";" 
25684             });
25685             if(this.fireEvent('beforesync', this, html) !== false){
25686                 this.el.dom.value = html;
25687                 this.fireEvent('sync', this, html);
25688             }
25689         }
25690     },
25691
25692     /**
25693      * Protected method that will not generally be called directly. Pushes the value of the textarea
25694      * into the iframe editor.
25695      */
25696     pushValue : function(){
25697         if(this.initialized){
25698             var v = this.el.dom.value;
25699             
25700             if(v.length < 1){
25701                 v = '&#160;';
25702             }
25703             
25704             if(this.fireEvent('beforepush', this, v) !== false){
25705                 var d = (this.doc.body || this.doc.documentElement);
25706                 d.innerHTML = v;
25707                 this.cleanUpPaste();
25708                 this.el.dom.value = d.innerHTML;
25709                 this.fireEvent('push', this, v);
25710             }
25711         }
25712     },
25713
25714     // private
25715     deferFocus : function(){
25716         this.focus.defer(10, this);
25717     },
25718
25719     // doc'ed in Field
25720     focus : function(){
25721         if(this.win && !this.sourceEditMode){
25722             this.win.focus();
25723         }else{
25724             this.el.focus();
25725         }
25726     },
25727     
25728     assignDocWin: function()
25729     {
25730         var iframe = this.iframe;
25731         
25732          if(Roo.isIE){
25733             this.doc = iframe.contentWindow.document;
25734             this.win = iframe.contentWindow;
25735         } else {
25736             if (!Roo.get(this.frameId)) {
25737                 return;
25738             }
25739             this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
25740             this.win = Roo.get(this.frameId).dom.contentWindow;
25741         }
25742     },
25743     
25744     // private
25745     initEditor : function(){
25746         //console.log("INIT EDITOR");
25747         this.assignDocWin();
25748         
25749         
25750         
25751         this.doc.designMode="on";
25752         this.doc.open();
25753         this.doc.write(this.getDocMarkup());
25754         this.doc.close();
25755         
25756         var dbody = (this.doc.body || this.doc.documentElement);
25757         //var ss = this.el.getStyles('font-size', 'font-family', 'background-image', 'background-repeat');
25758         // this copies styles from the containing element into thsi one..
25759         // not sure why we need all of this..
25760         var ss = this.el.getStyles('font-size', 'background-image', 'background-repeat');
25761         ss['background-attachment'] = 'fixed'; // w3c
25762         dbody.bgProperties = 'fixed'; // ie
25763         Roo.DomHelper.applyStyles(dbody, ss);
25764         Roo.EventManager.on(this.doc, {
25765             //'mousedown': this.onEditorEvent,
25766             'mouseup': this.onEditorEvent,
25767             'dblclick': this.onEditorEvent,
25768             'click': this.onEditorEvent,
25769             'keyup': this.onEditorEvent,
25770             buffer:100,
25771             scope: this
25772         });
25773         if(Roo.isGecko){
25774             Roo.EventManager.on(this.doc, 'keypress', this.mozKeyPress, this);
25775         }
25776         if(Roo.isIE || Roo.isSafari || Roo.isOpera){
25777             Roo.EventManager.on(this.doc, 'keydown', this.fixKeys, this);
25778         }
25779         this.initialized = true;
25780
25781         this.fireEvent('initialize', this);
25782         this.pushValue();
25783     },
25784
25785     // private
25786     onDestroy : function(){
25787         
25788         
25789         
25790         if(this.rendered){
25791             
25792             for (var i =0; i < this.toolbars.length;i++) {
25793                 // fixme - ask toolbars for heights?
25794                 this.toolbars[i].onDestroy();
25795             }
25796             
25797             this.wrap.dom.innerHTML = '';
25798             this.wrap.remove();
25799         }
25800     },
25801
25802     // private
25803     onFirstFocus : function(){
25804         
25805         this.assignDocWin();
25806         
25807         
25808         this.activated = true;
25809         for (var i =0; i < this.toolbars.length;i++) {
25810             this.toolbars[i].onFirstFocus();
25811         }
25812        
25813         if(Roo.isGecko){ // prevent silly gecko errors
25814             this.win.focus();
25815             var s = this.win.getSelection();
25816             if(!s.focusNode || s.focusNode.nodeType != 3){
25817                 var r = s.getRangeAt(0);
25818                 r.selectNodeContents((this.doc.body || this.doc.documentElement));
25819                 r.collapse(true);
25820                 this.deferFocus();
25821             }
25822             try{
25823                 this.execCmd('useCSS', true);
25824                 this.execCmd('styleWithCSS', false);
25825             }catch(e){}
25826         }
25827         this.fireEvent('activate', this);
25828     },
25829
25830     // private
25831     adjustFont: function(btn){
25832         var adjust = btn.cmd == 'increasefontsize' ? 1 : -1;
25833         //if(Roo.isSafari){ // safari
25834         //    adjust *= 2;
25835        // }
25836         var v = parseInt(this.doc.queryCommandValue('FontSize')|| 3, 10);
25837         if(Roo.isSafari){ // safari
25838             var sm = { 10 : 1, 13: 2, 16:3, 18:4, 24: 5, 32:6, 48: 7 };
25839             v =  (v < 10) ? 10 : v;
25840             v =  (v > 48) ? 48 : v;
25841             v = typeof(sm[v]) == 'undefined' ? 1 : sm[v];
25842             
25843         }
25844         
25845         
25846         v = Math.max(1, v+adjust);
25847         
25848         this.execCmd('FontSize', v  );
25849     },
25850
25851     onEditorEvent : function(e){
25852         this.fireEvent('editorevent', this, e);
25853       //  this.updateToolbar();
25854         this.syncValue(); //we can not sync so often.. sync cleans, so this breaks stuff
25855     },
25856
25857     insertTag : function(tg)
25858     {
25859         // could be a bit smarter... -> wrap the current selected tRoo..
25860         if (tg.toLowerCase() == 'span' || tg.toLowerCase() == 'code') {
25861             
25862             range = this.createRange(this.getSelection());
25863             var wrappingNode = this.doc.createElement(tg.toLowerCase());
25864             wrappingNode.appendChild(range.extractContents());
25865             range.insertNode(wrappingNode);
25866
25867             return;
25868             
25869             
25870             
25871         }
25872         this.execCmd("formatblock",   tg);
25873         
25874     },
25875     
25876     insertText : function(txt)
25877     {
25878         
25879         
25880         var range = this.createRange();
25881         range.deleteContents();
25882                //alert(Sender.getAttribute('label'));
25883                
25884         range.insertNode(this.doc.createTextNode(txt));
25885     } ,
25886     
25887     // private
25888     relayBtnCmd : function(btn){
25889         this.relayCmd(btn.cmd);
25890     },
25891
25892     /**
25893      * Executes a Midas editor command on the editor document and performs necessary focus and
25894      * toolbar updates. <b>This should only be called after the editor is initialized.</b>
25895      * @param {String} cmd The Midas command
25896      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
25897      */
25898     relayCmd : function(cmd, value){
25899         this.win.focus();
25900         this.execCmd(cmd, value);
25901         this.fireEvent('editorevent', this);
25902         //this.updateToolbar();
25903         this.deferFocus();
25904     },
25905
25906     /**
25907      * Executes a Midas editor command directly on the editor document.
25908      * For visual commands, you should use {@link #relayCmd} instead.
25909      * <b>This should only be called after the editor is initialized.</b>
25910      * @param {String} cmd The Midas command
25911      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
25912      */
25913     execCmd : function(cmd, value){
25914         this.doc.execCommand(cmd, false, value === undefined ? null : value);
25915         this.syncValue();
25916     },
25917  
25918  
25919    
25920     /**
25921      * Inserts the passed text at the current cursor position. Note: the editor must be initialized and activated
25922      * to insert tRoo.
25923      * @param {String} text | dom node.. 
25924      */
25925     insertAtCursor : function(text)
25926     {
25927         
25928         
25929         
25930         if(!this.activated){
25931             return;
25932         }
25933         /*
25934         if(Roo.isIE){
25935             this.win.focus();
25936             var r = this.doc.selection.createRange();
25937             if(r){
25938                 r.collapse(true);
25939                 r.pasteHTML(text);
25940                 this.syncValue();
25941                 this.deferFocus();
25942             
25943             }
25944             return;
25945         }
25946         */
25947         if(Roo.isGecko || Roo.isOpera || Roo.isSafari){
25948             this.win.focus();
25949             
25950             
25951             // from jquery ui (MIT licenced)
25952             var range, node;
25953             var win = this.win;
25954             
25955             if (win.getSelection && win.getSelection().getRangeAt) {
25956                 range = win.getSelection().getRangeAt(0);
25957                 node = typeof(text) == 'string' ? range.createContextualFragment(text) : text;
25958                 range.insertNode(node);
25959             } else if (win.document.selection && win.document.selection.createRange) {
25960                 // no firefox support
25961                 var txt = typeof(text) == 'string' ? text : text.outerHTML;
25962                 win.document.selection.createRange().pasteHTML(txt);
25963             } else {
25964                 // no firefox support
25965                 var txt = typeof(text) == 'string' ? text : text.outerHTML;
25966                 this.execCmd('InsertHTML', txt);
25967             } 
25968             
25969             this.syncValue();
25970             
25971             this.deferFocus();
25972         }
25973     },
25974  // private
25975     mozKeyPress : function(e){
25976         if(e.ctrlKey){
25977             var c = e.getCharCode(), cmd;
25978           
25979             if(c > 0){
25980                 c = String.fromCharCode(c).toLowerCase();
25981                 switch(c){
25982                     case 'b':
25983                         cmd = 'bold';
25984                         break;
25985                     case 'i':
25986                         cmd = 'italic';
25987                         break;
25988                     
25989                     case 'u':
25990                         cmd = 'underline';
25991                         break;
25992                     
25993                     case 'v':
25994                         this.cleanUpPaste.defer(100, this);
25995                         return;
25996                         
25997                 }
25998                 if(cmd){
25999                     this.win.focus();
26000                     this.execCmd(cmd);
26001                     this.deferFocus();
26002                     e.preventDefault();
26003                 }
26004                 
26005             }
26006         }
26007     },
26008
26009     // private
26010     fixKeys : function(){ // load time branching for fastest keydown performance
26011         if(Roo.isIE){
26012             return function(e){
26013                 var k = e.getKey(), r;
26014                 if(k == e.TAB){
26015                     e.stopEvent();
26016                     r = this.doc.selection.createRange();
26017                     if(r){
26018                         r.collapse(true);
26019                         r.pasteHTML('&#160;&#160;&#160;&#160;');
26020                         this.deferFocus();
26021                     }
26022                     return;
26023                 }
26024                 
26025                 if(k == e.ENTER){
26026                     r = this.doc.selection.createRange();
26027                     if(r){
26028                         var target = r.parentElement();
26029                         if(!target || target.tagName.toLowerCase() != 'li'){
26030                             e.stopEvent();
26031                             r.pasteHTML('<br />');
26032                             r.collapse(false);
26033                             r.select();
26034                         }
26035                     }
26036                 }
26037                 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
26038                     this.cleanUpPaste.defer(100, this);
26039                     return;
26040                 }
26041                 
26042                 
26043             };
26044         }else if(Roo.isOpera){
26045             return function(e){
26046                 var k = e.getKey();
26047                 if(k == e.TAB){
26048                     e.stopEvent();
26049                     this.win.focus();
26050                     this.execCmd('InsertHTML','&#160;&#160;&#160;&#160;');
26051                     this.deferFocus();
26052                 }
26053                 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
26054                     this.cleanUpPaste.defer(100, this);
26055                     return;
26056                 }
26057                 
26058             };
26059         }else if(Roo.isSafari){
26060             return function(e){
26061                 var k = e.getKey();
26062                 
26063                 if(k == e.TAB){
26064                     e.stopEvent();
26065                     this.execCmd('InsertText','\t');
26066                     this.deferFocus();
26067                     return;
26068                 }
26069                if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
26070                     this.cleanUpPaste.defer(100, this);
26071                     return;
26072                 }
26073                 
26074              };
26075         }
26076     }(),
26077     
26078     getAllAncestors: function()
26079     {
26080         var p = this.getSelectedNode();
26081         var a = [];
26082         if (!p) {
26083             a.push(p); // push blank onto stack..
26084             p = this.getParentElement();
26085         }
26086         
26087         
26088         while (p && (p.nodeType == 1) && (p.tagName.toLowerCase() != 'body')) {
26089             a.push(p);
26090             p = p.parentNode;
26091         }
26092         a.push(this.doc.body);
26093         return a;
26094     },
26095     lastSel : false,
26096     lastSelNode : false,
26097     
26098     
26099     getSelection : function() 
26100     {
26101         this.assignDocWin();
26102         return Roo.isIE ? this.doc.selection : this.win.getSelection();
26103     },
26104     
26105     getSelectedNode: function() 
26106     {
26107         // this may only work on Gecko!!!
26108         
26109         // should we cache this!!!!
26110         
26111         
26112         
26113          
26114         var range = this.createRange(this.getSelection()).cloneRange();
26115         
26116         if (Roo.isIE) {
26117             var parent = range.parentElement();
26118             while (true) {
26119                 var testRange = range.duplicate();
26120                 testRange.moveToElementText(parent);
26121                 if (testRange.inRange(range)) {
26122                     break;
26123                 }
26124                 if ((parent.nodeType != 1) || (parent.tagName.toLowerCase() == 'body')) {
26125                     break;
26126                 }
26127                 parent = parent.parentElement;
26128             }
26129             return parent;
26130         }
26131         
26132         // is ancestor a text element.
26133         var ac =  range.commonAncestorContainer;
26134         if (ac.nodeType == 3) {
26135             ac = ac.parentNode;
26136         }
26137         
26138         var ar = ac.childNodes;
26139          
26140         var nodes = [];
26141         var other_nodes = [];
26142         var has_other_nodes = false;
26143         for (var i=0;i<ar.length;i++) {
26144             if ((ar[i].nodeType == 3) && (!ar[i].data.length)) { // empty text ? 
26145                 continue;
26146             }
26147             // fullly contained node.
26148             
26149             if (this.rangeIntersectsNode(range,ar[i]) && this.rangeCompareNode(range,ar[i]) == 3) {
26150                 nodes.push(ar[i]);
26151                 continue;
26152             }
26153             
26154             // probably selected..
26155             if ((ar[i].nodeType == 1) && this.rangeIntersectsNode(range,ar[i]) && (this.rangeCompareNode(range,ar[i]) > 0)) {
26156                 other_nodes.push(ar[i]);
26157                 continue;
26158             }
26159             // outer..
26160             if (!this.rangeIntersectsNode(range,ar[i])|| (this.rangeCompareNode(range,ar[i]) == 0))  {
26161                 continue;
26162             }
26163             
26164             
26165             has_other_nodes = true;
26166         }
26167         if (!nodes.length && other_nodes.length) {
26168             nodes= other_nodes;
26169         }
26170         if (has_other_nodes || !nodes.length || (nodes.length > 1)) {
26171             return false;
26172         }
26173         
26174         return nodes[0];
26175     },
26176     createRange: function(sel)
26177     {
26178         // this has strange effects when using with 
26179         // top toolbar - not sure if it's a great idea.
26180         //this.editor.contentWindow.focus();
26181         if (typeof sel != "undefined") {
26182             try {
26183                 return sel.getRangeAt ? sel.getRangeAt(0) : sel.createRange();
26184             } catch(e) {
26185                 return this.doc.createRange();
26186             }
26187         } else {
26188             return this.doc.createRange();
26189         }
26190     },
26191     getParentElement: function()
26192     {
26193         
26194         this.assignDocWin();
26195         var sel = Roo.isIE ? this.doc.selection : this.win.getSelection();
26196         
26197         var range = this.createRange(sel);
26198          
26199         try {
26200             var p = range.commonAncestorContainer;
26201             while (p.nodeType == 3) { // text node
26202                 p = p.parentNode;
26203             }
26204             return p;
26205         } catch (e) {
26206             return null;
26207         }
26208     
26209     },
26210     /***
26211      *
26212      * Range intersection.. the hard stuff...
26213      *  '-1' = before
26214      *  '0' = hits..
26215      *  '1' = after.
26216      *         [ -- selected range --- ]
26217      *   [fail]                        [fail]
26218      *
26219      *    basically..
26220      *      if end is before start or  hits it. fail.
26221      *      if start is after end or hits it fail.
26222      *
26223      *   if either hits (but other is outside. - then it's not 
26224      *   
26225      *    
26226      **/
26227     
26228     
26229     // @see http://www.thismuchiknow.co.uk/?p=64.
26230     rangeIntersectsNode : function(range, node)
26231     {
26232         var nodeRange = node.ownerDocument.createRange();
26233         try {
26234             nodeRange.selectNode(node);
26235         } catch (e) {
26236             nodeRange.selectNodeContents(node);
26237         }
26238     
26239         var rangeStartRange = range.cloneRange();
26240         rangeStartRange.collapse(true);
26241     
26242         var rangeEndRange = range.cloneRange();
26243         rangeEndRange.collapse(false);
26244     
26245         var nodeStartRange = nodeRange.cloneRange();
26246         nodeStartRange.collapse(true);
26247     
26248         var nodeEndRange = nodeRange.cloneRange();
26249         nodeEndRange.collapse(false);
26250     
26251         return rangeStartRange.compareBoundaryPoints(
26252                  Range.START_TO_START, nodeEndRange) == -1 &&
26253                rangeEndRange.compareBoundaryPoints(
26254                  Range.START_TO_START, nodeStartRange) == 1;
26255         
26256          
26257     },
26258     rangeCompareNode : function(range, node)
26259     {
26260         var nodeRange = node.ownerDocument.createRange();
26261         try {
26262             nodeRange.selectNode(node);
26263         } catch (e) {
26264             nodeRange.selectNodeContents(node);
26265         }
26266         
26267         
26268         range.collapse(true);
26269     
26270         nodeRange.collapse(true);
26271      
26272         var ss = range.compareBoundaryPoints( Range.START_TO_START, nodeRange);
26273         var ee = range.compareBoundaryPoints(  Range.END_TO_END, nodeRange);
26274          
26275         //Roo.log(node.tagName + ': ss='+ss +', ee='+ee)
26276         
26277         var nodeIsBefore   =  ss == 1;
26278         var nodeIsAfter    = ee == -1;
26279         
26280         if (nodeIsBefore && nodeIsAfter)
26281             return 0; // outer
26282         if (!nodeIsBefore && nodeIsAfter)
26283             return 1; //right trailed.
26284         
26285         if (nodeIsBefore && !nodeIsAfter)
26286             return 2;  // left trailed.
26287         // fully contined.
26288         return 3;
26289     },
26290
26291     // private? - in a new class?
26292     cleanUpPaste :  function()
26293     {
26294         // cleans up the whole document..
26295          Roo.log('cleanuppaste');
26296         this.cleanUpChildren(this.doc.body);
26297         var clean = this.cleanWordChars(this.doc.body.innerHTML);
26298         if (clean != this.doc.body.innerHTML) {
26299             this.doc.body.innerHTML = clean;
26300         }
26301         
26302     },
26303     
26304     cleanWordChars : function(input) {// change the chars to hex code
26305         var he = Roo.form.HtmlEditor;
26306         
26307         var output = input;
26308         Roo.each(he.swapCodes, function(sw) { 
26309             var swapper = new RegExp("\\u" + sw[0].toString(16), "g"); // hex codes
26310             
26311             output = output.replace(swapper, sw[1]);
26312         });
26313         
26314         return output;
26315     },
26316     
26317     
26318     cleanUpChildren : function (n)
26319     {
26320         if (!n.childNodes.length) {
26321             return;
26322         }
26323         for (var i = n.childNodes.length-1; i > -1 ; i--) {
26324            this.cleanUpChild(n.childNodes[i]);
26325         }
26326     },
26327     
26328     
26329         
26330     
26331     cleanUpChild : function (node)
26332     {
26333         var ed = this;
26334         //console.log(node);
26335         if (node.nodeName == "#text") {
26336             // clean up silly Windows -- stuff?
26337             return; 
26338         }
26339         if (node.nodeName == "#comment") {
26340             node.parentNode.removeChild(node);
26341             // clean up silly Windows -- stuff?
26342             return; 
26343         }
26344         
26345         if (Roo.form.HtmlEditor.black.indexOf(node.tagName.toLowerCase()) > -1) {
26346             // remove node.
26347             node.parentNode.removeChild(node);
26348             return;
26349             
26350         }
26351         
26352         var remove_keep_children= Roo.form.HtmlEditor.remove.indexOf(node.tagName.toLowerCase()) > -1;
26353         
26354         // remove <a name=....> as rendering on yahoo mailer is borked with this.
26355         // this will have to be flaged elsewhere - perhaps ablack=name... on the mailer..
26356         
26357         //if (node.tagName.toLowerCase() == 'a' && !node.hasAttribute('href')) {
26358         //    remove_keep_children = true;
26359         //}
26360         
26361         if (remove_keep_children) {
26362             this.cleanUpChildren(node);
26363             // inserts everything just before this node...
26364             while (node.childNodes.length) {
26365                 var cn = node.childNodes[0];
26366                 node.removeChild(cn);
26367                 node.parentNode.insertBefore(cn, node);
26368             }
26369             node.parentNode.removeChild(node);
26370             return;
26371         }
26372         
26373         if (!node.attributes || !node.attributes.length) {
26374             this.cleanUpChildren(node);
26375             return;
26376         }
26377         
26378         function cleanAttr(n,v)
26379         {
26380             
26381             if (v.match(/^\./) || v.match(/^\//)) {
26382                 return;
26383             }
26384             if (v.match(/^(http|https):\/\//) || v.match(/^mailto:/)) {
26385                 return;
26386             }
26387             if (v.match(/^#/)) {
26388                 return;
26389             }
26390 //            Roo.log("(REMOVE TAG)"+ node.tagName +'.' + n + '=' + v);
26391             node.removeAttribute(n);
26392             
26393         }
26394         
26395         function cleanStyle(n,v)
26396         {
26397             if (v.match(/expression/)) { //XSS?? should we even bother..
26398                 node.removeAttribute(n);
26399                 return;
26400             }
26401             var cwhite = typeof(ed.cwhite) == 'undefined' ? Roo.form.HtmlEditor.cwhite : ed.cwhite;
26402             var cblack = typeof(ed.cblack) == 'undefined' ? Roo.form.HtmlEditor.cblack : ed.cblack;
26403             
26404             
26405             var parts = v.split(/;/);
26406             var clean = [];
26407             
26408             Roo.each(parts, function(p) {
26409                 p = p.replace(/^\s+/g,'').replace(/\s+$/g,'');
26410                 if (!p.length) {
26411                     return true;
26412                 }
26413                 var l = p.split(':').shift().replace(/\s+/g,'');
26414                 l = l.replace(/^\s+/g,'').replace(/\s+$/g,'');
26415                 
26416                 
26417                 if ( cblack.indexOf(l) > -1) {
26418 //                    Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
26419                     //node.removeAttribute(n);
26420                     return true;
26421                 }
26422                 //Roo.log()
26423                 // only allow 'c whitelisted system attributes'
26424                 if ( cwhite.length &&  cwhite.indexOf(l) < 0) {
26425 //                    Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
26426                     //node.removeAttribute(n);
26427                     return true;
26428                 }
26429                 
26430                 
26431                  
26432                 
26433                 clean.push(p);
26434                 return true;
26435             });
26436             if (clean.length) { 
26437                 node.setAttribute(n, clean.join(';'));
26438             } else {
26439                 node.removeAttribute(n);
26440             }
26441             
26442         }
26443         
26444         
26445         for (var i = node.attributes.length-1; i > -1 ; i--) {
26446             var a = node.attributes[i];
26447             //console.log(a);
26448             
26449             if (a.name.toLowerCase().substr(0,2)=='on')  {
26450                 node.removeAttribute(a.name);
26451                 continue;
26452             }
26453             if (Roo.form.HtmlEditor.ablack.indexOf(a.name.toLowerCase()) > -1) {
26454                 node.removeAttribute(a.name);
26455                 continue;
26456             }
26457             if (Roo.form.HtmlEditor.aclean.indexOf(a.name.toLowerCase()) > -1) {
26458                 cleanAttr(a.name,a.value); // fixme..
26459                 continue;
26460             }
26461             if (a.name == 'style') {
26462                 cleanStyle(a.name,a.value);
26463                 continue;
26464             }
26465             /// clean up MS crap..
26466             // tecnically this should be a list of valid class'es..
26467             
26468             
26469             if (a.name == 'class') {
26470                 if (a.value.match(/^Mso/)) {
26471                     node.className = '';
26472                 }
26473                 
26474                 if (a.value.match(/body/)) {
26475                     node.className = '';
26476                 }
26477                 continue;
26478             }
26479             
26480             // style cleanup!?
26481             // class cleanup?
26482             
26483         }
26484         
26485         
26486         this.cleanUpChildren(node);
26487         
26488         
26489     }
26490     
26491     
26492     // hide stuff that is not compatible
26493     /**
26494      * @event blur
26495      * @hide
26496      */
26497     /**
26498      * @event change
26499      * @hide
26500      */
26501     /**
26502      * @event focus
26503      * @hide
26504      */
26505     /**
26506      * @event specialkey
26507      * @hide
26508      */
26509     /**
26510      * @cfg {String} fieldClass @hide
26511      */
26512     /**
26513      * @cfg {String} focusClass @hide
26514      */
26515     /**
26516      * @cfg {String} autoCreate @hide
26517      */
26518     /**
26519      * @cfg {String} inputType @hide
26520      */
26521     /**
26522      * @cfg {String} invalidClass @hide
26523      */
26524     /**
26525      * @cfg {String} invalidText @hide
26526      */
26527     /**
26528      * @cfg {String} msgFx @hide
26529      */
26530     /**
26531      * @cfg {String} validateOnBlur @hide
26532      */
26533 });
26534
26535 Roo.form.HtmlEditor.white = [
26536         'area', 'br', 'img', 'input', 'hr', 'wbr',
26537         
26538        'address', 'blockquote', 'center', 'dd',      'dir',       'div', 
26539        'dl',      'dt',         'h1',     'h2',      'h3',        'h4', 
26540        'h5',      'h6',         'hr',     'isindex', 'listing',   'marquee', 
26541        'menu',    'multicol',   'ol',     'p',       'plaintext', 'pre', 
26542        'table',   'ul',         'xmp', 
26543        
26544        'caption', 'col', 'colgroup', 'tbody', 'td', 'tfoot', 'th', 
26545       'thead',   'tr', 
26546      
26547       'dir', 'menu', 'ol', 'ul', 'dl',
26548        
26549       'embed',  'object'
26550 ];
26551
26552
26553 Roo.form.HtmlEditor.black = [
26554     //    'embed',  'object', // enable - backend responsiblity to clean thiese
26555         'applet', // 
26556         'base',   'basefont', 'bgsound', 'blink',  'body', 
26557         'frame',  'frameset', 'head',    'html',   'ilayer', 
26558         'iframe', 'layer',  'link',     'meta',    'object',   
26559         'script', 'style' ,'title',  'xml' // clean later..
26560 ];
26561 Roo.form.HtmlEditor.clean = [
26562     'script', 'style', 'title', 'xml'
26563 ];
26564 Roo.form.HtmlEditor.remove = [
26565     'font'
26566 ];
26567 // attributes..
26568
26569 Roo.form.HtmlEditor.ablack = [
26570     'on'
26571 ];
26572     
26573 Roo.form.HtmlEditor.aclean = [ 
26574     'action', 'background', 'codebase', 'dynsrc', 'href', 'lowsrc' 
26575 ];
26576
26577 // protocols..
26578 Roo.form.HtmlEditor.pwhite= [
26579         'http',  'https',  'mailto'
26580 ];
26581
26582 // white listed style attributes.
26583 Roo.form.HtmlEditor.cwhite= [
26584       //  'text-align', /// default is to allow most things..
26585       
26586          
26587 //        'font-size'//??
26588 ];
26589
26590 // black listed style attributes.
26591 Roo.form.HtmlEditor.cblack= [
26592       //  'font-size' -- this can be set by the project 
26593 ];
26594
26595
26596 Roo.form.HtmlEditor.swapCodes   =[ 
26597     [    8211, "--" ], 
26598     [    8212, "--" ], 
26599     [    8216,  "'" ],  
26600     [    8217, "'" ],  
26601     [    8220, '"' ],  
26602     [    8221, '"' ],  
26603     [    8226, "*" ],  
26604     [    8230, "..." ]
26605 ]; 
26606
26607     // <script type="text/javascript">
26608 /*
26609  * Based on
26610  * Ext JS Library 1.1.1
26611  * Copyright(c) 2006-2007, Ext JS, LLC.
26612  *  
26613  
26614  */
26615
26616 /**
26617  * @class Roo.form.HtmlEditorToolbar1
26618  * Basic Toolbar
26619  * 
26620  * Usage:
26621  *
26622  new Roo.form.HtmlEditor({
26623     ....
26624     toolbars : [
26625         new Roo.form.HtmlEditorToolbar1({
26626             disable : { fonts: 1 , format: 1, ..., ... , ...],
26627             btns : [ .... ]
26628         })
26629     }
26630      
26631  * 
26632  * @cfg {Object} disable List of elements to disable..
26633  * @cfg {Array} btns List of additional buttons.
26634  * 
26635  * 
26636  * NEEDS Extra CSS? 
26637  * .x-html-editor-tb .x-edit-none .x-btn-text { background: none; }
26638  */
26639  
26640 Roo.form.HtmlEditor.ToolbarStandard = function(config)
26641 {
26642     
26643     Roo.apply(this, config);
26644     
26645     // default disabled, based on 'good practice'..
26646     this.disable = this.disable || {};
26647     Roo.applyIf(this.disable, {
26648         fontSize : true,
26649         colors : true,
26650         specialElements : true
26651     });
26652     
26653     
26654     //Roo.form.HtmlEditorToolbar1.superclass.constructor.call(this, editor.wrap.dom.firstChild, [], config);
26655     // dont call parent... till later.
26656 }
26657
26658 Roo.apply(Roo.form.HtmlEditor.ToolbarStandard.prototype,  {
26659     
26660     tb: false,
26661     
26662     rendered: false,
26663     
26664     editor : false,
26665     /**
26666      * @cfg {Object} disable  List of toolbar elements to disable
26667          
26668      */
26669     disable : false,
26670       /**
26671      * @cfg {Array} fontFamilies An array of available font families
26672      */
26673     fontFamilies : [
26674         'Arial',
26675         'Courier New',
26676         'Tahoma',
26677         'Times New Roman',
26678         'Verdana'
26679     ],
26680     
26681     specialChars : [
26682            "&#169;",
26683           "&#174;",     
26684           "&#8482;",    
26685           "&#163;" ,    
26686          // "&#8212;",    
26687           "&#8230;",    
26688           "&#247;" ,    
26689         //  "&#225;" ,     ?? a acute?
26690            "&#8364;"    , //Euro
26691        //   "&#8220;"    ,
26692         //  "&#8221;"    ,
26693         //  "&#8226;"    ,
26694           "&#176;"  //   , // degrees
26695
26696          // "&#233;"     , // e ecute
26697          // "&#250;"     , // u ecute?
26698     ],
26699     
26700     specialElements : [
26701         {
26702             text: "Insert Table",
26703             xtype: 'MenuItem',
26704             xns : Roo.Menu,
26705             ihtml :  '<table><tr><td>Cell</td></tr></table>' 
26706                 
26707         },
26708         {    
26709             text: "Insert Image",
26710             xtype: 'MenuItem',
26711             xns : Roo.Menu,
26712             ihtml : '<img src="about:blank"/>'
26713             
26714         }
26715         
26716          
26717     ],
26718     
26719     
26720     inputElements : [ 
26721             "form", "input:text", "input:hidden", "input:checkbox", "input:radio", "input:password", 
26722             "input:submit", "input:button", "select", "textarea", "label" ],
26723     formats : [
26724         ["p"] ,  
26725         ["h1"],["h2"],["h3"],["h4"],["h5"],["h6"], 
26726         ["pre"],[ "code"], 
26727         ["abbr"],[ "acronym"],[ "address"],[ "cite"],[ "samp"],[ "var"],
26728         ['div'],['span']
26729     ],
26730      /**
26731      * @cfg {String} defaultFont default font to use.
26732      */
26733     defaultFont: 'tahoma',
26734    
26735     fontSelect : false,
26736     
26737     
26738     formatCombo : false,
26739     
26740     init : function(editor)
26741     {
26742         this.editor = editor;
26743         
26744         
26745         var fid = editor.frameId;
26746         var etb = this;
26747         function btn(id, toggle, handler){
26748             var xid = fid + '-'+ id ;
26749             return {
26750                 id : xid,
26751                 cmd : id,
26752                 cls : 'x-btn-icon x-edit-'+id,
26753                 enableToggle:toggle !== false,
26754                 scope: editor, // was editor...
26755                 handler:handler||editor.relayBtnCmd,
26756                 clickEvent:'mousedown',
26757                 tooltip: etb.buttonTips[id] || undefined, ///tips ???
26758                 tabIndex:-1
26759             };
26760         }
26761         
26762         
26763         
26764         var tb = new Roo.Toolbar(editor.wrap.dom.firstChild);
26765         this.tb = tb;
26766          // stop form submits
26767         tb.el.on('click', function(e){
26768             e.preventDefault(); // what does this do?
26769         });
26770
26771         if(!this.disable.font) { // && !Roo.isSafari){
26772             /* why no safari for fonts 
26773             editor.fontSelect = tb.el.createChild({
26774                 tag:'select',
26775                 tabIndex: -1,
26776                 cls:'x-font-select',
26777                 html: this.createFontOptions()
26778             });
26779             
26780             editor.fontSelect.on('change', function(){
26781                 var font = editor.fontSelect.dom.value;
26782                 editor.relayCmd('fontname', font);
26783                 editor.deferFocus();
26784             }, editor);
26785             
26786             tb.add(
26787                 editor.fontSelect.dom,
26788                 '-'
26789             );
26790             */
26791             
26792         };
26793         if(!this.disable.formats){
26794             this.formatCombo = new Roo.form.ComboBox({
26795                 store: new Roo.data.SimpleStore({
26796                     id : 'tag',
26797                     fields: ['tag'],
26798                     data : this.formats // from states.js
26799                 }),
26800                 blockFocus : true,
26801                 name : '',
26802                 //autoCreate : {tag: "div",  size: "20"},
26803                 displayField:'tag',
26804                 typeAhead: false,
26805                 mode: 'local',
26806                 editable : false,
26807                 triggerAction: 'all',
26808                 emptyText:'Add tag',
26809                 selectOnFocus:true,
26810                 width:135,
26811                 listeners : {
26812                     'select': function(c, r, i) {
26813                         editor.insertTag(r.get('tag'));
26814                         editor.focus();
26815                     }
26816                 }
26817
26818             });
26819             tb.addField(this.formatCombo);
26820             
26821         }
26822         
26823         if(!this.disable.format){
26824             tb.add(
26825                 btn('bold'),
26826                 btn('italic'),
26827                 btn('underline')
26828             );
26829         };
26830         if(!this.disable.fontSize){
26831             tb.add(
26832                 '-',
26833                 
26834                 
26835                 btn('increasefontsize', false, editor.adjustFont),
26836                 btn('decreasefontsize', false, editor.adjustFont)
26837             );
26838         };
26839         
26840         
26841         if(!this.disable.colors){
26842             tb.add(
26843                 '-', {
26844                     id:editor.frameId +'-forecolor',
26845                     cls:'x-btn-icon x-edit-forecolor',
26846                     clickEvent:'mousedown',
26847                     tooltip: this.buttonTips['forecolor'] || undefined,
26848                     tabIndex:-1,
26849                     menu : new Roo.menu.ColorMenu({
26850                         allowReselect: true,
26851                         focus: Roo.emptyFn,
26852                         value:'000000',
26853                         plain:true,
26854                         selectHandler: function(cp, color){
26855                             editor.execCmd('forecolor', Roo.isSafari || Roo.isIE ? '#'+color : color);
26856                             editor.deferFocus();
26857                         },
26858                         scope: editor,
26859                         clickEvent:'mousedown'
26860                     })
26861                 }, {
26862                     id:editor.frameId +'backcolor',
26863                     cls:'x-btn-icon x-edit-backcolor',
26864                     clickEvent:'mousedown',
26865                     tooltip: this.buttonTips['backcolor'] || undefined,
26866                     tabIndex:-1,
26867                     menu : new Roo.menu.ColorMenu({
26868                         focus: Roo.emptyFn,
26869                         value:'FFFFFF',
26870                         plain:true,
26871                         allowReselect: true,
26872                         selectHandler: function(cp, color){
26873                             if(Roo.isGecko){
26874                                 editor.execCmd('useCSS', false);
26875                                 editor.execCmd('hilitecolor', color);
26876                                 editor.execCmd('useCSS', true);
26877                                 editor.deferFocus();
26878                             }else{
26879                                 editor.execCmd(Roo.isOpera ? 'hilitecolor' : 'backcolor', 
26880                                     Roo.isSafari || Roo.isIE ? '#'+color : color);
26881                                 editor.deferFocus();
26882                             }
26883                         },
26884                         scope:editor,
26885                         clickEvent:'mousedown'
26886                     })
26887                 }
26888             );
26889         };
26890         // now add all the items...
26891         
26892
26893         if(!this.disable.alignments){
26894             tb.add(
26895                 '-',
26896                 btn('justifyleft'),
26897                 btn('justifycenter'),
26898                 btn('justifyright')
26899             );
26900         };
26901
26902         //if(!Roo.isSafari){
26903             if(!this.disable.links){
26904                 tb.add(
26905                     '-',
26906                     btn('createlink', false, editor.createLink)    /// MOVE TO HERE?!!?!?!?!
26907                 );
26908             };
26909
26910             if(!this.disable.lists){
26911                 tb.add(
26912                     '-',
26913                     btn('insertorderedlist'),
26914                     btn('insertunorderedlist')
26915                 );
26916             }
26917             if(!this.disable.sourceEdit){
26918                 tb.add(
26919                     '-',
26920                     btn('sourceedit', true, function(btn){
26921                         this.toggleSourceEdit(btn.pressed);
26922                     })
26923                 );
26924             }
26925         //}
26926         
26927         var smenu = { };
26928         // special menu.. - needs to be tidied up..
26929         if (!this.disable.special) {
26930             smenu = {
26931                 text: "&#169;",
26932                 cls: 'x-edit-none',
26933                 
26934                 menu : {
26935                     items : []
26936                 }
26937             };
26938             for (var i =0; i < this.specialChars.length; i++) {
26939                 smenu.menu.items.push({
26940                     
26941                     html: this.specialChars[i],
26942                     handler: function(a,b) {
26943                         editor.insertAtCursor(String.fromCharCode(a.html.replace('&#','').replace(';', '')));
26944                         //editor.insertAtCursor(a.html);
26945                         
26946                     },
26947                     tabIndex:-1
26948                 });
26949             }
26950             
26951             
26952             tb.add(smenu);
26953             
26954             
26955         }
26956          
26957         if (!this.disable.specialElements) {
26958             var semenu = {
26959                 text: "Other;",
26960                 cls: 'x-edit-none',
26961                 menu : {
26962                     items : []
26963                 }
26964             };
26965             for (var i =0; i < this.specialElements.length; i++) {
26966                 semenu.menu.items.push(
26967                     Roo.apply({ 
26968                         handler: function(a,b) {
26969                             editor.insertAtCursor(this.ihtml);
26970                         }
26971                     }, this.specialElements[i])
26972                 );
26973                     
26974             }
26975             
26976             tb.add(semenu);
26977             
26978             
26979         }
26980          
26981         
26982         if (this.btns) {
26983             for(var i =0; i< this.btns.length;i++) {
26984                 var b = Roo.factory(this.btns[i],Roo.form);
26985                 b.cls =  'x-edit-none';
26986                 b.scope = editor;
26987                 tb.add(b);
26988             }
26989         
26990         }
26991         
26992         
26993         
26994         // disable everything...
26995         
26996         this.tb.items.each(function(item){
26997            if(item.id != editor.frameId+ '-sourceedit'){
26998                 item.disable();
26999             }
27000         });
27001         this.rendered = true;
27002         
27003         // the all the btns;
27004         editor.on('editorevent', this.updateToolbar, this);
27005         // other toolbars need to implement this..
27006         //editor.on('editmodechange', this.updateToolbar, this);
27007     },
27008     
27009     
27010     
27011     /**
27012      * Protected method that will not generally be called directly. It triggers
27013      * a toolbar update by reading the markup state of the current selection in the editor.
27014      */
27015     updateToolbar: function(){
27016
27017         if(!this.editor.activated){
27018             this.editor.onFirstFocus();
27019             return;
27020         }
27021
27022         var btns = this.tb.items.map, 
27023             doc = this.editor.doc,
27024             frameId = this.editor.frameId;
27025
27026         if(!this.disable.font && !Roo.isSafari){
27027             /*
27028             var name = (doc.queryCommandValue('FontName')||this.editor.defaultFont).toLowerCase();
27029             if(name != this.fontSelect.dom.value){
27030                 this.fontSelect.dom.value = name;
27031             }
27032             */
27033         }
27034         if(!this.disable.format){
27035             btns[frameId + '-bold'].toggle(doc.queryCommandState('bold'));
27036             btns[frameId + '-italic'].toggle(doc.queryCommandState('italic'));
27037             btns[frameId + '-underline'].toggle(doc.queryCommandState('underline'));
27038         }
27039         if(!this.disable.alignments){
27040             btns[frameId + '-justifyleft'].toggle(doc.queryCommandState('justifyleft'));
27041             btns[frameId + '-justifycenter'].toggle(doc.queryCommandState('justifycenter'));
27042             btns[frameId + '-justifyright'].toggle(doc.queryCommandState('justifyright'));
27043         }
27044         if(!Roo.isSafari && !this.disable.lists){
27045             btns[frameId + '-insertorderedlist'].toggle(doc.queryCommandState('insertorderedlist'));
27046             btns[frameId + '-insertunorderedlist'].toggle(doc.queryCommandState('insertunorderedlist'));
27047         }
27048         
27049         var ans = this.editor.getAllAncestors();
27050         if (this.formatCombo) {
27051             
27052             
27053             var store = this.formatCombo.store;
27054             this.formatCombo.setValue("");
27055             for (var i =0; i < ans.length;i++) {
27056                 if (ans[i] && store.query('tag',ans[i].tagName.toLowerCase(), false).length) {
27057                     // select it..
27058                     this.formatCombo.setValue(ans[i].tagName.toLowerCase());
27059                     break;
27060                 }
27061             }
27062         }
27063         
27064         
27065         
27066         // hides menus... - so this cant be on a menu...
27067         Roo.menu.MenuMgr.hideAll();
27068
27069         //this.editorsyncValue();
27070     },
27071    
27072     
27073     createFontOptions : function(){
27074         var buf = [], fs = this.fontFamilies, ff, lc;
27075         
27076         
27077         
27078         for(var i = 0, len = fs.length; i< len; i++){
27079             ff = fs[i];
27080             lc = ff.toLowerCase();
27081             buf.push(
27082                 '<option value="',lc,'" style="font-family:',ff,';"',
27083                     (this.defaultFont == lc ? ' selected="true">' : '>'),
27084                     ff,
27085                 '</option>'
27086             );
27087         }
27088         return buf.join('');
27089     },
27090     
27091     toggleSourceEdit : function(sourceEditMode){
27092         if(sourceEditMode === undefined){
27093             sourceEditMode = !this.sourceEditMode;
27094         }
27095         this.sourceEditMode = sourceEditMode === true;
27096         var btn = this.tb.items.get(this.editor.frameId +'-sourceedit');
27097         // just toggle the button?
27098         if(btn.pressed !== this.editor.sourceEditMode){
27099             btn.toggle(this.editor.sourceEditMode);
27100             return;
27101         }
27102         
27103         if(this.sourceEditMode){
27104             this.tb.items.each(function(item){
27105                 if(item.cmd != 'sourceedit'){
27106                     item.disable();
27107                 }
27108             });
27109           
27110         }else{
27111             if(this.initialized){
27112                 this.tb.items.each(function(item){
27113                     item.enable();
27114                 });
27115             }
27116             
27117         }
27118         // tell the editor that it's been pressed..
27119         this.editor.toggleSourceEdit(sourceEditMode);
27120        
27121     },
27122      /**
27123      * Object collection of toolbar tooltips for the buttons in the editor. The key
27124      * is the command id associated with that button and the value is a valid QuickTips object.
27125      * For example:
27126 <pre><code>
27127 {
27128     bold : {
27129         title: 'Bold (Ctrl+B)',
27130         text: 'Make the selected text bold.',
27131         cls: 'x-html-editor-tip'
27132     },
27133     italic : {
27134         title: 'Italic (Ctrl+I)',
27135         text: 'Make the selected text italic.',
27136         cls: 'x-html-editor-tip'
27137     },
27138     ...
27139 </code></pre>
27140     * @type Object
27141      */
27142     buttonTips : {
27143         bold : {
27144             title: 'Bold (Ctrl+B)',
27145             text: 'Make the selected text bold.',
27146             cls: 'x-html-editor-tip'
27147         },
27148         italic : {
27149             title: 'Italic (Ctrl+I)',
27150             text: 'Make the selected text italic.',
27151             cls: 'x-html-editor-tip'
27152         },
27153         underline : {
27154             title: 'Underline (Ctrl+U)',
27155             text: 'Underline the selected text.',
27156             cls: 'x-html-editor-tip'
27157         },
27158         increasefontsize : {
27159             title: 'Grow Text',
27160             text: 'Increase the font size.',
27161             cls: 'x-html-editor-tip'
27162         },
27163         decreasefontsize : {
27164             title: 'Shrink Text',
27165             text: 'Decrease the font size.',
27166             cls: 'x-html-editor-tip'
27167         },
27168         backcolor : {
27169             title: 'Text Highlight Color',
27170             text: 'Change the background color of the selected text.',
27171             cls: 'x-html-editor-tip'
27172         },
27173         forecolor : {
27174             title: 'Font Color',
27175             text: 'Change the color of the selected text.',
27176             cls: 'x-html-editor-tip'
27177         },
27178         justifyleft : {
27179             title: 'Align Text Left',
27180             text: 'Align text to the left.',
27181             cls: 'x-html-editor-tip'
27182         },
27183         justifycenter : {
27184             title: 'Center Text',
27185             text: 'Center text in the editor.',
27186             cls: 'x-html-editor-tip'
27187         },
27188         justifyright : {
27189             title: 'Align Text Right',
27190             text: 'Align text to the right.',
27191             cls: 'x-html-editor-tip'
27192         },
27193         insertunorderedlist : {
27194             title: 'Bullet List',
27195             text: 'Start a bulleted list.',
27196             cls: 'x-html-editor-tip'
27197         },
27198         insertorderedlist : {
27199             title: 'Numbered List',
27200             text: 'Start a numbered list.',
27201             cls: 'x-html-editor-tip'
27202         },
27203         createlink : {
27204             title: 'Hyperlink',
27205             text: 'Make the selected text a hyperlink.',
27206             cls: 'x-html-editor-tip'
27207         },
27208         sourceedit : {
27209             title: 'Source Edit',
27210             text: 'Switch to source editing mode.',
27211             cls: 'x-html-editor-tip'
27212         }
27213     },
27214     // private
27215     onDestroy : function(){
27216         if(this.rendered){
27217             
27218             this.tb.items.each(function(item){
27219                 if(item.menu){
27220                     item.menu.removeAll();
27221                     if(item.menu.el){
27222                         item.menu.el.destroy();
27223                     }
27224                 }
27225                 item.destroy();
27226             });
27227              
27228         }
27229     },
27230     onFirstFocus: function() {
27231         this.tb.items.each(function(item){
27232            item.enable();
27233         });
27234     }
27235 });
27236
27237
27238
27239
27240 // <script type="text/javascript">
27241 /*
27242  * Based on
27243  * Ext JS Library 1.1.1
27244  * Copyright(c) 2006-2007, Ext JS, LLC.
27245  *  
27246  
27247  */
27248
27249  
27250 /**
27251  * @class Roo.form.HtmlEditor.ToolbarContext
27252  * Context Toolbar
27253  * 
27254  * Usage:
27255  *
27256  new Roo.form.HtmlEditor({
27257     ....
27258     toolbars : [
27259         { xtype: 'ToolbarStandard', styles : {} }
27260         { xtype: 'ToolbarContext', disable : {} }
27261     ]
27262 })
27263
27264      
27265  * 
27266  * @config : {Object} disable List of elements to disable.. (not done yet.)
27267  * @config : {Object} styles  Map of styles available.
27268  * 
27269  */
27270
27271 Roo.form.HtmlEditor.ToolbarContext = function(config)
27272 {
27273     
27274     Roo.apply(this, config);
27275     //Roo.form.HtmlEditorToolbar1.superclass.constructor.call(this, editor.wrap.dom.firstChild, [], config);
27276     // dont call parent... till later.
27277     this.styles = this.styles || {};
27278 }
27279
27280  
27281
27282 Roo.form.HtmlEditor.ToolbarContext.types = {
27283     'IMG' : {
27284         width : {
27285             title: "Width",
27286             width: 40
27287         },
27288         height:  {
27289             title: "Height",
27290             width: 40
27291         },
27292         align: {
27293             title: "Align",
27294             opts : [ [""],[ "left"],[ "right"],[ "center"],[ "top"]],
27295             width : 80
27296             
27297         },
27298         border: {
27299             title: "Border",
27300             width: 40
27301         },
27302         alt: {
27303             title: "Alt",
27304             width: 120
27305         },
27306         src : {
27307             title: "Src",
27308             width: 220
27309         }
27310         
27311     },
27312     'A' : {
27313         name : {
27314             title: "Name",
27315             width: 50
27316         },
27317         href:  {
27318             title: "Href",
27319             width: 220
27320         } // border?
27321         
27322     },
27323     'TABLE' : {
27324         rows : {
27325             title: "Rows",
27326             width: 20
27327         },
27328         cols : {
27329             title: "Cols",
27330             width: 20
27331         },
27332         width : {
27333             title: "Width",
27334             width: 40
27335         },
27336         height : {
27337             title: "Height",
27338             width: 40
27339         },
27340         border : {
27341             title: "Border",
27342             width: 20
27343         }
27344     },
27345     'TD' : {
27346         width : {
27347             title: "Width",
27348             width: 40
27349         },
27350         height : {
27351             title: "Height",
27352             width: 40
27353         },   
27354         align: {
27355             title: "Align",
27356             opts : [[""],[ "left"],[ "center"],[ "right"],[ "justify"],[ "char"]],
27357             width: 80
27358         },
27359         valign: {
27360             title: "Valign",
27361             opts : [[""],[ "top"],[ "middle"],[ "bottom"],[ "baseline"]],
27362             width: 80
27363         },
27364         colspan: {
27365             title: "Colspan",
27366             width: 20
27367             
27368         },
27369          'font-family'  : {
27370             title : "Font",
27371             style : 'fontFamily',
27372             displayField: 'display',
27373             optname : 'font-family',
27374             width: 140
27375         }
27376     },
27377     'INPUT' : {
27378         name : {
27379             title: "name",
27380             width: 120
27381         },
27382         value : {
27383             title: "Value",
27384             width: 120
27385         },
27386         width : {
27387             title: "Width",
27388             width: 40
27389         }
27390     },
27391     'LABEL' : {
27392         'for' : {
27393             title: "For",
27394             width: 120
27395         }
27396     },
27397     'TEXTAREA' : {
27398           name : {
27399             title: "name",
27400             width: 120
27401         },
27402         rows : {
27403             title: "Rows",
27404             width: 20
27405         },
27406         cols : {
27407             title: "Cols",
27408             width: 20
27409         }
27410     },
27411     'SELECT' : {
27412         name : {
27413             title: "name",
27414             width: 120
27415         },
27416         selectoptions : {
27417             title: "Options",
27418             width: 200
27419         }
27420     },
27421     
27422     // should we really allow this??
27423     // should this just be 
27424     'BODY' : {
27425         title : {
27426             title: "Title",
27427             width: 200,
27428             disabled : true
27429         }
27430     },
27431     'SPAN' : {
27432         'font-family'  : {
27433             title : "Font",
27434             style : 'fontFamily',
27435             displayField: 'display',
27436             optname : 'font-family',
27437             width: 140
27438         }
27439     },
27440     'DIV' : {
27441         'font-family'  : {
27442             title : "Font",
27443             style : 'fontFamily',
27444             displayField: 'display',
27445             optname : 'font-family',
27446             width: 140
27447         }
27448     },
27449      'P' : {
27450         'font-family'  : {
27451             title : "Font",
27452             style : 'fontFamily',
27453             displayField: 'display',
27454             optname : 'font-family',
27455             width: 140
27456         }
27457     },
27458     
27459     '*' : {
27460         // empty..
27461     }
27462
27463 };
27464
27465 // this should be configurable.. - you can either set it up using stores, or modify options somehwere..
27466 Roo.form.HtmlEditor.ToolbarContext.stores = false;
27467
27468 Roo.form.HtmlEditor.ToolbarContext.options = {
27469         'font-family'  : [ 
27470                 [ 'Helvetica,Arial,sans-serif', 'Helvetica'],
27471                 [ 'Courier New', 'Courier New'],
27472                 [ 'Tahoma', 'Tahoma'],
27473                 [ 'Times New Roman,serif', 'Times'],
27474                 [ 'Verdana','Verdana' ]
27475         ]
27476 };
27477
27478 // fixme - these need to be configurable..
27479  
27480
27481 Roo.form.HtmlEditor.ToolbarContext.types
27482
27483
27484 Roo.apply(Roo.form.HtmlEditor.ToolbarContext.prototype,  {
27485     
27486     tb: false,
27487     
27488     rendered: false,
27489     
27490     editor : false,
27491     /**
27492      * @cfg {Object} disable  List of toolbar elements to disable
27493          
27494      */
27495     disable : false,
27496     /**
27497      * @cfg {Object} styles List of styles 
27498      *    eg. { '*' : [ 'headline' ] , 'TD' : [ 'underline', 'double-underline' ] } 
27499      *
27500      * These must be defined in the page, so they get rendered correctly..
27501      * .headline { }
27502      * TD.underline { }
27503      * 
27504      */
27505     styles : false,
27506     
27507     options: false,
27508     
27509     toolbars : false,
27510     
27511     init : function(editor)
27512     {
27513         this.editor = editor;
27514         
27515         
27516         var fid = editor.frameId;
27517         var etb = this;
27518         function btn(id, toggle, handler){
27519             var xid = fid + '-'+ id ;
27520             return {
27521                 id : xid,
27522                 cmd : id,
27523                 cls : 'x-btn-icon x-edit-'+id,
27524                 enableToggle:toggle !== false,
27525                 scope: editor, // was editor...
27526                 handler:handler||editor.relayBtnCmd,
27527                 clickEvent:'mousedown',
27528                 tooltip: etb.buttonTips[id] || undefined, ///tips ???
27529                 tabIndex:-1
27530             };
27531         }
27532         // create a new element.
27533         var wdiv = editor.wrap.createChild({
27534                 tag: 'div'
27535             }, editor.wrap.dom.firstChild.nextSibling, true);
27536         
27537         // can we do this more than once??
27538         
27539          // stop form submits
27540       
27541  
27542         // disable everything...
27543         var ty= Roo.form.HtmlEditor.ToolbarContext.types;
27544         this.toolbars = {};
27545            
27546         for (var i in  ty) {
27547           
27548             this.toolbars[i] = this.buildToolbar(ty[i],i);
27549         }
27550         this.tb = this.toolbars.BODY;
27551         this.tb.el.show();
27552         this.buildFooter();
27553         this.footer.show();
27554         editor.on('hide', function( ) { this.footer.hide() }, this);
27555         editor.on('show', function( ) { this.footer.show() }, this);
27556         
27557          
27558         this.rendered = true;
27559         
27560         // the all the btns;
27561         editor.on('editorevent', this.updateToolbar, this);
27562         // other toolbars need to implement this..
27563         //editor.on('editmodechange', this.updateToolbar, this);
27564     },
27565     
27566     
27567     
27568     /**
27569      * Protected method that will not generally be called directly. It triggers
27570      * a toolbar update by reading the markup state of the current selection in the editor.
27571      */
27572     updateToolbar: function(editor,ev,sel){
27573
27574         //Roo.log(ev);
27575         // capture mouse up - this is handy for selecting images..
27576         // perhaps should go somewhere else...
27577         if(!this.editor.activated){
27578              this.editor.onFirstFocus();
27579             return;
27580         }
27581         
27582         // http://developer.yahoo.com/yui/docs/simple-editor.js.html
27583         // selectNode - might want to handle IE?
27584         if (ev &&
27585             (ev.type == 'mouseup' || ev.type == 'click' ) &&
27586             ev.target && ev.target.tagName == 'IMG') {
27587             // they have click on an image...
27588             // let's see if we can change the selection...
27589             sel = ev.target;
27590          
27591               var nodeRange = sel.ownerDocument.createRange();
27592             try {
27593                 nodeRange.selectNode(sel);
27594             } catch (e) {
27595                 nodeRange.selectNodeContents(sel);
27596             }
27597             //nodeRange.collapse(true);
27598             var s = editor.win.getSelection();
27599             s.removeAllRanges();
27600             s.addRange(nodeRange);
27601         }  
27602         
27603       
27604         var updateFooter = sel ? false : true;
27605         
27606         
27607         var ans = this.editor.getAllAncestors();
27608         
27609         // pick
27610         var ty= Roo.form.HtmlEditor.ToolbarContext.types;
27611         
27612         if (!sel) { 
27613             sel = ans.length ? (ans[0] ?  ans[0]  : ans[1]) : this.editor.doc.body;
27614             sel = sel ? sel : this.editor.doc.body;
27615             sel = sel.tagName.length ? sel : this.editor.doc.body;
27616             
27617         }
27618         // pick a menu that exists..
27619         var tn = sel.tagName.toUpperCase();
27620         //sel = typeof(ty[tn]) != 'undefined' ? sel : this.editor.doc.body;
27621         
27622         tn = sel.tagName.toUpperCase();
27623         
27624         var lastSel = this.tb.selectedNode
27625         
27626         this.tb.selectedNode = sel;
27627         
27628         // if current menu does not match..
27629         if ((this.tb.name != tn) || (lastSel != this.tb.selectedNode)) {
27630                 
27631             this.tb.el.hide();
27632             ///console.log("show: " + tn);
27633             this.tb =  typeof(ty[tn]) != 'undefined' ? this.toolbars[tn] : this.toolbars['*'];
27634             this.tb.el.show();
27635             // update name
27636             this.tb.items.first().el.innerHTML = tn + ':&nbsp;';
27637             
27638             
27639             // update attributes
27640             if (this.tb.fields) {
27641                 this.tb.fields.each(function(e) {
27642                     if (e.stylename) {
27643                         e.setValue(sel.style[e.stylename]);
27644                         return;
27645                     } 
27646                    e.setValue(sel.getAttribute(e.attrname));
27647                 });
27648             }
27649             
27650             var hasStyles = false;
27651             for(var i in this.styles) {
27652                 hasStyles = true;
27653                 break;
27654             }
27655             
27656             // update styles
27657             if (hasStyles) { 
27658                 var st = this.tb.fields.item(0);
27659                 
27660                 st.store.removeAll();
27661                
27662                 
27663                 var cn = sel.className.split(/\s+/);
27664                 
27665                 var avs = [];
27666                 if (this.styles['*']) {
27667                     
27668                     Roo.each(this.styles['*'], function(v) {
27669                         avs.push( [ v , cn.indexOf(v) > -1 ? 1 : 0 ] );         
27670                     });
27671                 }
27672                 if (this.styles[tn]) { 
27673                     Roo.each(this.styles[tn], function(v) {
27674                         avs.push( [ v , cn.indexOf(v) > -1 ? 1 : 0 ] );         
27675                     });
27676                 }
27677                 
27678                 st.store.loadData(avs);
27679                 st.collapse();
27680                 st.setValue(cn);
27681             }
27682             // flag our selected Node.
27683             this.tb.selectedNode = sel;
27684            
27685            
27686             Roo.menu.MenuMgr.hideAll();
27687
27688         }
27689         
27690         if (!updateFooter) {
27691             //this.footDisp.dom.innerHTML = ''; 
27692             return;
27693         }
27694         // update the footer
27695         //
27696         var html = '';
27697         
27698         this.footerEls = ans.reverse();
27699         Roo.each(this.footerEls, function(a,i) {
27700             if (!a) { return; }
27701             html += html.length ? ' &gt; '  :  '';
27702             
27703             html += '<span class="x-ed-loc-' + i + '">' + a.tagName + '</span>';
27704             
27705         });
27706        
27707         // 
27708         var sz = this.footDisp.up('td').getSize();
27709         this.footDisp.dom.style.width = (sz.width -10) + 'px';
27710         this.footDisp.dom.style.marginLeft = '5px';
27711         
27712         this.footDisp.dom.style.overflow = 'hidden';
27713         
27714         this.footDisp.dom.innerHTML = html;
27715             
27716         //this.editorsyncValue();
27717     },
27718      
27719     
27720    
27721        
27722     // private
27723     onDestroy : function(){
27724         if(this.rendered){
27725             
27726             this.tb.items.each(function(item){
27727                 if(item.menu){
27728                     item.menu.removeAll();
27729                     if(item.menu.el){
27730                         item.menu.el.destroy();
27731                     }
27732                 }
27733                 item.destroy();
27734             });
27735              
27736         }
27737     },
27738     onFirstFocus: function() {
27739         // need to do this for all the toolbars..
27740         this.tb.items.each(function(item){
27741            item.enable();
27742         });
27743     },
27744     buildToolbar: function(tlist, nm)
27745     {
27746         var editor = this.editor;
27747          // create a new element.
27748         var wdiv = editor.wrap.createChild({
27749                 tag: 'div'
27750             }, editor.wrap.dom.firstChild.nextSibling, true);
27751         
27752        
27753         var tb = new Roo.Toolbar(wdiv);
27754         // add the name..
27755         
27756         tb.add(nm+ ":&nbsp;");
27757         
27758         var styles = [];
27759         for(var i in this.styles) {
27760             styles.push(i);
27761         }
27762         
27763         // styles...
27764         if (styles && styles.length) {
27765             
27766             // this needs a multi-select checkbox...
27767             tb.addField( new Roo.form.ComboBox({
27768                 store: new Roo.data.SimpleStore({
27769                     id : 'val',
27770                     fields: ['val', 'selected'],
27771                     data : [] 
27772                 }),
27773                 name : '-roo-edit-className',
27774                 attrname : 'className',
27775                 displayField: 'val',
27776                 typeAhead: false,
27777                 mode: 'local',
27778                 editable : false,
27779                 triggerAction: 'all',
27780                 emptyText:'Select Style',
27781                 selectOnFocus:true,
27782                 width: 130,
27783                 listeners : {
27784                     'select': function(c, r, i) {
27785                         // initial support only for on class per el..
27786                         tb.selectedNode.className =  r ? r.get('val') : '';
27787                         editor.syncValue();
27788                     }
27789                 }
27790     
27791             }));
27792         }
27793         
27794         var tbc = Roo.form.HtmlEditor.ToolbarContext;
27795         var tbops = tbc.options;
27796         
27797         for (var i in tlist) {
27798             
27799             var item = tlist[i];
27800             tb.add(item.title + ":&nbsp;");
27801             
27802             
27803             //optname == used so you can configure the options available..
27804             var opts = item.opts ? item.opts : false;
27805             if (item.optname) {
27806                 opts = tbops[item.optname];
27807            
27808             }
27809             
27810             if (opts) {
27811                 // opts == pulldown..
27812                 tb.addField( new Roo.form.ComboBox({
27813                     store:   typeof(tbc.stores[i]) != 'undefined' ?  Roo.factory(tbc.stores[i],Roo.data) : new Roo.data.SimpleStore({
27814                         id : 'val',
27815                         fields: ['val', 'display'],
27816                         data : opts  
27817                     }),
27818                     name : '-roo-edit-' + i,
27819                     attrname : i,
27820                     stylename : item.style ? item.style : false,
27821                     displayField: item.displayField ? item.displayField : 'val',
27822                     valueField :  'val',
27823                     typeAhead: false,
27824                     mode: typeof(tbc.stores[i]) != 'undefined'  ? 'remote' : 'local',
27825                     editable : false,
27826                     triggerAction: 'all',
27827                     emptyText:'Select',
27828                     selectOnFocus:true,
27829                     width: item.width ? item.width  : 130,
27830                     listeners : {
27831                         'select': function(c, r, i) {
27832                             if (c.stylename) {
27833                                 tb.selectedNode.style[c.stylename] =  r.get('val');
27834                                 return;
27835                             }
27836                             tb.selectedNode.setAttribute(c.attrname, r.get('val'));
27837                         }
27838                     }
27839
27840                 }));
27841                 continue;
27842                     
27843                  
27844                 
27845                 tb.addField( new Roo.form.TextField({
27846                     name: i,
27847                     width: 100,
27848                     //allowBlank:false,
27849                     value: ''
27850                 }));
27851                 continue;
27852             }
27853             tb.addField( new Roo.form.TextField({
27854                 name: '-roo-edit-' + i,
27855                 attrname : i,
27856                 
27857                 width: item.width,
27858                 //allowBlank:true,
27859                 value: '',
27860                 listeners: {
27861                     'change' : function(f, nv, ov) {
27862                         tb.selectedNode.setAttribute(f.attrname, nv);
27863                     }
27864                 }
27865             }));
27866              
27867         }
27868         tb.addFill();
27869         var _this = this;
27870         tb.addButton( {
27871             text: 'Remove Tag',
27872     
27873             listeners : {
27874                 click : function ()
27875                 {
27876                     // remove
27877                     // undo does not work.
27878                      
27879                     var sn = tb.selectedNode;
27880                     
27881                     var pn = sn.parentNode;
27882                     
27883                     var stn =  sn.childNodes[0];
27884                     var en = sn.childNodes[sn.childNodes.length - 1 ];
27885                     while (sn.childNodes.length) {
27886                         var node = sn.childNodes[0];
27887                         sn.removeChild(node);
27888                         //Roo.log(node);
27889                         pn.insertBefore(node, sn);
27890                         
27891                     }
27892                     pn.removeChild(sn);
27893                     var range = editor.createRange();
27894         
27895                     range.setStart(stn,0);
27896                     range.setEnd(en,0); //????
27897                     //range.selectNode(sel);
27898                     
27899                     
27900                     var selection = editor.getSelection();
27901                     selection.removeAllRanges();
27902                     selection.addRange(range);
27903                     
27904                     
27905                     
27906                     //_this.updateToolbar(null, null, pn);
27907                     _this.updateToolbar(null, null, null);
27908                     _this.footDisp.dom.innerHTML = ''; 
27909                 }
27910             }
27911             
27912                     
27913                 
27914             
27915         });
27916         
27917         
27918         tb.el.on('click', function(e){
27919             e.preventDefault(); // what does this do?
27920         });
27921         tb.el.setVisibilityMode( Roo.Element.DISPLAY);
27922         tb.el.hide();
27923         tb.name = nm;
27924         // dont need to disable them... as they will get hidden
27925         return tb;
27926          
27927         
27928     },
27929     buildFooter : function()
27930     {
27931         
27932         var fel = this.editor.wrap.createChild();
27933         this.footer = new Roo.Toolbar(fel);
27934         // toolbar has scrolly on left / right?
27935         var footDisp= new Roo.Toolbar.Fill();
27936         var _t = this;
27937         this.footer.add(
27938             {
27939                 text : '&lt;',
27940                 xtype: 'Button',
27941                 handler : function() {
27942                     _t.footDisp.scrollTo('left',0,true)
27943                 }
27944             }
27945         );
27946         this.footer.add( footDisp );
27947         this.footer.add( 
27948             {
27949                 text : '&gt;',
27950                 xtype: 'Button',
27951                 handler : function() {
27952                     // no animation..
27953                     _t.footDisp.select('span').last().scrollIntoView(_t.footDisp,true);
27954                 }
27955             }
27956         );
27957         var fel = Roo.get(footDisp.el);
27958         fel.addClass('x-editor-context');
27959         this.footDispWrap = fel; 
27960         this.footDispWrap.overflow  = 'hidden';
27961         
27962         this.footDisp = fel.createChild();
27963         this.footDispWrap.on('click', this.onContextClick, this)
27964         
27965         
27966     },
27967     onContextClick : function (ev,dom)
27968     {
27969         ev.preventDefault();
27970         var  cn = dom.className;
27971         //Roo.log(cn);
27972         if (!cn.match(/x-ed-loc-/)) {
27973             return;
27974         }
27975         var n = cn.split('-').pop();
27976         var ans = this.footerEls;
27977         var sel = ans[n];
27978         
27979          // pick
27980         var range = this.editor.createRange();
27981         
27982         range.selectNodeContents(sel);
27983         //range.selectNode(sel);
27984         
27985         
27986         var selection = this.editor.getSelection();
27987         selection.removeAllRanges();
27988         selection.addRange(range);
27989         
27990         
27991         
27992         this.updateToolbar(null, null, sel);
27993         
27994         
27995     }
27996     
27997     
27998     
27999     
28000     
28001 });
28002
28003
28004
28005
28006
28007 /*
28008  * Based on:
28009  * Ext JS Library 1.1.1
28010  * Copyright(c) 2006-2007, Ext JS, LLC.
28011  *
28012  * Originally Released Under LGPL - original licence link has changed is not relivant.
28013  *
28014  * Fork - LGPL
28015  * <script type="text/javascript">
28016  */
28017  
28018 /**
28019  * @class Roo.form.BasicForm
28020  * @extends Roo.util.Observable
28021  * Supplies the functionality to do "actions" on forms and initialize Roo.form.Field types on existing markup.
28022  * @constructor
28023  * @param {String/HTMLElement/Roo.Element} el The form element or its id
28024  * @param {Object} config Configuration options
28025  */
28026 Roo.form.BasicForm = function(el, config){
28027     this.allItems = [];
28028     this.childForms = [];
28029     Roo.apply(this, config);
28030     /*
28031      * The Roo.form.Field items in this form.
28032      * @type MixedCollection
28033      */
28034      
28035      
28036     this.items = new Roo.util.MixedCollection(false, function(o){
28037         return o.id || (o.id = Roo.id());
28038     });
28039     this.addEvents({
28040         /**
28041          * @event beforeaction
28042          * Fires before any action is performed. Return false to cancel the action.
28043          * @param {Form} this
28044          * @param {Action} action The action to be performed
28045          */
28046         beforeaction: true,
28047         /**
28048          * @event actionfailed
28049          * Fires when an action fails.
28050          * @param {Form} this
28051          * @param {Action} action The action that failed
28052          */
28053         actionfailed : true,
28054         /**
28055          * @event actioncomplete
28056          * Fires when an action is completed.
28057          * @param {Form} this
28058          * @param {Action} action The action that completed
28059          */
28060         actioncomplete : true
28061     });
28062     if(el){
28063         this.initEl(el);
28064     }
28065     Roo.form.BasicForm.superclass.constructor.call(this);
28066 };
28067
28068 Roo.extend(Roo.form.BasicForm, Roo.util.Observable, {
28069     /**
28070      * @cfg {String} method
28071      * The request method to use (GET or POST) for form actions if one isn't supplied in the action options.
28072      */
28073     /**
28074      * @cfg {DataReader} reader
28075      * An Roo.data.DataReader (e.g. {@link Roo.data.XmlReader}) to be used to read data when executing "load" actions.
28076      * This is optional as there is built-in support for processing JSON.
28077      */
28078     /**
28079      * @cfg {DataReader} errorReader
28080      * An Roo.data.DataReader (e.g. {@link Roo.data.XmlReader}) to be used to read data when reading validation errors on "submit" actions.
28081      * This is completely optional as there is built-in support for processing JSON.
28082      */
28083     /**
28084      * @cfg {String} url
28085      * The URL to use for form actions if one isn't supplied in the action options.
28086      */
28087     /**
28088      * @cfg {Boolean} fileUpload
28089      * Set to true if this form is a file upload.
28090      */
28091      
28092     /**
28093      * @cfg {Object} baseParams
28094      * Parameters to pass with all requests. e.g. baseParams: {id: '123', foo: 'bar'}.
28095      */
28096      /**
28097      
28098     /**
28099      * @cfg {Number} timeout Timeout for form actions in seconds (default is 30 seconds).
28100      */
28101     timeout: 30,
28102
28103     // private
28104     activeAction : null,
28105
28106     /**
28107      * @cfg {Boolean} trackResetOnLoad If set to true, form.reset() resets to the last loaded
28108      * or setValues() data instead of when the form was first created.
28109      */
28110     trackResetOnLoad : false,
28111     
28112     
28113     /**
28114      * childForms - used for multi-tab forms
28115      * @type {Array}
28116      */
28117     childForms : false,
28118     
28119     /**
28120      * allItems - full list of fields.
28121      * @type {Array}
28122      */
28123     allItems : false,
28124     
28125     /**
28126      * By default wait messages are displayed with Roo.MessageBox.wait. You can target a specific
28127      * element by passing it or its id or mask the form itself by passing in true.
28128      * @type Mixed
28129      */
28130     waitMsgTarget : false,
28131
28132     // private
28133     initEl : function(el){
28134         this.el = Roo.get(el);
28135         this.id = this.el.id || Roo.id();
28136         this.el.on('submit', this.onSubmit, this);
28137         this.el.addClass('x-form');
28138     },
28139
28140     // private
28141     onSubmit : function(e){
28142         e.stopEvent();
28143     },
28144
28145     /**
28146      * Returns true if client-side validation on the form is successful.
28147      * @return Boolean
28148      */
28149     isValid : function(){
28150         var valid = true;
28151         this.items.each(function(f){
28152            if(!f.validate()){
28153                valid = false;
28154            }
28155         });
28156         return valid;
28157     },
28158
28159     /**
28160      * Returns true if any fields in this form have changed since their original load.
28161      * @return Boolean
28162      */
28163     isDirty : function(){
28164         var dirty = false;
28165         this.items.each(function(f){
28166            if(f.isDirty()){
28167                dirty = true;
28168                return false;
28169            }
28170         });
28171         return dirty;
28172     },
28173
28174     /**
28175      * Performs a predefined action (submit or load) or custom actions you define on this form.
28176      * @param {String} actionName The name of the action type
28177      * @param {Object} options (optional) The options to pass to the action.  All of the config options listed
28178      * below are supported by both the submit and load actions unless otherwise noted (custom actions could also
28179      * accept other config options):
28180      * <pre>
28181 Property          Type             Description
28182 ----------------  ---------------  ----------------------------------------------------------------------------------
28183 url               String           The url for the action (defaults to the form's url)
28184 method            String           The form method to use (defaults to the form's method, or POST if not defined)
28185 params            String/Object    The params to pass (defaults to the form's baseParams, or none if not defined)
28186 clientValidation  Boolean          Applies to submit only.  Pass true to call form.isValid() prior to posting to
28187                                    validate the form on the client (defaults to false)
28188      * </pre>
28189      * @return {BasicForm} this
28190      */
28191     doAction : function(action, options){
28192         if(typeof action == 'string'){
28193             action = new Roo.form.Action.ACTION_TYPES[action](this, options);
28194         }
28195         if(this.fireEvent('beforeaction', this, action) !== false){
28196             this.beforeAction(action);
28197             action.run.defer(100, action);
28198         }
28199         return this;
28200     },
28201
28202     /**
28203      * Shortcut to do a submit action.
28204      * @param {Object} options The options to pass to the action (see {@link #doAction} for details)
28205      * @return {BasicForm} this
28206      */
28207     submit : function(options){
28208         this.doAction('submit', options);
28209         return this;
28210     },
28211
28212     /**
28213      * Shortcut to do a load action.
28214      * @param {Object} options The options to pass to the action (see {@link #doAction} for details)
28215      * @return {BasicForm} this
28216      */
28217     load : function(options){
28218         this.doAction('load', options);
28219         return this;
28220     },
28221
28222     /**
28223      * Persists the values in this form into the passed Roo.data.Record object in a beginEdit/endEdit block.
28224      * @param {Record} record The record to edit
28225      * @return {BasicForm} this
28226      */
28227     updateRecord : function(record){
28228         record.beginEdit();
28229         var fs = record.fields;
28230         fs.each(function(f){
28231             var field = this.findField(f.name);
28232             if(field){
28233                 record.set(f.name, field.getValue());
28234             }
28235         }, this);
28236         record.endEdit();
28237         return this;
28238     },
28239
28240     /**
28241      * Loads an Roo.data.Record into this form.
28242      * @param {Record} record The record to load
28243      * @return {BasicForm} this
28244      */
28245     loadRecord : function(record){
28246         this.setValues(record.data);
28247         return this;
28248     },
28249
28250     // private
28251     beforeAction : function(action){
28252         var o = action.options;
28253         
28254        
28255         if(this.waitMsgTarget === true){
28256             this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
28257         }else if(this.waitMsgTarget){
28258             this.waitMsgTarget = Roo.get(this.waitMsgTarget);
28259             this.waitMsgTarget.mask(o.waitMsg || "Sending", 'x-mask-loading');
28260         }else {
28261             Roo.MessageBox.wait(o.waitMsg || "Sending", o.waitTitle || this.waitTitle || 'Please Wait...');
28262         }
28263          
28264     },
28265
28266     // private
28267     afterAction : function(action, success){
28268         this.activeAction = null;
28269         var o = action.options;
28270         
28271         if(this.waitMsgTarget === true){
28272             this.el.unmask();
28273         }else if(this.waitMsgTarget){
28274             this.waitMsgTarget.unmask();
28275         }else{
28276             Roo.MessageBox.updateProgress(1);
28277             Roo.MessageBox.hide();
28278         }
28279          
28280         if(success){
28281             if(o.reset){
28282                 this.reset();
28283             }
28284             Roo.callback(o.success, o.scope, [this, action]);
28285             this.fireEvent('actioncomplete', this, action);
28286             
28287         }else{
28288             
28289             // failure condition..
28290             // we have a scenario where updates need confirming.
28291             // eg. if a locking scenario exists..
28292             // we look for { errors : { needs_confirm : true }} in the response.
28293             if (
28294                 (typeof(action.result) != 'undefined')  &&
28295                 (typeof(action.result.errors) != 'undefined')  &&
28296                 (typeof(action.result.errors.needs_confirm) != 'undefined')
28297            ){
28298                 var _t = this;
28299                 Roo.MessageBox.confirm(
28300                     "Change requires confirmation",
28301                     action.result.errorMsg,
28302                     function(r) {
28303                         if (r != 'yes') {
28304                             return;
28305                         }
28306                         _t.doAction('submit', { params :  { _submit_confirmed : 1 } }  );
28307                     }
28308                     
28309                 );
28310                 
28311                 
28312                 
28313                 return;
28314             }
28315             
28316             Roo.callback(o.failure, o.scope, [this, action]);
28317             // show an error message if no failed handler is set..
28318             if (!this.hasListener('actionfailed')) {
28319                 Roo.MessageBox.alert("Error",
28320                     (typeof(action.result) != 'undefined' && typeof(action.result.errorMsg) != 'undefined') ?
28321                         action.result.errorMsg :
28322                         "Saving Failed, please check your entries or try again"
28323                 );
28324             }
28325             
28326             this.fireEvent('actionfailed', this, action);
28327         }
28328         
28329     },
28330
28331     /**
28332      * Find a Roo.form.Field in this form by id, dataIndex, name or hiddenName
28333      * @param {String} id The value to search for
28334      * @return Field
28335      */
28336     findField : function(id){
28337         var field = this.items.get(id);
28338         if(!field){
28339             this.items.each(function(f){
28340                 if(f.isFormField && (f.dataIndex == id || f.id == id || f.getName() == id)){
28341                     field = f;
28342                     return false;
28343                 }
28344             });
28345         }
28346         return field || null;
28347     },
28348
28349     /**
28350      * Add a secondary form to this one, 
28351      * Used to provide tabbed forms. One form is primary, with hidden values 
28352      * which mirror the elements from the other forms.
28353      * 
28354      * @param {Roo.form.Form} form to add.
28355      * 
28356      */
28357     addForm : function(form)
28358     {
28359        
28360         if (this.childForms.indexOf(form) > -1) {
28361             // already added..
28362             return;
28363         }
28364         this.childForms.push(form);
28365         var n = '';
28366         Roo.each(form.allItems, function (fe) {
28367             
28368             n = typeof(fe.getName) == 'undefined' ? fe.name : fe.getName();
28369             if (this.findField(n)) { // already added..
28370                 return;
28371             }
28372             var add = new Roo.form.Hidden({
28373                 name : n
28374             });
28375             add.render(this.el);
28376             
28377             this.add( add );
28378         }, this);
28379         
28380     },
28381     /**
28382      * Mark fields in this form invalid in bulk.
28383      * @param {Array/Object} errors Either an array in the form [{id:'fieldId', msg:'The message'},...] or an object hash of {id: msg, id2: msg2}
28384      * @return {BasicForm} this
28385      */
28386     markInvalid : function(errors){
28387         if(errors instanceof Array){
28388             for(var i = 0, len = errors.length; i < len; i++){
28389                 var fieldError = errors[i];
28390                 var f = this.findField(fieldError.id);
28391                 if(f){
28392                     f.markInvalid(fieldError.msg);
28393                 }
28394             }
28395         }else{
28396             var field, id;
28397             for(id in errors){
28398                 if(typeof errors[id] != 'function' && (field = this.findField(id))){
28399                     field.markInvalid(errors[id]);
28400                 }
28401             }
28402         }
28403         Roo.each(this.childForms || [], function (f) {
28404             f.markInvalid(errors);
28405         });
28406         
28407         return this;
28408     },
28409
28410     /**
28411      * Set values for fields in this form in bulk.
28412      * @param {Array/Object} values Either an array in the form [{id:'fieldId', value:'foo'},...] or an object hash of {id: value, id2: value2}
28413      * @return {BasicForm} this
28414      */
28415     setValues : function(values){
28416         if(values instanceof Array){ // array of objects
28417             for(var i = 0, len = values.length; i < len; i++){
28418                 var v = values[i];
28419                 var f = this.findField(v.id);
28420                 if(f){
28421                     f.setValue(v.value);
28422                     if(this.trackResetOnLoad){
28423                         f.originalValue = f.getValue();
28424                     }
28425                 }
28426             }
28427         }else{ // object hash
28428             var field, id;
28429             for(id in values){
28430                 if(typeof values[id] != 'function' && (field = this.findField(id))){
28431                     
28432                     if (field.setFromData && 
28433                         field.valueField && 
28434                         field.displayField &&
28435                         // combos' with local stores can 
28436                         // be queried via setValue()
28437                         // to set their value..
28438                         (field.store && !field.store.isLocal)
28439                         ) {
28440                         // it's a combo
28441                         var sd = { };
28442                         sd[field.valueField] = typeof(values[field.hiddenName]) == 'undefined' ? '' : values[field.hiddenName];
28443                         sd[field.displayField] = typeof(values[field.name]) == 'undefined' ? '' : values[field.name];
28444                         field.setFromData(sd);
28445                         
28446                     } else {
28447                         field.setValue(values[id]);
28448                     }
28449                     
28450                     
28451                     if(this.trackResetOnLoad){
28452                         field.originalValue = field.getValue();
28453                     }
28454                 }
28455             }
28456         }
28457          
28458         Roo.each(this.childForms || [], function (f) {
28459             f.setValues(values);
28460         });
28461                 
28462         return this;
28463     },
28464
28465     /**
28466      * Returns the fields in this form as an object with key/value pairs. If multiple fields exist with the same name
28467      * they are returned as an array.
28468      * @param {Boolean} asString
28469      * @return {Object}
28470      */
28471     getValues : function(asString){
28472         if (this.childForms) {
28473             // copy values from the child forms
28474             Roo.each(this.childForms, function (f) {
28475                 this.setValues(f.getValues());
28476             }, this);
28477         }
28478         
28479         
28480         
28481         var fs = Roo.lib.Ajax.serializeForm(this.el.dom);
28482         if(asString === true){
28483             return fs;
28484         }
28485         return Roo.urlDecode(fs);
28486     },
28487     
28488     /**
28489      * Returns the fields in this form as an object with key/value pairs. 
28490      * This differs from getValues as it calls getValue on each child item, rather than using dom data.
28491      * @return {Object}
28492      */
28493     getFieldValues : function(with_hidden)
28494     {
28495         if (this.childForms) {
28496             // copy values from the child forms
28497             // should this call getFieldValues - probably not as we do not currently copy
28498             // hidden fields when we generate..
28499             Roo.each(this.childForms, function (f) {
28500                 this.setValues(f.getValues());
28501             }, this);
28502         }
28503         
28504         var ret = {};
28505         this.items.each(function(f){
28506             if (!f.getName()) {
28507                 return;
28508             }
28509             var v = f.getValue();
28510             if (f.inputType =='radio') {
28511                 if (typeof(ret[f.getName()]) == 'undefined') {
28512                     ret[f.getName()] = ''; // empty..
28513                 }
28514                 
28515                 if (!f.el.dom.checked) {
28516                     return;
28517                     
28518                 }
28519                 v = f.el.dom.value;
28520                 
28521             }
28522             
28523             // not sure if this supported any more..
28524             if ((typeof(v) == 'object') && f.getRawValue) {
28525                 v = f.getRawValue() ; // dates..
28526             }
28527             // combo boxes where name != hiddenName...
28528             if (f.name != f.getName()) {
28529                 ret[f.name] = f.getRawValue();
28530             }
28531             ret[f.getName()] = v;
28532         });
28533         
28534         return ret;
28535     },
28536
28537     /**
28538      * Clears all invalid messages in this form.
28539      * @return {BasicForm} this
28540      */
28541     clearInvalid : function(){
28542         this.items.each(function(f){
28543            f.clearInvalid();
28544         });
28545         
28546         Roo.each(this.childForms || [], function (f) {
28547             f.clearInvalid();
28548         });
28549         
28550         
28551         return this;
28552     },
28553
28554     /**
28555      * Resets this form.
28556      * @return {BasicForm} this
28557      */
28558     reset : function(){
28559         this.items.each(function(f){
28560             f.reset();
28561         });
28562         
28563         Roo.each(this.childForms || [], function (f) {
28564             f.reset();
28565         });
28566        
28567         
28568         return this;
28569     },
28570
28571     /**
28572      * Add Roo.form components to this form.
28573      * @param {Field} field1
28574      * @param {Field} field2 (optional)
28575      * @param {Field} etc (optional)
28576      * @return {BasicForm} this
28577      */
28578     add : function(){
28579         this.items.addAll(Array.prototype.slice.call(arguments, 0));
28580         return this;
28581     },
28582
28583
28584     /**
28585      * Removes a field from the items collection (does NOT remove its markup).
28586      * @param {Field} field
28587      * @return {BasicForm} this
28588      */
28589     remove : function(field){
28590         this.items.remove(field);
28591         return this;
28592     },
28593
28594     /**
28595      * Looks at the fields in this form, checks them for an id attribute,
28596      * and calls applyTo on the existing dom element with that id.
28597      * @return {BasicForm} this
28598      */
28599     render : function(){
28600         this.items.each(function(f){
28601             if(f.isFormField && !f.rendered && document.getElementById(f.id)){ // if the element exists
28602                 f.applyTo(f.id);
28603             }
28604         });
28605         return this;
28606     },
28607
28608     /**
28609      * Calls {@link Ext#apply} for all fields in this form with the passed object.
28610      * @param {Object} values
28611      * @return {BasicForm} this
28612      */
28613     applyToFields : function(o){
28614         this.items.each(function(f){
28615            Roo.apply(f, o);
28616         });
28617         return this;
28618     },
28619
28620     /**
28621      * Calls {@link Ext#applyIf} for all field in this form with the passed object.
28622      * @param {Object} values
28623      * @return {BasicForm} this
28624      */
28625     applyIfToFields : function(o){
28626         this.items.each(function(f){
28627            Roo.applyIf(f, o);
28628         });
28629         return this;
28630     }
28631 });
28632
28633 // back compat
28634 Roo.BasicForm = Roo.form.BasicForm;/*
28635  * Based on:
28636  * Ext JS Library 1.1.1
28637  * Copyright(c) 2006-2007, Ext JS, LLC.
28638  *
28639  * Originally Released Under LGPL - original licence link has changed is not relivant.
28640  *
28641  * Fork - LGPL
28642  * <script type="text/javascript">
28643  */
28644
28645 /**
28646  * @class Roo.form.Form
28647  * @extends Roo.form.BasicForm
28648  * Adds the ability to dynamically render forms with JavaScript to {@link Roo.form.BasicForm}.
28649  * @constructor
28650  * @param {Object} config Configuration options
28651  */
28652 Roo.form.Form = function(config){
28653     var xitems =  [];
28654     if (config.items) {
28655         xitems = config.items;
28656         delete config.items;
28657     }
28658    
28659     
28660     Roo.form.Form.superclass.constructor.call(this, null, config);
28661     this.url = this.url || this.action;
28662     if(!this.root){
28663         this.root = new Roo.form.Layout(Roo.applyIf({
28664             id: Roo.id()
28665         }, config));
28666     }
28667     this.active = this.root;
28668     /**
28669      * Array of all the buttons that have been added to this form via {@link addButton}
28670      * @type Array
28671      */
28672     this.buttons = [];
28673     this.allItems = [];
28674     this.addEvents({
28675         /**
28676          * @event clientvalidation
28677          * If the monitorValid config option is true, this event fires repetitively to notify of valid state
28678          * @param {Form} this
28679          * @param {Boolean} valid true if the form has passed client-side validation
28680          */
28681         clientvalidation: true,
28682         /**
28683          * @event rendered
28684          * Fires when the form is rendered
28685          * @param {Roo.form.Form} form
28686          */
28687         rendered : true
28688     });
28689     
28690     if (this.progressUrl) {
28691             // push a hidden field onto the list of fields..
28692             this.addxtype( {
28693                     xns: Roo.form, 
28694                     xtype : 'Hidden', 
28695                     name : 'UPLOAD_IDENTIFIER' 
28696             });
28697         }
28698         
28699     
28700     Roo.each(xitems, this.addxtype, this);
28701     
28702     
28703     
28704 };
28705
28706 Roo.extend(Roo.form.Form, Roo.form.BasicForm, {
28707     /**
28708      * @cfg {Number} labelWidth The width of labels. This property cascades to child containers.
28709      */
28710     /**
28711      * @cfg {String} itemCls A css class to apply to the x-form-item of fields. This property cascades to child containers.
28712      */
28713     /**
28714      * @cfg {String} buttonAlign Valid values are "left," "center" and "right" (defaults to "center")
28715      */
28716     buttonAlign:'center',
28717
28718     /**
28719      * @cfg {Number} minButtonWidth Minimum width of all buttons in pixels (defaults to 75)
28720      */
28721     minButtonWidth:75,
28722
28723     /**
28724      * @cfg {String} labelAlign Valid values are "left," "top" and "right" (defaults to "left").
28725      * This property cascades to child containers if not set.
28726      */
28727     labelAlign:'left',
28728
28729     /**
28730      * @cfg {Boolean} monitorValid If true the form monitors its valid state <b>client-side</b> and
28731      * fires a looping event with that state. This is required to bind buttons to the valid
28732      * state using the config value formBind:true on the button.
28733      */
28734     monitorValid : false,
28735
28736     /**
28737      * @cfg {Number} monitorPoll The milliseconds to poll valid state, ignored if monitorValid is not true (defaults to 200)
28738      */
28739     monitorPoll : 200,
28740     
28741     /**
28742      * @cfg {String} progressUrl - Url to return progress data 
28743      */
28744     
28745     progressUrl : false,
28746   
28747     /**
28748      * Opens a new {@link Roo.form.Column} container in the layout stack. If fields are passed after the config, the
28749      * fields are added and the column is closed. If no fields are passed the column remains open
28750      * until end() is called.
28751      * @param {Object} config The config to pass to the column
28752      * @param {Field} field1 (optional)
28753      * @param {Field} field2 (optional)
28754      * @param {Field} etc (optional)
28755      * @return Column The column container object
28756      */
28757     column : function(c){
28758         var col = new Roo.form.Column(c);
28759         this.start(col);
28760         if(arguments.length > 1){ // duplicate code required because of Opera
28761             this.add.apply(this, Array.prototype.slice.call(arguments, 1));
28762             this.end();
28763         }
28764         return col;
28765     },
28766
28767     /**
28768      * Opens a new {@link Roo.form.FieldSet} container in the layout stack. If fields are passed after the config, the
28769      * fields are added and the fieldset is closed. If no fields are passed the fieldset remains open
28770      * until end() is called.
28771      * @param {Object} config The config to pass to the fieldset
28772      * @param {Field} field1 (optional)
28773      * @param {Field} field2 (optional)
28774      * @param {Field} etc (optional)
28775      * @return FieldSet The fieldset container object
28776      */
28777     fieldset : function(c){
28778         var fs = new Roo.form.FieldSet(c);
28779         this.start(fs);
28780         if(arguments.length > 1){ // duplicate code required because of Opera
28781             this.add.apply(this, Array.prototype.slice.call(arguments, 1));
28782             this.end();
28783         }
28784         return fs;
28785     },
28786
28787     /**
28788      * Opens a new {@link Roo.form.Layout} container in the layout stack. If fields are passed after the config, the
28789      * fields are added and the container is closed. If no fields are passed the container remains open
28790      * until end() is called.
28791      * @param {Object} config The config to pass to the Layout
28792      * @param {Field} field1 (optional)
28793      * @param {Field} field2 (optional)
28794      * @param {Field} etc (optional)
28795      * @return Layout The container object
28796      */
28797     container : function(c){
28798         var l = new Roo.form.Layout(c);
28799         this.start(l);
28800         if(arguments.length > 1){ // duplicate code required because of Opera
28801             this.add.apply(this, Array.prototype.slice.call(arguments, 1));
28802             this.end();
28803         }
28804         return l;
28805     },
28806
28807     /**
28808      * Opens the passed container in the layout stack. The container can be any {@link Roo.form.Layout} or subclass.
28809      * @param {Object} container A Roo.form.Layout or subclass of Layout
28810      * @return {Form} this
28811      */
28812     start : function(c){
28813         // cascade label info
28814         Roo.applyIf(c, {'labelAlign': this.active.labelAlign, 'labelWidth': this.active.labelWidth, 'itemCls': this.active.itemCls});
28815         this.active.stack.push(c);
28816         c.ownerCt = this.active;
28817         this.active = c;
28818         return this;
28819     },
28820
28821     /**
28822      * Closes the current open container
28823      * @return {Form} this
28824      */
28825     end : function(){
28826         if(this.active == this.root){
28827             return this;
28828         }
28829         this.active = this.active.ownerCt;
28830         return this;
28831     },
28832
28833     /**
28834      * Add Roo.form components to the current open container (e.g. column, fieldset, etc.).  Fields added via this method
28835      * can also be passed with an additional property of fieldLabel, which if supplied, will provide the text to display
28836      * as the label of the field.
28837      * @param {Field} field1
28838      * @param {Field} field2 (optional)
28839      * @param {Field} etc. (optional)
28840      * @return {Form} this
28841      */
28842     add : function(){
28843         this.active.stack.push.apply(this.active.stack, arguments);
28844         this.allItems.push.apply(this.allItems,arguments);
28845         var r = [];
28846         for(var i = 0, a = arguments, len = a.length; i < len; i++) {
28847             if(a[i].isFormField){
28848                 r.push(a[i]);
28849             }
28850         }
28851         if(r.length > 0){
28852             Roo.form.Form.superclass.add.apply(this, r);
28853         }
28854         return this;
28855     },
28856     
28857
28858     
28859     
28860     
28861      /**
28862      * Find any element that has been added to a form, using it's ID or name
28863      * This can include framesets, columns etc. along with regular fields..
28864      * @param {String} id - id or name to find.
28865      
28866      * @return {Element} e - or false if nothing found.
28867      */
28868     findbyId : function(id)
28869     {
28870         var ret = false;
28871         if (!id) {
28872             return ret;
28873         }
28874         Roo.each(this.allItems, function(f){
28875             if (f.id == id || f.name == id ){
28876                 ret = f;
28877                 return false;
28878             }
28879         });
28880         return ret;
28881     },
28882
28883     
28884     
28885     /**
28886      * Render this form into the passed container. This should only be called once!
28887      * @param {String/HTMLElement/Element} container The element this component should be rendered into
28888      * @return {Form} this
28889      */
28890     render : function(ct)
28891     {
28892         
28893         
28894         
28895         ct = Roo.get(ct);
28896         var o = this.autoCreate || {
28897             tag: 'form',
28898             method : this.method || 'POST',
28899             id : this.id || Roo.id()
28900         };
28901         this.initEl(ct.createChild(o));
28902
28903         this.root.render(this.el);
28904         
28905        
28906              
28907         this.items.each(function(f){
28908             f.render('x-form-el-'+f.id);
28909         });
28910
28911         if(this.buttons.length > 0){
28912             // tables are required to maintain order and for correct IE layout
28913             var tb = this.el.createChild({cls:'x-form-btns-ct', cn: {
28914                 cls:"x-form-btns x-form-btns-"+this.buttonAlign,
28915                 html:'<table cellspacing="0"><tbody><tr></tr></tbody></table><div class="x-clear"></div>'
28916             }}, null, true);
28917             var tr = tb.getElementsByTagName('tr')[0];
28918             for(var i = 0, len = this.buttons.length; i < len; i++) {
28919                 var b = this.buttons[i];
28920                 var td = document.createElement('td');
28921                 td.className = 'x-form-btn-td';
28922                 b.render(tr.appendChild(td));
28923             }
28924         }
28925         if(this.monitorValid){ // initialize after render
28926             this.startMonitoring();
28927         }
28928         this.fireEvent('rendered', this);
28929         return this;
28930     },
28931
28932     /**
28933      * Adds a button to the footer of the form - this <b>must</b> be called before the form is rendered.
28934      * @param {String/Object} config A string becomes the button text, an object can either be a Button config
28935      * object or a valid Roo.DomHelper element config
28936      * @param {Function} handler The function called when the button is clicked
28937      * @param {Object} scope (optional) The scope of the handler function
28938      * @return {Roo.Button}
28939      */
28940     addButton : function(config, handler, scope){
28941         var bc = {
28942             handler: handler,
28943             scope: scope,
28944             minWidth: this.minButtonWidth,
28945             hideParent:true
28946         };
28947         if(typeof config == "string"){
28948             bc.text = config;
28949         }else{
28950             Roo.apply(bc, config);
28951         }
28952         var btn = new Roo.Button(null, bc);
28953         this.buttons.push(btn);
28954         return btn;
28955     },
28956
28957      /**
28958      * Adds a series of form elements (using the xtype property as the factory method.
28959      * Valid xtypes are:  TextField, TextArea .... Button, Layout, FieldSet, Column, (and 'end' to close a block)
28960      * @param {Object} config 
28961      */
28962     
28963     addxtype : function()
28964     {
28965         var ar = Array.prototype.slice.call(arguments, 0);
28966         var ret = false;
28967         for(var i = 0; i < ar.length; i++) {
28968             if (!ar[i]) {
28969                 continue; // skip -- if this happends something invalid got sent, we 
28970                 // should ignore it, as basically that interface element will not show up
28971                 // and that should be pretty obvious!!
28972             }
28973             
28974             if (Roo.form[ar[i].xtype]) {
28975                 ar[i].form = this;
28976                 var fe = Roo.factory(ar[i], Roo.form);
28977                 if (!ret) {
28978                     ret = fe;
28979                 }
28980                 fe.form = this;
28981                 if (fe.store) {
28982                     fe.store.form = this;
28983                 }
28984                 if (fe.isLayout) {  
28985                          
28986                     this.start(fe);
28987                     this.allItems.push(fe);
28988                     if (fe.items && fe.addxtype) {
28989                         fe.addxtype.apply(fe, fe.items);
28990                         delete fe.items;
28991                     }
28992                      this.end();
28993                     continue;
28994                 }
28995                 
28996                 
28997                  
28998                 this.add(fe);
28999               //  console.log('adding ' + ar[i].xtype);
29000             }
29001             if (ar[i].xtype == 'Button') {  
29002                 //console.log('adding button');
29003                 //console.log(ar[i]);
29004                 this.addButton(ar[i]);
29005                 this.allItems.push(fe);
29006                 continue;
29007             }
29008             
29009             if (ar[i].xtype == 'end') { // so we can add fieldsets... / layout etc.
29010                 alert('end is not supported on xtype any more, use items');
29011             //    this.end();
29012             //    //console.log('adding end');
29013             }
29014             
29015         }
29016         return ret;
29017     },
29018     
29019     /**
29020      * Starts monitoring of the valid state of this form. Usually this is done by passing the config
29021      * option "monitorValid"
29022      */
29023     startMonitoring : function(){
29024         if(!this.bound){
29025             this.bound = true;
29026             Roo.TaskMgr.start({
29027                 run : this.bindHandler,
29028                 interval : this.monitorPoll || 200,
29029                 scope: this
29030             });
29031         }
29032     },
29033
29034     /**
29035      * Stops monitoring of the valid state of this form
29036      */
29037     stopMonitoring : function(){
29038         this.bound = false;
29039     },
29040
29041     // private
29042     bindHandler : function(){
29043         if(!this.bound){
29044             return false; // stops binding
29045         }
29046         var valid = true;
29047         this.items.each(function(f){
29048             if(!f.isValid(true)){
29049                 valid = false;
29050                 return false;
29051             }
29052         });
29053         for(var i = 0, len = this.buttons.length; i < len; i++){
29054             var btn = this.buttons[i];
29055             if(btn.formBind === true && btn.disabled === valid){
29056                 btn.setDisabled(!valid);
29057             }
29058         }
29059         this.fireEvent('clientvalidation', this, valid);
29060     }
29061     
29062     
29063     
29064     
29065     
29066     
29067     
29068     
29069 });
29070
29071
29072 // back compat
29073 Roo.Form = Roo.form.Form;
29074 /*
29075  * Based on:
29076  * Ext JS Library 1.1.1
29077  * Copyright(c) 2006-2007, Ext JS, LLC.
29078  *
29079  * Originally Released Under LGPL - original licence link has changed is not relivant.
29080  *
29081  * Fork - LGPL
29082  * <script type="text/javascript">
29083  */
29084  
29085  /**
29086  * @class Roo.form.Action
29087  * Internal Class used to handle form actions
29088  * @constructor
29089  * @param {Roo.form.BasicForm} el The form element or its id
29090  * @param {Object} config Configuration options
29091  */
29092  
29093  
29094 // define the action interface
29095 Roo.form.Action = function(form, options){
29096     this.form = form;
29097     this.options = options || {};
29098 };
29099 /**
29100  * Client Validation Failed
29101  * @const 
29102  */
29103 Roo.form.Action.CLIENT_INVALID = 'client';
29104 /**
29105  * Server Validation Failed
29106  * @const 
29107  */
29108  Roo.form.Action.SERVER_INVALID = 'server';
29109  /**
29110  * Connect to Server Failed
29111  * @const 
29112  */
29113 Roo.form.Action.CONNECT_FAILURE = 'connect';
29114 /**
29115  * Reading Data from Server Failed
29116  * @const 
29117  */
29118 Roo.form.Action.LOAD_FAILURE = 'load';
29119
29120 Roo.form.Action.prototype = {
29121     type : 'default',
29122     failureType : undefined,
29123     response : undefined,
29124     result : undefined,
29125
29126     // interface method
29127     run : function(options){
29128
29129     },
29130
29131     // interface method
29132     success : function(response){
29133
29134     },
29135
29136     // interface method
29137     handleResponse : function(response){
29138
29139     },
29140
29141     // default connection failure
29142     failure : function(response){
29143         
29144         this.response = response;
29145         this.failureType = Roo.form.Action.CONNECT_FAILURE;
29146         this.form.afterAction(this, false);
29147     },
29148
29149     processResponse : function(response){
29150         this.response = response;
29151         if(!response.responseText){
29152             return true;
29153         }
29154         this.result = this.handleResponse(response);
29155         return this.result;
29156     },
29157
29158     // utility functions used internally
29159     getUrl : function(appendParams){
29160         var url = this.options.url || this.form.url || this.form.el.dom.action;
29161         if(appendParams){
29162             var p = this.getParams();
29163             if(p){
29164                 url += (url.indexOf('?') != -1 ? '&' : '?') + p;
29165             }
29166         }
29167         return url;
29168     },
29169
29170     getMethod : function(){
29171         return (this.options.method || this.form.method || this.form.el.dom.method || 'POST').toUpperCase();
29172     },
29173
29174     getParams : function(){
29175         var bp = this.form.baseParams;
29176         var p = this.options.params;
29177         if(p){
29178             if(typeof p == "object"){
29179                 p = Roo.urlEncode(Roo.applyIf(p, bp));
29180             }else if(typeof p == 'string' && bp){
29181                 p += '&' + Roo.urlEncode(bp);
29182             }
29183         }else if(bp){
29184             p = Roo.urlEncode(bp);
29185         }
29186         return p;
29187     },
29188
29189     createCallback : function(){
29190         return {
29191             success: this.success,
29192             failure: this.failure,
29193             scope: this,
29194             timeout: (this.form.timeout*1000),
29195             upload: this.form.fileUpload ? this.success : undefined
29196         };
29197     }
29198 };
29199
29200 Roo.form.Action.Submit = function(form, options){
29201     Roo.form.Action.Submit.superclass.constructor.call(this, form, options);
29202 };
29203
29204 Roo.extend(Roo.form.Action.Submit, Roo.form.Action, {
29205     type : 'submit',
29206
29207     haveProgress : false,
29208     uploadComplete : false,
29209     
29210     // uploadProgress indicator.
29211     uploadProgress : function()
29212     {
29213         if (!this.form.progressUrl) {
29214             return;
29215         }
29216         
29217         if (!this.haveProgress) {
29218             Roo.MessageBox.progress("Uploading", "Uploading");
29219         }
29220         if (this.uploadComplete) {
29221            Roo.MessageBox.hide();
29222            return;
29223         }
29224         
29225         this.haveProgress = true;
29226    
29227         var uid = this.form.findField('UPLOAD_IDENTIFIER').getValue();
29228         
29229         var c = new Roo.data.Connection();
29230         c.request({
29231             url : this.form.progressUrl,
29232             params: {
29233                 id : uid
29234             },
29235             method: 'GET',
29236             success : function(req){
29237                //console.log(data);
29238                 var rdata = false;
29239                 var edata;
29240                 try  {
29241                    rdata = Roo.decode(req.responseText)
29242                 } catch (e) {
29243                     Roo.log("Invalid data from server..");
29244                     Roo.log(edata);
29245                     return;
29246                 }
29247                 if (!rdata || !rdata.success) {
29248                     Roo.log(rdata);
29249                     Roo.MessageBox.alert(Roo.encode(rdata));
29250                     return;
29251                 }
29252                 var data = rdata.data;
29253                 
29254                 if (this.uploadComplete) {
29255                    Roo.MessageBox.hide();
29256                    return;
29257                 }
29258                    
29259                 if (data){
29260                     Roo.MessageBox.updateProgress(data.bytes_uploaded/data.bytes_total,
29261                        Math.floor((data.bytes_total - data.bytes_uploaded)/1000) + 'k remaining'
29262                     );
29263                 }
29264                 this.uploadProgress.defer(2000,this);
29265             },
29266        
29267             failure: function(data) {
29268                 Roo.log('progress url failed ');
29269                 Roo.log(data);
29270             },
29271             scope : this
29272         });
29273            
29274     },
29275     
29276     
29277     run : function()
29278     {
29279         // run get Values on the form, so it syncs any secondary forms.
29280         this.form.getValues();
29281         
29282         var o = this.options;
29283         var method = this.getMethod();
29284         var isPost = method == 'POST';
29285         if(o.clientValidation === false || this.form.isValid()){
29286             
29287             if (this.form.progressUrl) {
29288                 this.form.findField('UPLOAD_IDENTIFIER').setValue(
29289                     (new Date() * 1) + '' + Math.random());
29290                     
29291             } 
29292             
29293             
29294             Roo.Ajax.request(Roo.apply(this.createCallback(), {
29295                 form:this.form.el.dom,
29296                 url:this.getUrl(!isPost),
29297                 method: method,
29298                 params:isPost ? this.getParams() : null,
29299                 isUpload: this.form.fileUpload
29300             }));
29301             
29302             this.uploadProgress();
29303
29304         }else if (o.clientValidation !== false){ // client validation failed
29305             this.failureType = Roo.form.Action.CLIENT_INVALID;
29306             this.form.afterAction(this, false);
29307         }
29308     },
29309
29310     success : function(response)
29311     {
29312         this.uploadComplete= true;
29313         if (this.haveProgress) {
29314             Roo.MessageBox.hide();
29315         }
29316         
29317         
29318         var result = this.processResponse(response);
29319         if(result === true || result.success){
29320             this.form.afterAction(this, true);
29321             return;
29322         }
29323         if(result.errors){
29324             this.form.markInvalid(result.errors);
29325             this.failureType = Roo.form.Action.SERVER_INVALID;
29326         }
29327         this.form.afterAction(this, false);
29328     },
29329     failure : function(response)
29330     {
29331         this.uploadComplete= true;
29332         if (this.haveProgress) {
29333             Roo.MessageBox.hide();
29334         }
29335         
29336         this.response = response;
29337         this.failureType = Roo.form.Action.CONNECT_FAILURE;
29338         this.form.afterAction(this, false);
29339     },
29340     
29341     handleResponse : function(response){
29342         if(this.form.errorReader){
29343             var rs = this.form.errorReader.read(response);
29344             var errors = [];
29345             if(rs.records){
29346                 for(var i = 0, len = rs.records.length; i < len; i++) {
29347                     var r = rs.records[i];
29348                     errors[i] = r.data;
29349                 }
29350             }
29351             if(errors.length < 1){
29352                 errors = null;
29353             }
29354             return {
29355                 success : rs.success,
29356                 errors : errors
29357             };
29358         }
29359         var ret = false;
29360         try {
29361             ret = Roo.decode(response.responseText);
29362         } catch (e) {
29363             ret = {
29364                 success: false,
29365                 errorMsg: "Failed to read server message: " + (response ? response.responseText : ' - no message'),
29366                 errors : []
29367             };
29368         }
29369         return ret;
29370         
29371     }
29372 });
29373
29374
29375 Roo.form.Action.Load = function(form, options){
29376     Roo.form.Action.Load.superclass.constructor.call(this, form, options);
29377     this.reader = this.form.reader;
29378 };
29379
29380 Roo.extend(Roo.form.Action.Load, Roo.form.Action, {
29381     type : 'load',
29382
29383     run : function(){
29384         
29385         Roo.Ajax.request(Roo.apply(
29386                 this.createCallback(), {
29387                     method:this.getMethod(),
29388                     url:this.getUrl(false),
29389                     params:this.getParams()
29390         }));
29391     },
29392
29393     success : function(response){
29394         
29395         var result = this.processResponse(response);
29396         if(result === true || !result.success || !result.data){
29397             this.failureType = Roo.form.Action.LOAD_FAILURE;
29398             this.form.afterAction(this, false);
29399             return;
29400         }
29401         this.form.clearInvalid();
29402         this.form.setValues(result.data);
29403         this.form.afterAction(this, true);
29404     },
29405
29406     handleResponse : function(response){
29407         if(this.form.reader){
29408             var rs = this.form.reader.read(response);
29409             var data = rs.records && rs.records[0] ? rs.records[0].data : null;
29410             return {
29411                 success : rs.success,
29412                 data : data
29413             };
29414         }
29415         return Roo.decode(response.responseText);
29416     }
29417 });
29418
29419 Roo.form.Action.ACTION_TYPES = {
29420     'load' : Roo.form.Action.Load,
29421     'submit' : Roo.form.Action.Submit
29422 };/*
29423  * Based on:
29424  * Ext JS Library 1.1.1
29425  * Copyright(c) 2006-2007, Ext JS, LLC.
29426  *
29427  * Originally Released Under LGPL - original licence link has changed is not relivant.
29428  *
29429  * Fork - LGPL
29430  * <script type="text/javascript">
29431  */
29432  
29433 /**
29434  * @class Roo.form.Layout
29435  * @extends Roo.Component
29436  * Creates a container for layout and rendering of fields in an {@link Roo.form.Form}.
29437  * @constructor
29438  * @param {Object} config Configuration options
29439  */
29440 Roo.form.Layout = function(config){
29441     var xitems = [];
29442     if (config.items) {
29443         xitems = config.items;
29444         delete config.items;
29445     }
29446     Roo.form.Layout.superclass.constructor.call(this, config);
29447     this.stack = [];
29448     Roo.each(xitems, this.addxtype, this);
29449      
29450 };
29451
29452 Roo.extend(Roo.form.Layout, Roo.Component, {
29453     /**
29454      * @cfg {String/Object} autoCreate
29455      * A DomHelper element spec used to autocreate the layout (defaults to {tag: 'div', cls: 'x-form-ct'})
29456      */
29457     /**
29458      * @cfg {String/Object/Function} style
29459      * A style specification string, e.g. "width:100px", or object in the form {width:"100px"}, or
29460      * a function which returns such a specification.
29461      */
29462     /**
29463      * @cfg {String} labelAlign
29464      * Valid values are "left," "top" and "right" (defaults to "left")
29465      */
29466     /**
29467      * @cfg {Number} labelWidth
29468      * Fixed width in pixels of all field labels (defaults to undefined)
29469      */
29470     /**
29471      * @cfg {Boolean} clear
29472      * True to add a clearing element at the end of this layout, equivalent to CSS clear: both (defaults to true)
29473      */
29474     clear : true,
29475     /**
29476      * @cfg {String} labelSeparator
29477      * The separator to use after field labels (defaults to ':')
29478      */
29479     labelSeparator : ':',
29480     /**
29481      * @cfg {Boolean} hideLabels
29482      * True to suppress the display of field labels in this layout (defaults to false)
29483      */
29484     hideLabels : false,
29485
29486     // private
29487     defaultAutoCreate : {tag: 'div', cls: 'x-form-ct'},
29488     
29489     isLayout : true,
29490     
29491     // private
29492     onRender : function(ct, position){
29493         if(this.el){ // from markup
29494             this.el = Roo.get(this.el);
29495         }else {  // generate
29496             var cfg = this.getAutoCreate();
29497             this.el = ct.createChild(cfg, position);
29498         }
29499         if(this.style){
29500             this.el.applyStyles(this.style);
29501         }
29502         if(this.labelAlign){
29503             this.el.addClass('x-form-label-'+this.labelAlign);
29504         }
29505         if(this.hideLabels){
29506             this.labelStyle = "display:none";
29507             this.elementStyle = "padding-left:0;";
29508         }else{
29509             if(typeof this.labelWidth == 'number'){
29510                 this.labelStyle = "width:"+this.labelWidth+"px;";
29511                 this.elementStyle = "padding-left:"+((this.labelWidth+(typeof this.labelPad == 'number' ? this.labelPad : 5))+'px')+";";
29512             }
29513             if(this.labelAlign == 'top'){
29514                 this.labelStyle = "width:auto;";
29515                 this.elementStyle = "padding-left:0;";
29516             }
29517         }
29518         var stack = this.stack;
29519         var slen = stack.length;
29520         if(slen > 0){
29521             if(!this.fieldTpl){
29522                 var t = new Roo.Template(
29523                     '<div class="x-form-item {5}">',
29524                         '<label for="{0}" style="{2}">{1}{4}</label>',
29525                         '<div class="x-form-element" id="x-form-el-{0}" style="{3}">',
29526                         '</div>',
29527                     '</div><div class="x-form-clear-left"></div>'
29528                 );
29529                 t.disableFormats = true;
29530                 t.compile();
29531                 Roo.form.Layout.prototype.fieldTpl = t;
29532             }
29533             for(var i = 0; i < slen; i++) {
29534                 if(stack[i].isFormField){
29535                     this.renderField(stack[i]);
29536                 }else{
29537                     this.renderComponent(stack[i]);
29538                 }
29539             }
29540         }
29541         if(this.clear){
29542             this.el.createChild({cls:'x-form-clear'});
29543         }
29544     },
29545
29546     // private
29547     renderField : function(f){
29548         f.fieldEl = Roo.get(this.fieldTpl.append(this.el, [
29549                f.id, //0
29550                f.fieldLabel, //1
29551                f.labelStyle||this.labelStyle||'', //2
29552                this.elementStyle||'', //3
29553                typeof f.labelSeparator == 'undefined' ? this.labelSeparator : f.labelSeparator, //4
29554                f.itemCls||this.itemCls||''  //5
29555        ], true).getPrevSibling());
29556     },
29557
29558     // private
29559     renderComponent : function(c){
29560         c.render(c.isLayout ? this.el : this.el.createChild());    
29561     },
29562     /**
29563      * Adds a object form elements (using the xtype property as the factory method.)
29564      * Valid xtypes are:  TextField, TextArea .... Button, Layout, FieldSet, Column
29565      * @param {Object} config 
29566      */
29567     addxtype : function(o)
29568     {
29569         // create the lement.
29570         o.form = this.form;
29571         var fe = Roo.factory(o, Roo.form);
29572         this.form.allItems.push(fe);
29573         this.stack.push(fe);
29574         
29575         if (fe.isFormField) {
29576             this.form.items.add(fe);
29577         }
29578          
29579         return fe;
29580     }
29581 });
29582
29583 /**
29584  * @class Roo.form.Column
29585  * @extends Roo.form.Layout
29586  * Creates a column container for layout and rendering of fields in an {@link Roo.form.Form}.
29587  * @constructor
29588  * @param {Object} config Configuration options
29589  */
29590 Roo.form.Column = function(config){
29591     Roo.form.Column.superclass.constructor.call(this, config);
29592 };
29593
29594 Roo.extend(Roo.form.Column, Roo.form.Layout, {
29595     /**
29596      * @cfg {Number/String} width
29597      * The fixed width of the column in pixels or CSS value (defaults to "auto")
29598      */
29599     /**
29600      * @cfg {String/Object} autoCreate
29601      * A DomHelper element spec used to autocreate the column (defaults to {tag: 'div', cls: 'x-form-ct x-form-column'})
29602      */
29603
29604     // private
29605     defaultAutoCreate : {tag: 'div', cls: 'x-form-ct x-form-column'},
29606
29607     // private
29608     onRender : function(ct, position){
29609         Roo.form.Column.superclass.onRender.call(this, ct, position);
29610         if(this.width){
29611             this.el.setWidth(this.width);
29612         }
29613     }
29614 });
29615
29616
29617 /**
29618  * @class Roo.form.Row
29619  * @extends Roo.form.Layout
29620  * Creates a row container for layout and rendering of fields in an {@link Roo.form.Form}.
29621  * @constructor
29622  * @param {Object} config Configuration options
29623  */
29624
29625  
29626 Roo.form.Row = function(config){
29627     Roo.form.Row.superclass.constructor.call(this, config);
29628 };
29629  
29630 Roo.extend(Roo.form.Row, Roo.form.Layout, {
29631       /**
29632      * @cfg {Number/String} width
29633      * The fixed width of the column in pixels or CSS value (defaults to "auto")
29634      */
29635     /**
29636      * @cfg {Number/String} height
29637      * The fixed height of the column in pixels or CSS value (defaults to "auto")
29638      */
29639     defaultAutoCreate : {tag: 'div', cls: 'x-form-ct x-form-row'},
29640     
29641     padWidth : 20,
29642     // private
29643     onRender : function(ct, position){
29644         //console.log('row render');
29645         if(!this.rowTpl){
29646             var t = new Roo.Template(
29647                 '<div class="x-form-item {5}" style="float:left;width:{6}px">',
29648                     '<label for="{0}" style="{2}">{1}{4}</label>',
29649                     '<div class="x-form-element" id="x-form-el-{0}" style="{3}">',
29650                     '</div>',
29651                 '</div>'
29652             );
29653             t.disableFormats = true;
29654             t.compile();
29655             Roo.form.Layout.prototype.rowTpl = t;
29656         }
29657         this.fieldTpl = this.rowTpl;
29658         
29659         //console.log('lw' + this.labelWidth +', la:' + this.labelAlign);
29660         var labelWidth = 100;
29661         
29662         if ((this.labelAlign != 'top')) {
29663             if (typeof this.labelWidth == 'number') {
29664                 labelWidth = this.labelWidth
29665             }
29666             this.padWidth =  20 + labelWidth;
29667             
29668         }
29669         
29670         Roo.form.Column.superclass.onRender.call(this, ct, position);
29671         if(this.width){
29672             this.el.setWidth(this.width);
29673         }
29674         if(this.height){
29675             this.el.setHeight(this.height);
29676         }
29677     },
29678     
29679     // private
29680     renderField : function(f){
29681         f.fieldEl = this.fieldTpl.append(this.el, [
29682                f.id, f.fieldLabel,
29683                f.labelStyle||this.labelStyle||'',
29684                this.elementStyle||'',
29685                typeof f.labelSeparator == 'undefined' ? this.labelSeparator : f.labelSeparator,
29686                f.itemCls||this.itemCls||'',
29687                f.width ? f.width + this.padWidth : 160 + this.padWidth
29688        ],true);
29689     }
29690 });
29691  
29692
29693 /**
29694  * @class Roo.form.FieldSet
29695  * @extends Roo.form.Layout
29696  * Creates a fieldset container for layout and rendering of fields in an {@link Roo.form.Form}.
29697  * @constructor
29698  * @param {Object} config Configuration options
29699  */
29700 Roo.form.FieldSet = function(config){
29701     Roo.form.FieldSet.superclass.constructor.call(this, config);
29702 };
29703
29704 Roo.extend(Roo.form.FieldSet, Roo.form.Layout, {
29705     /**
29706      * @cfg {String} legend
29707      * The text to display as the legend for the FieldSet (defaults to '')
29708      */
29709     /**
29710      * @cfg {String/Object} autoCreate
29711      * A DomHelper element spec used to autocreate the fieldset (defaults to {tag: 'fieldset', cn: {tag:'legend'}})
29712      */
29713
29714     // private
29715     defaultAutoCreate : {tag: 'fieldset', cn: {tag:'legend'}},
29716
29717     // private
29718     onRender : function(ct, position){
29719         Roo.form.FieldSet.superclass.onRender.call(this, ct, position);
29720         if(this.legend){
29721             this.setLegend(this.legend);
29722         }
29723     },
29724
29725     // private
29726     setLegend : function(text){
29727         if(this.rendered){
29728             this.el.child('legend').update(text);
29729         }
29730     }
29731 });/*
29732  * Based on:
29733  * Ext JS Library 1.1.1
29734  * Copyright(c) 2006-2007, Ext JS, LLC.
29735  *
29736  * Originally Released Under LGPL - original licence link has changed is not relivant.
29737  *
29738  * Fork - LGPL
29739  * <script type="text/javascript">
29740  */
29741 /**
29742  * @class Roo.form.VTypes
29743  * Overridable validation definitions. The validations provided are basic and intended to be easily customizable and extended.
29744  * @singleton
29745  */
29746 Roo.form.VTypes = function(){
29747     // closure these in so they are only created once.
29748     var alpha = /^[a-zA-Z_]+$/;
29749     var alphanum = /^[a-zA-Z0-9_]+$/;
29750     var email = /^([\w]+)(.[\w]+)*@([\w-]+\.){1,5}([A-Za-z]){2,4}$/;
29751     var url = /(((https?)|(ftp)):\/\/([\-\w]+\.)+\w{2,3}(\/[%\-\w]+(\.\w{2,})?)*(([\w\-\.\?\\\/+@&#;`~=%!]*)(\.\w{2,})?)*\/?)/i;
29752
29753     // All these messages and functions are configurable
29754     return {
29755         /**
29756          * The function used to validate email addresses
29757          * @param {String} value The email address
29758          */
29759         'email' : function(v){
29760             return email.test(v);
29761         },
29762         /**
29763          * The error text to display when the email validation function returns false
29764          * @type String
29765          */
29766         'emailText' : 'This field should be an e-mail address in the format "user@domain.com"',
29767         /**
29768          * The keystroke filter mask to be applied on email input
29769          * @type RegExp
29770          */
29771         'emailMask' : /[a-z0-9_\.\-@]/i,
29772
29773         /**
29774          * The function used to validate URLs
29775          * @param {String} value The URL
29776          */
29777         'url' : function(v){
29778             return url.test(v);
29779         },
29780         /**
29781          * The error text to display when the url validation function returns false
29782          * @type String
29783          */
29784         'urlText' : 'This field should be a URL in the format "http:/'+'/www.domain.com"',
29785         
29786         /**
29787          * The function used to validate alpha values
29788          * @param {String} value The value
29789          */
29790         'alpha' : function(v){
29791             return alpha.test(v);
29792         },
29793         /**
29794          * The error text to display when the alpha validation function returns false
29795          * @type String
29796          */
29797         'alphaText' : 'This field should only contain letters and _',
29798         /**
29799          * The keystroke filter mask to be applied on alpha input
29800          * @type RegExp
29801          */
29802         'alphaMask' : /[a-z_]/i,
29803
29804         /**
29805          * The function used to validate alphanumeric values
29806          * @param {String} value The value
29807          */
29808         'alphanum' : function(v){
29809             return alphanum.test(v);
29810         },
29811         /**
29812          * The error text to display when the alphanumeric validation function returns false
29813          * @type String
29814          */
29815         'alphanumText' : 'This field should only contain letters, numbers and _',
29816         /**
29817          * The keystroke filter mask to be applied on alphanumeric input
29818          * @type RegExp
29819          */
29820         'alphanumMask' : /[a-z0-9_]/i
29821     };
29822 }();//<script type="text/javascript">
29823
29824 /**
29825  * @class Roo.form.FCKeditor
29826  * @extends Roo.form.TextArea
29827  * Wrapper around the FCKEditor http://www.fckeditor.net
29828  * @constructor
29829  * Creates a new FCKeditor
29830  * @param {Object} config Configuration options
29831  */
29832 Roo.form.FCKeditor = function(config){
29833     Roo.form.FCKeditor.superclass.constructor.call(this, config);
29834     this.addEvents({
29835          /**
29836          * @event editorinit
29837          * Fired when the editor is initialized - you can add extra handlers here..
29838          * @param {FCKeditor} this
29839          * @param {Object} the FCK object.
29840          */
29841         editorinit : true
29842     });
29843     
29844     
29845 };
29846 Roo.form.FCKeditor.editors = { };
29847 Roo.extend(Roo.form.FCKeditor, Roo.form.TextArea,
29848 {
29849     //defaultAutoCreate : {
29850     //    tag : "textarea",style   : "width:100px;height:60px;" ,autocomplete    : "off"
29851     //},
29852     // private
29853     /**
29854      * @cfg {Object} fck options - see fck manual for details.
29855      */
29856     fckconfig : false,
29857     
29858     /**
29859      * @cfg {Object} fck toolbar set (Basic or Default)
29860      */
29861     toolbarSet : 'Basic',
29862     /**
29863      * @cfg {Object} fck BasePath
29864      */ 
29865     basePath : '/fckeditor/',
29866     
29867     
29868     frame : false,
29869     
29870     value : '',
29871     
29872    
29873     onRender : function(ct, position)
29874     {
29875         if(!this.el){
29876             this.defaultAutoCreate = {
29877                 tag: "textarea",
29878                 style:"width:300px;height:60px;",
29879                 autocomplete: "off"
29880             };
29881         }
29882         Roo.form.FCKeditor.superclass.onRender.call(this, ct, position);
29883         /*
29884         if(this.grow){
29885             this.textSizeEl = Roo.DomHelper.append(document.body, {tag: "pre", cls: "x-form-grow-sizer"});
29886             if(this.preventScrollbars){
29887                 this.el.setStyle("overflow", "hidden");
29888             }
29889             this.el.setHeight(this.growMin);
29890         }
29891         */
29892         //console.log('onrender' + this.getId() );
29893         Roo.form.FCKeditor.editors[this.getId()] = this;
29894          
29895
29896         this.replaceTextarea() ;
29897         
29898     },
29899     
29900     getEditor : function() {
29901         return this.fckEditor;
29902     },
29903     /**
29904      * Sets a data value into the field and validates it.  To set the value directly without validation see {@link #setRawValue}.
29905      * @param {Mixed} value The value to set
29906      */
29907     
29908     
29909     setValue : function(value)
29910     {
29911         //console.log('setValue: ' + value);
29912         
29913         if(typeof(value) == 'undefined') { // not sure why this is happending...
29914             return;
29915         }
29916         Roo.form.FCKeditor.superclass.setValue.apply(this,[value]);
29917         
29918         //if(!this.el || !this.getEditor()) {
29919         //    this.value = value;
29920             //this.setValue.defer(100,this,[value]);    
29921         //    return;
29922         //} 
29923         
29924         if(!this.getEditor()) {
29925             return;
29926         }
29927         
29928         this.getEditor().SetData(value);
29929         
29930         //
29931
29932     },
29933
29934     /**
29935      * Returns the normalized data value (undefined or emptyText will be returned as '').  To return the raw value see {@link #getRawValue}.
29936      * @return {Mixed} value The field value
29937      */
29938     getValue : function()
29939     {
29940         
29941         if (this.frame && this.frame.dom.style.display == 'none') {
29942             return Roo.form.FCKeditor.superclass.getValue.call(this);
29943         }
29944         
29945         if(!this.el || !this.getEditor()) {
29946            
29947            // this.getValue.defer(100,this); 
29948             return this.value;
29949         }
29950        
29951         
29952         var value=this.getEditor().GetData();
29953         Roo.form.FCKeditor.superclass.setValue.apply(this,[value]);
29954         return Roo.form.FCKeditor.superclass.getValue.call(this);
29955         
29956
29957     },
29958
29959     /**
29960      * Returns the raw data value which may or may not be a valid, defined value.  To return a normalized value see {@link #getValue}.
29961      * @return {Mixed} value The field value
29962      */
29963     getRawValue : function()
29964     {
29965         if (this.frame && this.frame.dom.style.display == 'none') {
29966             return Roo.form.FCKeditor.superclass.getRawValue.call(this);
29967         }
29968         
29969         if(!this.el || !this.getEditor()) {
29970             //this.getRawValue.defer(100,this); 
29971             return this.value;
29972             return;
29973         }
29974         
29975         
29976         
29977         var value=this.getEditor().GetData();
29978         Roo.form.FCKeditor.superclass.setRawValue.apply(this,[value]);
29979         return Roo.form.FCKeditor.superclass.getRawValue.call(this);
29980          
29981     },
29982     
29983     setSize : function(w,h) {
29984         
29985         
29986         
29987         //if (this.frame && this.frame.dom.style.display == 'none') {
29988         //    Roo.form.FCKeditor.superclass.setSize.apply(this, [w, h]);
29989         //    return;
29990         //}
29991         //if(!this.el || !this.getEditor()) {
29992         //    this.setSize.defer(100,this, [w,h]); 
29993         //    return;
29994         //}
29995         
29996         
29997         
29998         Roo.form.FCKeditor.superclass.setSize.apply(this, [w, h]);
29999         
30000         this.frame.dom.setAttribute('width', w);
30001         this.frame.dom.setAttribute('height', h);
30002         this.frame.setSize(w,h);
30003         
30004     },
30005     
30006     toggleSourceEdit : function(value) {
30007         
30008       
30009          
30010         this.el.dom.style.display = value ? '' : 'none';
30011         this.frame.dom.style.display = value ?  'none' : '';
30012         
30013     },
30014     
30015     
30016     focus: function(tag)
30017     {
30018         if (this.frame.dom.style.display == 'none') {
30019             return Roo.form.FCKeditor.superclass.focus.call(this);
30020         }
30021         if(!this.el || !this.getEditor()) {
30022             this.focus.defer(100,this, [tag]); 
30023             return;
30024         }
30025         
30026         
30027         
30028         
30029         var tgs = this.getEditor().EditorDocument.getElementsByTagName(tag);
30030         this.getEditor().Focus();
30031         if (tgs.length) {
30032             if (!this.getEditor().Selection.GetSelection()) {
30033                 this.focus.defer(100,this, [tag]); 
30034                 return;
30035             }
30036             
30037             
30038             var r = this.getEditor().EditorDocument.createRange();
30039             r.setStart(tgs[0],0);
30040             r.setEnd(tgs[0],0);
30041             this.getEditor().Selection.GetSelection().removeAllRanges();
30042             this.getEditor().Selection.GetSelection().addRange(r);
30043             this.getEditor().Focus();
30044         }
30045         
30046     },
30047     
30048     
30049     
30050     replaceTextarea : function()
30051     {
30052         if ( document.getElementById( this.getId() + '___Frame' ) )
30053             return ;
30054         //if ( !this.checkBrowser || this._isCompatibleBrowser() )
30055         //{
30056             // We must check the elements firstly using the Id and then the name.
30057         var oTextarea = document.getElementById( this.getId() );
30058         
30059         var colElementsByName = document.getElementsByName( this.getId() ) ;
30060          
30061         oTextarea.style.display = 'none' ;
30062
30063         if ( oTextarea.tabIndex ) {            
30064             this.TabIndex = oTextarea.tabIndex ;
30065         }
30066         
30067         this._insertHtmlBefore( this._getConfigHtml(), oTextarea ) ;
30068         this._insertHtmlBefore( this._getIFrameHtml(), oTextarea ) ;
30069         this.frame = Roo.get(this.getId() + '___Frame')
30070     },
30071     
30072     _getConfigHtml : function()
30073     {
30074         var sConfig = '' ;
30075
30076         for ( var o in this.fckconfig ) {
30077             sConfig += sConfig.length > 0  ? '&amp;' : '';
30078             sConfig += encodeURIComponent( o ) + '=' + encodeURIComponent( this.fckconfig[o] ) ;
30079         }
30080
30081         return '<input type="hidden" id="' + this.getId() + '___Config" value="' + sConfig + '" style="display:none" />' ;
30082     },
30083     
30084     
30085     _getIFrameHtml : function()
30086     {
30087         var sFile = 'fckeditor.html' ;
30088         /* no idea what this is about..
30089         try
30090         {
30091             if ( (/fcksource=true/i).test( window.top.location.search ) )
30092                 sFile = 'fckeditor.original.html' ;
30093         }
30094         catch (e) { 
30095         */
30096
30097         var sLink = this.basePath + 'editor/' + sFile + '?InstanceName=' + encodeURIComponent( this.getId() ) ;
30098         sLink += this.toolbarSet ? ( '&amp;Toolbar=' + this.toolbarSet)  : '';
30099         
30100         
30101         var html = '<iframe id="' + this.getId() +
30102             '___Frame" src="' + sLink +
30103             '" width="' + this.width +
30104             '" height="' + this.height + '"' +
30105             (this.tabIndex ?  ' tabindex="' + this.tabIndex + '"' :'' ) +
30106             ' frameborder="0" scrolling="no"></iframe>' ;
30107
30108         return html ;
30109     },
30110     
30111     _insertHtmlBefore : function( html, element )
30112     {
30113         if ( element.insertAdjacentHTML )       {
30114             // IE
30115             element.insertAdjacentHTML( 'beforeBegin', html ) ;
30116         } else { // Gecko
30117             var oRange = document.createRange() ;
30118             oRange.setStartBefore( element ) ;
30119             var oFragment = oRange.createContextualFragment( html );
30120             element.parentNode.insertBefore( oFragment, element ) ;
30121         }
30122     }
30123     
30124     
30125   
30126     
30127     
30128     
30129     
30130
30131 });
30132
30133 //Roo.reg('fckeditor', Roo.form.FCKeditor);
30134
30135 function FCKeditor_OnComplete(editorInstance){
30136     var f = Roo.form.FCKeditor.editors[editorInstance.Name];
30137     f.fckEditor = editorInstance;
30138     //console.log("loaded");
30139     f.fireEvent('editorinit', f, editorInstance);
30140
30141   
30142
30143  
30144
30145
30146
30147
30148
30149
30150
30151
30152
30153
30154
30155
30156
30157
30158
30159 //<script type="text/javascript">
30160 /**
30161  * @class Roo.form.GridField
30162  * @extends Roo.form.Field
30163  * Embed a grid (or editable grid into a form)
30164  * STATUS ALPHA
30165  * 
30166  * This embeds a grid in a form, the value of the field should be the json encoded array of rows
30167  * it needs 
30168  * xgrid.store = Roo.data.Store
30169  * xgrid.store.proxy = Roo.data.MemoryProxy (data = [] )
30170  * xgrid.store.reader = Roo.data.JsonReader 
30171  * 
30172  * 
30173  * @constructor
30174  * Creates a new GridField
30175  * @param {Object} config Configuration options
30176  */
30177 Roo.form.GridField = function(config){
30178     Roo.form.GridField.superclass.constructor.call(this, config);
30179      
30180 };
30181
30182 Roo.extend(Roo.form.GridField, Roo.form.Field,  {
30183     /**
30184      * @cfg {Number} width  - used to restrict width of grid..
30185      */
30186     width : 100,
30187     /**
30188      * @cfg {Number} height - used to restrict height of grid..
30189      */
30190     height : 50,
30191      /**
30192      * @cfg {Object} xgrid (xtype'd description of grid) { xtype : 'Grid', dataSource: .... }
30193          * 
30194          *}
30195      */
30196     xgrid : false, 
30197     /**
30198      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
30199      * {tag: "input", type: "checkbox", autocomplete: "off"})
30200      */
30201    // defaultAutoCreate : { tag: 'div' },
30202     defaultAutoCreate : { tag: 'input', type: 'hidden', autocomplete: 'off'},
30203     /**
30204      * @cfg {String} addTitle Text to include for adding a title.
30205      */
30206     addTitle : false,
30207     //
30208     onResize : function(){
30209         Roo.form.Field.superclass.onResize.apply(this, arguments);
30210     },
30211
30212     initEvents : function(){
30213         // Roo.form.Checkbox.superclass.initEvents.call(this);
30214         // has no events...
30215        
30216     },
30217
30218
30219     getResizeEl : function(){
30220         return this.wrap;
30221     },
30222
30223     getPositionEl : function(){
30224         return this.wrap;
30225     },
30226
30227     // private
30228     onRender : function(ct, position){
30229         
30230         this.style = this.style || 'overflow: hidden; border:1px solid #c3daf9;';
30231         var style = this.style;
30232         delete this.style;
30233         
30234         Roo.form.GridField.superclass.onRender.call(this, ct, position);
30235         this.wrap = this.el.wrap({cls: ''}); // not sure why ive done thsi...
30236         this.viewEl = this.wrap.createChild({ tag: 'div' });
30237         if (style) {
30238             this.viewEl.applyStyles(style);
30239         }
30240         if (this.width) {
30241             this.viewEl.setWidth(this.width);
30242         }
30243         if (this.height) {
30244             this.viewEl.setHeight(this.height);
30245         }
30246         //if(this.inputValue !== undefined){
30247         //this.setValue(this.value);
30248         
30249         
30250         this.grid = new Roo.grid[this.xgrid.xtype](this.viewEl, this.xgrid);
30251         
30252         
30253         this.grid.render();
30254         this.grid.getDataSource().on('remove', this.refreshValue, this);
30255         this.grid.getDataSource().on('update', this.refreshValue, this);
30256         this.grid.on('afteredit', this.refreshValue, this);
30257  
30258     },
30259      
30260     
30261     /**
30262      * Sets the value of the item. 
30263      * @param {String} either an object  or a string..
30264      */
30265     setValue : function(v){
30266         //this.value = v;
30267         v = v || []; // empty set..
30268         // this does not seem smart - it really only affects memoryproxy grids..
30269         if (this.grid && this.grid.getDataSource() && typeof(v) != 'undefined') {
30270             var ds = this.grid.getDataSource();
30271             // assumes a json reader..
30272             var data = {}
30273             data[ds.reader.meta.root ] =  typeof(v) == 'string' ? Roo.decode(v) : v;
30274             ds.loadData( data);
30275         }
30276         // clear selection so it does not get stale.
30277         if (this.grid.sm) { 
30278             this.grid.sm.clearSelections();
30279         }
30280         
30281         Roo.form.GridField.superclass.setValue.call(this, v);
30282         this.refreshValue();
30283         // should load data in the grid really....
30284     },
30285     
30286     // private
30287     refreshValue: function() {
30288          var val = [];
30289         this.grid.getDataSource().each(function(r) {
30290             val.push(r.data);
30291         });
30292         this.el.dom.value = Roo.encode(val);
30293     }
30294     
30295      
30296     
30297     
30298 });/*
30299  * Based on:
30300  * Ext JS Library 1.1.1
30301  * Copyright(c) 2006-2007, Ext JS, LLC.
30302  *
30303  * Originally Released Under LGPL - original licence link has changed is not relivant.
30304  *
30305  * Fork - LGPL
30306  * <script type="text/javascript">
30307  */
30308 /**
30309  * @class Roo.form.DisplayField
30310  * @extends Roo.form.Field
30311  * A generic Field to display non-editable data.
30312  * @constructor
30313  * Creates a new Display Field item.
30314  * @param {Object} config Configuration options
30315  */
30316 Roo.form.DisplayField = function(config){
30317     Roo.form.DisplayField.superclass.constructor.call(this, config);
30318     
30319 };
30320
30321 Roo.extend(Roo.form.DisplayField, Roo.form.TextField,  {
30322     inputType:      'hidden',
30323     allowBlank:     true,
30324     readOnly:         true,
30325     
30326  
30327     /**
30328      * @cfg {String} focusClass The CSS class to use when the checkbox receives focus (defaults to undefined)
30329      */
30330     focusClass : undefined,
30331     /**
30332      * @cfg {String} fieldClass The default CSS class for the checkbox (defaults to "x-form-field")
30333      */
30334     fieldClass: 'x-form-field',
30335     
30336      /**
30337      * @cfg {Function} valueRenderer The renderer for the field (so you can reformat output). should return raw HTML
30338      */
30339     valueRenderer: undefined,
30340     
30341     width: 100,
30342     /**
30343      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
30344      * {tag: "input", type: "checkbox", autocomplete: "off"})
30345      */
30346      
30347  //   defaultAutoCreate : { tag: 'input', type: 'hidden', autocomplete: 'off'},
30348
30349     onResize : function(){
30350         Roo.form.DisplayField.superclass.onResize.apply(this, arguments);
30351         
30352     },
30353
30354     initEvents : function(){
30355         // Roo.form.Checkbox.superclass.initEvents.call(this);
30356         // has no events...
30357        
30358     },
30359
30360
30361     getResizeEl : function(){
30362         return this.wrap;
30363     },
30364
30365     getPositionEl : function(){
30366         return this.wrap;
30367     },
30368
30369     // private
30370     onRender : function(ct, position){
30371         
30372         Roo.form.DisplayField.superclass.onRender.call(this, ct, position);
30373         //if(this.inputValue !== undefined){
30374         this.wrap = this.el.wrap();
30375         
30376         this.viewEl = this.wrap.createChild({ tag: 'div', cls: 'x-form-displayfield'});
30377         
30378         if (this.bodyStyle) {
30379             this.viewEl.applyStyles(this.bodyStyle);
30380         }
30381         //this.viewEl.setStyle('padding', '2px');
30382         
30383         this.setValue(this.value);
30384         
30385     },
30386 /*
30387     // private
30388     initValue : Roo.emptyFn,
30389
30390   */
30391
30392         // private
30393     onClick : function(){
30394         
30395     },
30396
30397     /**
30398      * Sets the checked state of the checkbox.
30399      * @param {Boolean/String} checked True, 'true', '1', or 'on' to check the checkbox, any other value will uncheck it.
30400      */
30401     setValue : function(v){
30402         this.value = v;
30403         var html = this.valueRenderer ?  this.valueRenderer(v) : String.format('{0}', v);
30404         // this might be called before we have a dom element..
30405         if (!this.viewEl) {
30406             return;
30407         }
30408         this.viewEl.dom.innerHTML = html;
30409         Roo.form.DisplayField.superclass.setValue.call(this, v);
30410
30411     }
30412 });/*
30413  * 
30414  * Licence- LGPL
30415  * 
30416  */
30417
30418 /**
30419  * @class Roo.form.DayPicker
30420  * @extends Roo.form.Field
30421  * A Day picker show [M] [T] [W] ....
30422  * @constructor
30423  * Creates a new Day Picker
30424  * @param {Object} config Configuration options
30425  */
30426 Roo.form.DayPicker= function(config){
30427     Roo.form.DayPicker.superclass.constructor.call(this, config);
30428      
30429 };
30430
30431 Roo.extend(Roo.form.DayPicker, Roo.form.Field,  {
30432     /**
30433      * @cfg {String} focusClass The CSS class to use when the checkbox receives focus (defaults to undefined)
30434      */
30435     focusClass : undefined,
30436     /**
30437      * @cfg {String} fieldClass The default CSS class for the checkbox (defaults to "x-form-field")
30438      */
30439     fieldClass: "x-form-field",
30440    
30441     /**
30442      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
30443      * {tag: "input", type: "checkbox", autocomplete: "off"})
30444      */
30445     defaultAutoCreate : { tag: "input", type: 'hidden', autocomplete: "off"},
30446     
30447    
30448     actionMode : 'viewEl', 
30449     //
30450     // private
30451  
30452     inputType : 'hidden',
30453     
30454      
30455     inputElement: false, // real input element?
30456     basedOn: false, // ????
30457     
30458     isFormField: true, // not sure where this is needed!!!!
30459
30460     onResize : function(){
30461         Roo.form.Checkbox.superclass.onResize.apply(this, arguments);
30462         if(!this.boxLabel){
30463             this.el.alignTo(this.wrap, 'c-c');
30464         }
30465     },
30466
30467     initEvents : function(){
30468         Roo.form.Checkbox.superclass.initEvents.call(this);
30469         this.el.on("click", this.onClick,  this);
30470         this.el.on("change", this.onClick,  this);
30471     },
30472
30473
30474     getResizeEl : function(){
30475         return this.wrap;
30476     },
30477
30478     getPositionEl : function(){
30479         return this.wrap;
30480     },
30481
30482     
30483     // private
30484     onRender : function(ct, position){
30485         Roo.form.Checkbox.superclass.onRender.call(this, ct, position);
30486        
30487         this.wrap = this.el.wrap({cls: 'x-form-daypick-item '});
30488         
30489         var r1 = '<table><tr>';
30490         var r2 = '<tr class="x-form-daypick-icons">';
30491         for (var i=0; i < 7; i++) {
30492             r1+= '<td><div>' + Date.dayNames[i].substring(0,3) + '</div></td>';
30493             r2+= '<td><img class="x-menu-item-icon" src="' + Roo.BLANK_IMAGE_URL  +'"></td>';
30494         }
30495         
30496         var viewEl = this.wrap.createChild( r1 + '</tr>' + r2 + '</tr></table>');
30497         viewEl.select('img').on('click', this.onClick, this);
30498         this.viewEl = viewEl;   
30499         
30500         
30501         // this will not work on Chrome!!!
30502         this.el.on('DOMAttrModified', this.setFromHidden,  this); //ff
30503         this.el.on('propertychange', this.setFromHidden,  this);  //ie
30504         
30505         
30506           
30507
30508     },
30509
30510     // private
30511     initValue : Roo.emptyFn,
30512
30513     /**
30514      * Returns the checked state of the checkbox.
30515      * @return {Boolean} True if checked, else false
30516      */
30517     getValue : function(){
30518         return this.el.dom.value;
30519         
30520     },
30521
30522         // private
30523     onClick : function(e){ 
30524         //this.setChecked(!this.checked);
30525         Roo.get(e.target).toggleClass('x-menu-item-checked');
30526         this.refreshValue();
30527         //if(this.el.dom.checked != this.checked){
30528         //    this.setValue(this.el.dom.checked);
30529        // }
30530     },
30531     
30532     // private
30533     refreshValue : function()
30534     {
30535         var val = '';
30536         this.viewEl.select('img',true).each(function(e,i,n)  {
30537             val += e.is(".x-menu-item-checked") ? String(n) : '';
30538         });
30539         this.setValue(val, true);
30540     },
30541
30542     /**
30543      * Sets the checked state of the checkbox.
30544      * On is always based on a string comparison between inputValue and the param.
30545      * @param {Boolean/String} value - the value to set 
30546      * @param {Boolean/String} suppressEvent - whether to suppress the checkchange event.
30547      */
30548     setValue : function(v,suppressEvent){
30549         if (!this.el.dom) {
30550             return;
30551         }
30552         var old = this.el.dom.value ;
30553         this.el.dom.value = v;
30554         if (suppressEvent) {
30555             return ;
30556         }
30557          
30558         // update display..
30559         this.viewEl.select('img',true).each(function(e,i,n)  {
30560             
30561             var on = e.is(".x-menu-item-checked");
30562             var newv = v.indexOf(String(n)) > -1;
30563             if (on != newv) {
30564                 e.toggleClass('x-menu-item-checked');
30565             }
30566             
30567         });
30568         
30569         
30570         this.fireEvent('change', this, v, old);
30571         
30572         
30573     },
30574    
30575     // handle setting of hidden value by some other method!!?!?
30576     setFromHidden: function()
30577     {
30578         if(!this.el){
30579             return;
30580         }
30581         //console.log("SET FROM HIDDEN");
30582         //alert('setFrom hidden');
30583         this.setValue(this.el.dom.value);
30584     },
30585     
30586     onDestroy : function()
30587     {
30588         if(this.viewEl){
30589             Roo.get(this.viewEl).remove();
30590         }
30591          
30592         Roo.form.DayPicker.superclass.onDestroy.call(this);
30593     }
30594
30595 });/*
30596  * RooJS Library 1.1.1
30597  * Copyright(c) 2008-2011  Alan Knowles
30598  *
30599  * License - LGPL
30600  */
30601  
30602
30603 /**
30604  * @class Roo.form.ComboCheck
30605  * @extends Roo.form.ComboBox
30606  * A combobox for multiple select items.
30607  *
30608  * FIXME - could do with a reset button..
30609  * 
30610  * @constructor
30611  * Create a new ComboCheck
30612  * @param {Object} config Configuration options
30613  */
30614 Roo.form.ComboCheck = function(config){
30615     Roo.form.ComboCheck.superclass.constructor.call(this, config);
30616     // should verify some data...
30617     // like
30618     // hiddenName = required..
30619     // displayField = required
30620     // valudField == required
30621     var req= [ 'hiddenName', 'displayField', 'valueField' ];
30622     var _t = this;
30623     Roo.each(req, function(e) {
30624         if ((typeof(_t[e]) == 'undefined' ) || !_t[e].length) {
30625             throw "Roo.form.ComboCheck : missing value for: " + e;
30626         }
30627     });
30628     
30629     
30630 };
30631
30632 Roo.extend(Roo.form.ComboCheck, Roo.form.ComboBox, {
30633      
30634      
30635     editable : false,
30636      
30637     selectedClass: 'x-menu-item-checked', 
30638     
30639     // private
30640     onRender : function(ct, position){
30641         var _t = this;
30642         
30643         
30644         
30645         if(!this.tpl){
30646             var cls = 'x-combo-list';
30647
30648             
30649             this.tpl =  new Roo.Template({
30650                 html :  '<div class="'+cls+'-item x-menu-check-item">' +
30651                    '<img class="x-menu-item-icon" style="margin: 0px;" src="' + Roo.BLANK_IMAGE_URL + '">' + 
30652                    '<span>{' + this.displayField + '}</span>' +
30653                     '</div>' 
30654                 
30655             });
30656         }
30657  
30658         
30659         Roo.form.ComboCheck.superclass.onRender.call(this, ct, position);
30660         this.view.singleSelect = false;
30661         this.view.multiSelect = true;
30662         this.view.toggleSelect = true;
30663         this.pageTb.add(new Roo.Toolbar.Fill(), {
30664             
30665             text: 'Done',
30666             handler: function()
30667             {
30668                 _t.collapse();
30669             }
30670         });
30671     },
30672     
30673     onViewOver : function(e, t){
30674         // do nothing...
30675         return;
30676         
30677     },
30678     
30679     onViewClick : function(doFocus,index){
30680         return;
30681         
30682     },
30683     select: function () {
30684         //Roo.log("SELECT CALLED");
30685     },
30686      
30687     selectByValue : function(xv, scrollIntoView){
30688         var ar = this.getValueArray();
30689         var sels = [];
30690         
30691         Roo.each(ar, function(v) {
30692             if(v === undefined || v === null){
30693                 return;
30694             }
30695             var r = this.findRecord(this.valueField, v);
30696             if(r){
30697                 sels.push(this.store.indexOf(r))
30698                 
30699             }
30700         },this);
30701         this.view.select(sels);
30702         return false;
30703     },
30704     
30705     
30706     
30707     onSelect : function(record, index){
30708        // Roo.log("onselect Called");
30709        // this is only called by the clear button now..
30710         this.view.clearSelections();
30711         this.setValue('[]');
30712         if (this.value != this.valueBefore) {
30713             this.fireEvent('change', this, this.value, this.valueBefore);
30714             this.valueBefore = this.value;
30715         }
30716     },
30717     getValueArray : function()
30718     {
30719         var ar = [] ;
30720         
30721         try {
30722             //Roo.log(this.value);
30723             if (typeof(this.value) == 'undefined') {
30724                 return [];
30725             }
30726             var ar = Roo.decode(this.value);
30727             return  ar instanceof Array ? ar : []; //?? valid?
30728             
30729         } catch(e) {
30730             Roo.log(e + "\nRoo.form.ComboCheck:getValueArray  invalid data:" + this.getValue());
30731             return [];
30732         }
30733          
30734     },
30735     expand : function ()
30736     {
30737         
30738         Roo.form.ComboCheck.superclass.expand.call(this);
30739         this.valueBefore = typeof(this.value) == 'undefined' ? '' : this.value;
30740         //this.valueBefore = typeof(this.valueBefore) == 'undefined' ? '' : this.valueBefore;
30741         
30742
30743     },
30744     
30745     collapse : function(){
30746         Roo.form.ComboCheck.superclass.collapse.call(this);
30747         var sl = this.view.getSelectedIndexes();
30748         var st = this.store;
30749         var nv = [];
30750         var tv = [];
30751         var r;
30752         Roo.each(sl, function(i) {
30753             r = st.getAt(i);
30754             nv.push(r.get(this.valueField));
30755         },this);
30756         this.setValue(Roo.encode(nv));
30757         if (this.value != this.valueBefore) {
30758
30759             this.fireEvent('change', this, this.value, this.valueBefore);
30760             this.valueBefore = this.value;
30761         }
30762         
30763     },
30764     
30765     setValue : function(v){
30766         // Roo.log(v);
30767         this.value = v;
30768         
30769         var vals = this.getValueArray();
30770         var tv = [];
30771         Roo.each(vals, function(k) {
30772             var r = this.findRecord(this.valueField, k);
30773             if(r){
30774                 tv.push(r.data[this.displayField]);
30775             }else if(this.valueNotFoundText !== undefined){
30776                 tv.push( this.valueNotFoundText );
30777             }
30778         },this);
30779        // Roo.log(tv);
30780         
30781         Roo.form.ComboBox.superclass.setValue.call(this, tv.join(', '));
30782         this.hiddenField.value = v;
30783         this.value = v;
30784     }
30785     
30786 });/*
30787  * Based on:
30788  * Ext JS Library 1.1.1
30789  * Copyright(c) 2006-2007, Ext JS, LLC.
30790  *
30791  * Originally Released Under LGPL - original licence link has changed is not relivant.
30792  *
30793  * Fork - LGPL
30794  * <script type="text/javascript">
30795  */
30796  
30797 /**
30798  * @class Roo.form.Signature
30799  * @extends Roo.form.Field
30800  * Signature field.  
30801  * @constructor
30802  * 
30803  * @param {Object} config Configuration options
30804  */
30805
30806 Roo.form.Signature = function(config){
30807     Roo.form.Signature.superclass.constructor.call(this, config);
30808     
30809     this.addEvents({// not in used??
30810          /**
30811          * @event confirm
30812          * Fires when the 'confirm' icon is pressed (add a listener to enable add button)
30813              * @param {Roo.form.Signature} combo This combo box
30814              */
30815         'confirm' : true,
30816         /**
30817          * @event reset
30818          * Fires when the 'edit' icon is pressed (add a listener to enable add button)
30819              * @param {Roo.form.ComboBox} combo This combo box
30820              * @param {Roo.data.Record|false} record The data record returned from the underlying store (or false on nothing selected)
30821              */
30822         'reset' : true
30823     });
30824 };
30825
30826 Roo.extend(Roo.form.Signature, Roo.form.Field,  {
30827     /**
30828      * @cfg {Object} labels Label to use when rendering a form.
30829      * defaults to 
30830      * labels : { 
30831      *      clear : "Clear",
30832      *      confirm : "Confirm"
30833      *  }
30834      */
30835     labels : { 
30836         clear : "Clear",
30837         confirm : "Confirm"
30838     },
30839     /**
30840      * @cfg {Number} width The signature panel width (defaults to 300)
30841      */
30842     width: 300,
30843     /**
30844      * @cfg {Number} height The signature panel height (defaults to 100)
30845      */
30846     height : 100,
30847     /**
30848      * @cfg {Boolean} allowBlank False to validate that the value length > 0 (defaults to false)
30849      */
30850     allowBlank : false,
30851     
30852     //private
30853     // {Object} signPanel The signature SVG panel element (defaults to {})
30854     signPanel : {},
30855     // {Boolean} isMouseDown False to validate that the mouse down event (defaults to false)
30856     isMouseDown : false,
30857     // {Boolean} isConfirmed validate the signature is confirmed or not for submitting form (defaults to false)
30858     isConfirmed : false,
30859     // {String} signatureTmp SVG mapping string (defaults to empty string)
30860     signatureTmp : '',
30861     
30862     
30863     defaultAutoCreate : { // modified by initCompnoent..
30864         tag: "input",
30865         type:"hidden"
30866     },
30867
30868     // private
30869     onRender : function(ct, position){
30870         
30871         Roo.form.Signature.superclass.onRender.call(this, ct, position);
30872         
30873         this.wrap = this.el.wrap({
30874             cls:'x-form-signature-wrap', style : 'width: ' + this.width + 'px', cn:{cls:'x-form-signature'}
30875         });
30876         
30877         this.createToolbar(this);
30878         this.signPanel = this.wrap.createChild({
30879                 tag: 'div',
30880                 style: 'width: ' + this.width + 'px; height: ' + this.height + 'px; border: 0;'
30881             }, this.el
30882         );
30883             
30884         this.svgID = Roo.id();
30885         this.svgEl = this.signPanel.createChild({
30886               xmlns : 'http://www.w3.org/2000/svg',
30887               tag : 'svg',
30888               id : this.svgID + "-svg",
30889               width: this.width,
30890               height: this.height,
30891               viewBox: '0 0 '+this.width+' '+this.height,
30892               cn : [
30893                 {
30894                     tag: "rect",
30895                     id: this.svgID + "-svg-r",
30896                     width: this.width,
30897                     height: this.height,
30898                     fill: "#ffa"
30899                 },
30900                 {
30901                     tag: "line",
30902                     id: this.svgID + "-svg-l",
30903                     x1: "0", // start
30904                     y1: (this.height*0.8), // start set the line in 80% of height
30905                     x2: this.width, // end
30906                     y2: (this.height*0.8), // end set the line in 80% of height
30907                     'stroke': "#666",
30908                     'stroke-width': "1",
30909                     'stroke-dasharray': "3",
30910                     'shape-rendering': "crispEdges",
30911                     'pointer-events': "none"
30912                 },
30913                 {
30914                     tag: "path",
30915                     id: this.svgID + "-svg-p",
30916                     'stroke': "navy",
30917                     'stroke-width': "3",
30918                     'fill': "none",
30919                     'pointer-events': 'none'
30920                 }
30921               ]
30922         });
30923         this.createSVG();
30924         this.svgBox = this.svgEl.dom.getScreenCTM();
30925     },
30926     createSVG : function(){ 
30927         var svg = this.signPanel;
30928         var r = svg.select('#'+ this.svgID + '-svg-r', true).first().dom;
30929         var t = this;
30930
30931         r.addEventListener('mousedown', function(e) { return t.down(e); }, false);
30932         r.addEventListener('mousemove', function(e) { return t.move(e); }, false);
30933         r.addEventListener('mouseup', function(e) { return t.up(e); }, false);
30934         r.addEventListener('mouseout', function(e) { return t.up(e); }, false);
30935         r.addEventListener('touchstart', function(e) { return t.down(e); }, false);
30936         r.addEventListener('touchmove', function(e) { return t.move(e); }, false);
30937         r.addEventListener('touchend', function(e) { return t.up(e); }, false);
30938         
30939     },
30940     isTouchEvent : function(e){
30941         return e.type.match(/^touch/);
30942     },
30943     getCoords : function (e) {
30944         var pt    = this.svgEl.dom.createSVGPoint();
30945         pt.x = e.clientX; 
30946         pt.y = e.clientY;
30947         if (this.isTouchEvent(e)) {
30948             pt.x =  e.targetTouches[0].clientX 
30949             pt.y = e.targetTouches[0].clientY;
30950         }
30951         var a = this.svgEl.dom.getScreenCTM();
30952         var b = a.inverse();
30953         var mx = pt.matrixTransform(b);
30954         return mx.x + ',' + mx.y;
30955     },
30956     //mouse event headler 
30957     down : function (e) {
30958         this.signatureTmp += 'M' + this.getCoords(e) + ' ';
30959         this.signPanel.select('#'+ this.svgID + '-svg-p', true).first().attr('d', this.signatureTmp);
30960         
30961         this.isMouseDown = true;
30962         
30963         e.preventDefault();
30964     },
30965     move : function (e) {
30966         if (this.isMouseDown) {
30967             this.signatureTmp += 'L' + this.getCoords(e) + ' ';
30968             this.signPanel.select('#'+ this.svgID + '-svg-p', true).first().attr( 'd', this.signatureTmp);
30969         }
30970         
30971         e.preventDefault();
30972     },
30973     up : function (e) {
30974         this.isMouseDown = false;
30975         var sp = this.signatureTmp.split(' ');
30976         
30977         if(sp.length > 1){
30978             if(!sp[sp.length-2].match(/^L/)){
30979                 sp.pop();
30980                 sp.pop();
30981                 sp.push("");
30982                 this.signatureTmp = sp.join(" ");
30983             }
30984         }
30985         if(this.getValue() != this.signatureTmp){
30986             this.signPanel.select('#'+ this.svgID + '-svg-r', true).first().attr('fill', '#ffa');
30987             this.isConfirmed = false;
30988         }
30989         e.preventDefault();
30990     },
30991     
30992     /**
30993      * Protected method that will not generally be called directly. It
30994      * is called when the editor creates its toolbar. Override this method if you need to
30995      * add custom toolbar buttons.
30996      * @param {HtmlEditor} editor
30997      */
30998     createToolbar : function(editor){
30999          function btn(id, toggle, handler){
31000             var xid = fid + '-'+ id ;
31001             return {
31002                 id : xid,
31003                 cmd : id,
31004                 cls : 'x-btn-icon x-edit-'+id,
31005                 enableToggle:toggle !== false,
31006                 scope: editor, // was editor...
31007                 handler:handler||editor.relayBtnCmd,
31008                 clickEvent:'mousedown',
31009                 tooltip: etb.buttonTips[id] || undefined, ///tips ???
31010                 tabIndex:-1
31011             };
31012         }
31013         
31014         
31015         var tb = new Roo.Toolbar(editor.wrap.dom.firstChild);
31016         this.tb = tb;
31017         this.tb.add(
31018            {
31019                 cls : ' x-signature-btn x-signature-'+id,
31020                 scope: editor, // was editor...
31021                 handler: this.reset,
31022                 clickEvent:'mousedown',
31023                 text: this.labels.clear
31024             },
31025             {
31026                  xtype : 'Fill',
31027                  xns: Roo.Toolbar
31028             }, 
31029             {
31030                 cls : '  x-signature-btn x-signature-'+id,
31031                 scope: editor, // was editor...
31032                 handler: this.confirmHandler,
31033                 clickEvent:'mousedown',
31034                 text: this.labels.confirm
31035             }
31036         );
31037     
31038     },
31039     //public
31040     /**
31041      * when user is clicked confirm then show this image.....
31042      * 
31043      * @return {String} Image Data URI
31044      */
31045     getImageDataURI : function(){
31046         var svg = this.svgEl.dom.parentNode.innerHTML;
31047         var src = 'data:image/svg+xml;base64,'+window.btoa(svg);
31048         return src; 
31049     },
31050     /**
31051      * 
31052      * @return {Boolean} this.isConfirmed
31053      */
31054     getConfirmed : function(){
31055         return this.isConfirmed;
31056     },
31057     /**
31058      * 
31059      * @return {Number} this.width
31060      */
31061     getWidth : function(){
31062         return this.width;
31063     },
31064     /**
31065      * 
31066      * @return {Number} this.height
31067      */
31068     getHeight : function(){
31069         return this.height;
31070     },
31071     // private
31072     getSignature : function(){
31073         return this.signatureTmp;
31074     },
31075     // private
31076     reset : function(){
31077         this.signatureTmp = '';
31078         this.signPanel.select('#'+ this.svgID + '-svg-r', true).first().attr('fill', '#ffa');
31079         this.signPanel.select('#'+ this.svgID + '-svg-p', true).first().attr( 'd', '');
31080         this.isConfirmed = false;
31081         Roo.form.Signature.superclass.reset.call(this);
31082     },
31083     setSignature : function(s){
31084         this.signatureTmp = s;
31085         this.signPanel.select('#'+ this.svgID + '-svg-r', true).first().attr('fill', '#ffa');
31086         this.signPanel.select('#'+ this.svgID + '-svg-p', true).first().attr( 'd', s);
31087         this.setValue(s);
31088         this.isConfirmed = false;
31089         Roo.form.Signature.superclass.reset.call(this);
31090     }, 
31091     test : function(){
31092 //        Roo.log(this.signPanel.dom.contentWindow.up())
31093     },
31094     //private
31095     setConfirmed : function(){
31096         
31097         
31098         
31099 //        Roo.log(Roo.get(this.signPanel.dom.contentWindow.r).attr('fill', '#cfc'));
31100     },
31101     // private
31102     confirmHandler : function(){
31103         if(!this.getSignature()){
31104             return;
31105         }
31106         
31107         this.signPanel.select('#'+ this.svgID + '-svg-r', true).first().attr('fill', '#cfc');
31108         this.setValue(this.getSignature());
31109         this.isConfirmed = true;
31110         
31111         this.fireEvent('confirm', this);
31112     },
31113     // private
31114     // Subclasses should provide the validation implementation by overriding this
31115     validateValue : function(value){
31116         if(this.allowBlank){
31117             return true;
31118         }
31119         
31120         if(this.isConfirmed){
31121             return true;
31122         }
31123         return false;
31124     }
31125 });/*
31126  * Based on:
31127  * Ext JS Library 1.1.1
31128  * Copyright(c) 2006-2007, Ext JS, LLC.
31129  *
31130  * Originally Released Under LGPL - original licence link has changed is not relivant.
31131  *
31132  * Fork - LGPL
31133  * <script type="text/javascript">
31134  */
31135  
31136
31137 /**
31138  * @class Roo.form.ComboBox
31139  * @extends Roo.form.TriggerField
31140  * A combobox control with support for autocomplete, remote-loading, paging and many other features.
31141  * @constructor
31142  * Create a new ComboBox.
31143  * @param {Object} config Configuration options
31144  */
31145 Roo.form.Select = function(config){
31146     Roo.form.Select.superclass.constructor.call(this, config);
31147      
31148 };
31149
31150 Roo.extend(Roo.form.Select , Roo.form.ComboBox, {
31151     /**
31152      * @cfg {String/HTMLElement/Element} transform The id, DOM node or element of an existing select to convert to a ComboBox
31153      */
31154     /**
31155      * @cfg {Boolean} lazyRender True to prevent the ComboBox from rendering until requested (should always be used when
31156      * rendering into an Roo.Editor, defaults to false)
31157      */
31158     /**
31159      * @cfg {Boolean/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to:
31160      * {tag: "input", type: "text", size: "24", autocomplete: "off"})
31161      */
31162     /**
31163      * @cfg {Roo.data.Store} store The data store to which this combo is bound (defaults to undefined)
31164      */
31165     /**
31166      * @cfg {String} title If supplied, a header element is created containing this text and added into the top of
31167      * the dropdown list (defaults to undefined, with no header element)
31168      */
31169
31170      /**
31171      * @cfg {String/Roo.Template} tpl The template to use to render the output
31172      */
31173      
31174     // private
31175     defaultAutoCreate : {tag: "select"  },
31176     /**
31177      * @cfg {Number} listWidth The width in pixels of the dropdown list (defaults to the width of the ComboBox field)
31178      */
31179     listWidth: undefined,
31180     /**
31181      * @cfg {String} displayField The underlying data field name to bind to this CombBox (defaults to undefined if
31182      * mode = 'remote' or 'text' if mode = 'local')
31183      */
31184     displayField: undefined,
31185     /**
31186      * @cfg {String} valueField The underlying data value name to bind to this CombBox (defaults to undefined if
31187      * mode = 'remote' or 'value' if mode = 'local'). 
31188      * Note: use of a valueField requires the user make a selection
31189      * in order for a value to be mapped.
31190      */
31191     valueField: undefined,
31192     
31193     
31194     /**
31195      * @cfg {String} hiddenName If specified, a hidden form field with this name is dynamically generated to store the
31196      * field's data value (defaults to the underlying DOM element's name)
31197      */
31198     hiddenName: undefined,
31199     /**
31200      * @cfg {String} listClass CSS class to apply to the dropdown list element (defaults to '')
31201      */
31202     listClass: '',
31203     /**
31204      * @cfg {String} selectedClass CSS class to apply to the selected item in the dropdown list (defaults to 'x-combo-selected')
31205      */
31206     selectedClass: 'x-combo-selected',
31207     /**
31208      * @cfg {String} triggerClass An additional CSS class used to style the trigger button.  The trigger will always get the
31209      * class 'x-form-trigger' and triggerClass will be <b>appended</b> if specified (defaults to 'x-form-arrow-trigger'
31210      * which displays a downward arrow icon).
31211      */
31212     triggerClass : 'x-form-arrow-trigger',
31213     /**
31214      * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
31215      */
31216     shadow:'sides',
31217     /**
31218      * @cfg {String} listAlign A valid anchor position value. See {@link Roo.Element#alignTo} for details on supported
31219      * anchor positions (defaults to 'tl-bl')
31220      */
31221     listAlign: 'tl-bl?',
31222     /**
31223      * @cfg {Number} maxHeight The maximum height in pixels of the dropdown list before scrollbars are shown (defaults to 300)
31224      */
31225     maxHeight: 300,
31226     /**
31227      * @cfg {String} triggerAction The action to execute when the trigger field is activated.  Use 'all' to run the
31228      * query specified by the allQuery config option (defaults to 'query')
31229      */
31230     triggerAction: 'query',
31231     /**
31232      * @cfg {Number} minChars The minimum number of characters the user must type before autocomplete and typeahead activate
31233      * (defaults to 4, does not apply if editable = false)
31234      */
31235     minChars : 4,
31236     /**
31237      * @cfg {Boolean} typeAhead True to populate and autoselect the remainder of the text being typed after a configurable
31238      * delay (typeAheadDelay) if it matches a known value (defaults to false)
31239      */
31240     typeAhead: false,
31241     /**
31242      * @cfg {Number} queryDelay The length of time in milliseconds to delay between the start of typing and sending the
31243      * query to filter the dropdown list (defaults to 500 if mode = 'remote' or 10 if mode = 'local')
31244      */
31245     queryDelay: 500,
31246     /**
31247      * @cfg {Number} pageSize If greater than 0, a paging toolbar is displayed in the footer of the dropdown list and the
31248      * filter queries will execute with page start and limit parameters.  Only applies when mode = 'remote' (defaults to 0)
31249      */
31250     pageSize: 0,
31251     /**
31252      * @cfg {Boolean} selectOnFocus True to select any existing text in the field immediately on focus.  Only applies
31253      * when editable = true (defaults to false)
31254      */
31255     selectOnFocus:false,
31256     /**
31257      * @cfg {String} queryParam Name of the query as it will be passed on the querystring (defaults to 'query')
31258      */
31259     queryParam: 'query',
31260     /**
31261      * @cfg {String} loadingText The text to display in the dropdown list while data is loading.  Only applies
31262      * when mode = 'remote' (defaults to 'Loading...')
31263      */
31264     loadingText: 'Loading...',
31265     /**
31266      * @cfg {Boolean} resizable True to add a resize handle to the bottom of the dropdown list (defaults to false)
31267      */
31268     resizable: false,
31269     /**
31270      * @cfg {Number} handleHeight The height in pixels of the dropdown list resize handle if resizable = true (defaults to 8)
31271      */
31272     handleHeight : 8,
31273     /**
31274      * @cfg {Boolean} editable False to prevent the user from typing text directly into the field, just like a
31275      * traditional select (defaults to true)
31276      */
31277     editable: true,
31278     /**
31279      * @cfg {String} allQuery The text query to send to the server to return all records for the list with no filtering (defaults to '')
31280      */
31281     allQuery: '',
31282     /**
31283      * @cfg {String} mode Set to 'local' if the ComboBox loads local data (defaults to 'remote' which loads from the server)
31284      */
31285     mode: 'remote',
31286     /**
31287      * @cfg {Number} minListWidth The minimum width of the dropdown list in pixels (defaults to 70, will be ignored if
31288      * listWidth has a higher value)
31289      */
31290     minListWidth : 70,
31291     /**
31292      * @cfg {Boolean} forceSelection True to restrict the selected value to one of the values in the list, false to
31293      * allow the user to set arbitrary text into the field (defaults to false)
31294      */
31295     forceSelection:false,
31296     /**
31297      * @cfg {Number} typeAheadDelay The length of time in milliseconds to wait until the typeahead text is displayed
31298      * if typeAhead = true (defaults to 250)
31299      */
31300     typeAheadDelay : 250,
31301     /**
31302      * @cfg {String} valueNotFoundText When using a name/value combo, if the value passed to setValue is not found in
31303      * the store, valueNotFoundText will be displayed as the field text if defined (defaults to undefined)
31304      */
31305     valueNotFoundText : undefined,
31306     
31307     /**
31308      * @cfg {String} defaultValue The value displayed after loading the store.
31309      */
31310     defaultValue: '',
31311     
31312     /**
31313      * @cfg {Boolean} blockFocus Prevents all focus calls, so it can work with things like HTML edtor bar
31314      */
31315     blockFocus : false,
31316     
31317     /**
31318      * @cfg {Boolean} disableClear Disable showing of clear button.
31319      */
31320     disableClear : false,
31321     /**
31322      * @cfg {Boolean} alwaysQuery  Disable caching of results, and always send query
31323      */
31324     alwaysQuery : false,
31325     
31326     //private
31327     addicon : false,
31328     editicon: false,
31329     
31330     // element that contains real text value.. (when hidden is used..)
31331      
31332     // private
31333     onRender : function(ct, position){
31334         Roo.form.Field.prototype.onRender.call(this, ct, position);
31335         
31336         if(this.store){
31337             this.store.on('beforeload', this.onBeforeLoad, this);
31338             this.store.on('load', this.onLoad, this);
31339             this.store.on('loadexception', this.onLoadException, this);
31340             this.store.load({});
31341         }
31342         
31343         
31344         
31345     },
31346
31347     // private
31348     initEvents : function(){
31349         //Roo.form.ComboBox.superclass.initEvents.call(this);
31350  
31351     },
31352
31353     onDestroy : function(){
31354        
31355         if(this.store){
31356             this.store.un('beforeload', this.onBeforeLoad, this);
31357             this.store.un('load', this.onLoad, this);
31358             this.store.un('loadexception', this.onLoadException, this);
31359         }
31360         //Roo.form.ComboBox.superclass.onDestroy.call(this);
31361     },
31362
31363     // private
31364     fireKey : function(e){
31365         if(e.isNavKeyPress() && !this.list.isVisible()){
31366             this.fireEvent("specialkey", this, e);
31367         }
31368     },
31369
31370     // private
31371     onResize: function(w, h){
31372         
31373         return; 
31374     
31375         
31376     },
31377
31378     /**
31379      * Allow or prevent the user from directly editing the field text.  If false is passed,
31380      * the user will only be able to select from the items defined in the dropdown list.  This method
31381      * is the runtime equivalent of setting the 'editable' config option at config time.
31382      * @param {Boolean} value True to allow the user to directly edit the field text
31383      */
31384     setEditable : function(value){
31385          
31386     },
31387
31388     // private
31389     onBeforeLoad : function(){
31390         
31391         Roo.log("Select before load");
31392         return;
31393     
31394         this.innerList.update(this.loadingText ?
31395                '<div class="loading-indicator">'+this.loadingText+'</div>' : '');
31396         //this.restrictHeight();
31397         this.selectedIndex = -1;
31398     },
31399
31400     // private
31401     onLoad : function(){
31402
31403     
31404         var dom = this.el.dom;
31405         dom.innerHTML = '';
31406          var od = dom.ownerDocument;
31407          
31408         if (this.emptyText) {
31409             var op = od.createElement('option');
31410             op.setAttribute('value', '');
31411             op.innerHTML = String.format('{0}', this.emptyText);
31412             dom.appendChild(op);
31413         }
31414         if(this.store.getCount() > 0){
31415            
31416             var vf = this.valueField;
31417             var df = this.displayField;
31418             this.store.data.each(function(r) {
31419                 // which colmsn to use... testing - cdoe / title..
31420                 var op = od.createElement('option');
31421                 op.setAttribute('value', r.data[vf]);
31422                 op.innerHTML = String.format('{0}', r.data[df]);
31423                 dom.appendChild(op);
31424             });
31425             if (typeof(this.defaultValue != 'undefined')) {
31426                 this.setValue(this.defaultValue);
31427             }
31428             
31429              
31430         }else{
31431             //this.onEmptyResults();
31432         }
31433         //this.el.focus();
31434     },
31435     // private
31436     onLoadException : function()
31437     {
31438         dom.innerHTML = '';
31439             
31440         Roo.log("Select on load exception");
31441         return;
31442     
31443         this.collapse();
31444         Roo.log(this.store.reader.jsonData);
31445         if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
31446             Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
31447         }
31448         
31449         
31450     },
31451     // private
31452     onTypeAhead : function(){
31453          
31454     },
31455
31456     // private
31457     onSelect : function(record, index){
31458         Roo.log('on select?');
31459         return;
31460         if(this.fireEvent('beforeselect', this, record, index) !== false){
31461             this.setFromData(index > -1 ? record.data : false);
31462             this.collapse();
31463             this.fireEvent('select', this, record, index);
31464         }
31465     },
31466
31467     /**
31468      * Returns the currently selected field value or empty string if no value is set.
31469      * @return {String} value The selected value
31470      */
31471     getValue : function(){
31472         var dom = this.el.dom;
31473         this.value = dom.options[dom.selectedIndex].value;
31474         return this.value;
31475         
31476     },
31477
31478     /**
31479      * Clears any text/value currently set in the field
31480      */
31481     clearValue : function(){
31482         this.value = '';
31483         this.el.dom.selectedIndex = this.emptyText ? 0 : -1;
31484         
31485     },
31486
31487     /**
31488      * Sets the specified value into the field.  If the value finds a match, the corresponding record text
31489      * will be displayed in the field.  If the value does not match the data value of an existing item,
31490      * and the valueNotFoundText config option is defined, it will be displayed as the default field text.
31491      * Otherwise the field will be blank (although the value will still be set).
31492      * @param {String} value The value to match
31493      */
31494     setValue : function(v){
31495         var d = this.el.dom;
31496         for (var i =0; i < d.options.length;i++) {
31497             if (v == d.options[i].value) {
31498                 d.selectedIndex = i;
31499                 this.value = v;
31500                 return;
31501             }
31502         }
31503         this.clearValue();
31504     },
31505     /**
31506      * @property {Object} the last set data for the element
31507      */
31508     
31509     lastData : false,
31510     /**
31511      * Sets the value of the field based on a object which is related to the record format for the store.
31512      * @param {Object} value the value to set as. or false on reset?
31513      */
31514     setFromData : function(o){
31515         Roo.log('setfrom data?');
31516          
31517         
31518         
31519     },
31520     // private
31521     reset : function(){
31522         this.clearValue();
31523     },
31524     // private
31525     findRecord : function(prop, value){
31526         
31527         return false;
31528     
31529         var record;
31530         if(this.store.getCount() > 0){
31531             this.store.each(function(r){
31532                 if(r.data[prop] == value){
31533                     record = r;
31534                     return false;
31535                 }
31536                 return true;
31537             });
31538         }
31539         return record;
31540     },
31541     
31542     getName: function()
31543     {
31544         // returns hidden if it's set..
31545         if (!this.rendered) {return ''};
31546         return !this.hiddenName && this.el.dom.name  ? this.el.dom.name : (this.hiddenName || '');
31547         
31548     },
31549      
31550
31551     
31552
31553     // private
31554     onEmptyResults : function(){
31555         Roo.log('empty results');
31556         //this.collapse();
31557     },
31558
31559     /**
31560      * Returns true if the dropdown list is expanded, else false.
31561      */
31562     isExpanded : function(){
31563         return false;
31564     },
31565
31566     /**
31567      * Select an item in the dropdown list by its data value. This function does NOT cause the select event to fire.
31568      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
31569      * @param {String} value The data value of the item to select
31570      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
31571      * selected item if it is not currently in view (defaults to true)
31572      * @return {Boolean} True if the value matched an item in the list, else false
31573      */
31574     selectByValue : function(v, scrollIntoView){
31575         Roo.log('select By Value');
31576         return false;
31577     
31578         if(v !== undefined && v !== null){
31579             var r = this.findRecord(this.valueField || this.displayField, v);
31580             if(r){
31581                 this.select(this.store.indexOf(r), scrollIntoView);
31582                 return true;
31583             }
31584         }
31585         return false;
31586     },
31587
31588     /**
31589      * Select an item in the dropdown list by its numeric index in the list. This function does NOT cause the select event to fire.
31590      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
31591      * @param {Number} index The zero-based index of the list item to select
31592      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
31593      * selected item if it is not currently in view (defaults to true)
31594      */
31595     select : function(index, scrollIntoView){
31596         Roo.log('select ');
31597         return  ;
31598         
31599         this.selectedIndex = index;
31600         this.view.select(index);
31601         if(scrollIntoView !== false){
31602             var el = this.view.getNode(index);
31603             if(el){
31604                 this.innerList.scrollChildIntoView(el, false);
31605             }
31606         }
31607     },
31608
31609       
31610
31611     // private
31612     validateBlur : function(){
31613         
31614         return;
31615         
31616     },
31617
31618     // private
31619     initQuery : function(){
31620         this.doQuery(this.getRawValue());
31621     },
31622
31623     // private
31624     doForce : function(){
31625         if(this.el.dom.value.length > 0){
31626             this.el.dom.value =
31627                 this.lastSelectionText === undefined ? '' : this.lastSelectionText;
31628              
31629         }
31630     },
31631
31632     /**
31633      * Execute a query to filter the dropdown list.  Fires the beforequery event prior to performing the
31634      * query allowing the query action to be canceled if needed.
31635      * @param {String} query The SQL query to execute
31636      * @param {Boolean} forceAll True to force the query to execute even if there are currently fewer characters
31637      * in the field than the minimum specified by the minChars config option.  It also clears any filter previously
31638      * saved in the current store (defaults to false)
31639      */
31640     doQuery : function(q, forceAll){
31641         
31642         Roo.log('doQuery?');
31643         if(q === undefined || q === null){
31644             q = '';
31645         }
31646         var qe = {
31647             query: q,
31648             forceAll: forceAll,
31649             combo: this,
31650             cancel:false
31651         };
31652         if(this.fireEvent('beforequery', qe)===false || qe.cancel){
31653             return false;
31654         }
31655         q = qe.query;
31656         forceAll = qe.forceAll;
31657         if(forceAll === true || (q.length >= this.minChars)){
31658             if(this.lastQuery != q || this.alwaysQuery){
31659                 this.lastQuery = q;
31660                 if(this.mode == 'local'){
31661                     this.selectedIndex = -1;
31662                     if(forceAll){
31663                         this.store.clearFilter();
31664                     }else{
31665                         this.store.filter(this.displayField, q);
31666                     }
31667                     this.onLoad();
31668                 }else{
31669                     this.store.baseParams[this.queryParam] = q;
31670                     this.store.load({
31671                         params: this.getParams(q)
31672                     });
31673                     this.expand();
31674                 }
31675             }else{
31676                 this.selectedIndex = -1;
31677                 this.onLoad();   
31678             }
31679         }
31680     },
31681
31682     // private
31683     getParams : function(q){
31684         var p = {};
31685         //p[this.queryParam] = q;
31686         if(this.pageSize){
31687             p.start = 0;
31688             p.limit = this.pageSize;
31689         }
31690         return p;
31691     },
31692
31693     /**
31694      * Hides the dropdown list if it is currently expanded. Fires the 'collapse' event on completion.
31695      */
31696     collapse : function(){
31697         
31698     },
31699
31700     // private
31701     collapseIf : function(e){
31702         
31703     },
31704
31705     /**
31706      * Expands the dropdown list if it is currently hidden. Fires the 'expand' event on completion.
31707      */
31708     expand : function(){
31709         
31710     } ,
31711
31712     // private
31713      
31714
31715     /** 
31716     * @cfg {Boolean} grow 
31717     * @hide 
31718     */
31719     /** 
31720     * @cfg {Number} growMin 
31721     * @hide 
31722     */
31723     /** 
31724     * @cfg {Number} growMax 
31725     * @hide 
31726     */
31727     /**
31728      * @hide
31729      * @method autoSize
31730      */
31731     
31732     setWidth : function()
31733     {
31734         
31735     },
31736     getResizeEl : function(){
31737         return this.el;
31738     }
31739 });//<script type="text/javasscript">
31740  
31741
31742 /**
31743  * @class Roo.DDView
31744  * A DnD enabled version of Roo.View.
31745  * @param {Element/String} container The Element in which to create the View.
31746  * @param {String} tpl The template string used to create the markup for each element of the View
31747  * @param {Object} config The configuration properties. These include all the config options of
31748  * {@link Roo.View} plus some specific to this class.<br>
31749  * <p>
31750  * Drag/drop is implemented by adding {@link Roo.data.Record}s to the target DDView. If copying is
31751  * not being performed, the original {@link Roo.data.Record} is removed from the source DDView.<br>
31752  * <p>
31753  * The following extra CSS rules are needed to provide insertion point highlighting:<pre><code>
31754 .x-view-drag-insert-above {
31755         border-top:1px dotted #3366cc;
31756 }
31757 .x-view-drag-insert-below {
31758         border-bottom:1px dotted #3366cc;
31759 }
31760 </code></pre>
31761  * 
31762  */
31763  
31764 Roo.DDView = function(container, tpl, config) {
31765     Roo.DDView.superclass.constructor.apply(this, arguments);
31766     this.getEl().setStyle("outline", "0px none");
31767     this.getEl().unselectable();
31768     if (this.dragGroup) {
31769                 this.setDraggable(this.dragGroup.split(","));
31770     }
31771     if (this.dropGroup) {
31772                 this.setDroppable(this.dropGroup.split(","));
31773     }
31774     if (this.deletable) {
31775         this.setDeletable();
31776     }
31777     this.isDirtyFlag = false;
31778         this.addEvents({
31779                 "drop" : true
31780         });
31781 };
31782
31783 Roo.extend(Roo.DDView, Roo.View, {
31784 /**     @cfg {String/Array} dragGroup The ddgroup name(s) for the View's DragZone. */
31785 /**     @cfg {String/Array} dropGroup The ddgroup name(s) for the View's DropZone. */
31786 /**     @cfg {Boolean} copy Causes drag operations to copy nodes rather than move. */
31787 /**     @cfg {Boolean} allowCopy Causes ctrl/drag operations to copy nodes rather than move. */
31788
31789         isFormField: true,
31790
31791         reset: Roo.emptyFn,
31792         
31793         clearInvalid: Roo.form.Field.prototype.clearInvalid,
31794
31795         validate: function() {
31796                 return true;
31797         },
31798         
31799         destroy: function() {
31800                 this.purgeListeners();
31801                 this.getEl.removeAllListeners();
31802                 this.getEl().remove();
31803                 if (this.dragZone) {
31804                         if (this.dragZone.destroy) {
31805                                 this.dragZone.destroy();
31806                         }
31807                 }
31808                 if (this.dropZone) {
31809                         if (this.dropZone.destroy) {
31810                                 this.dropZone.destroy();
31811                         }
31812                 }
31813         },
31814
31815 /**     Allows this class to be an Roo.form.Field so it can be found using {@link Roo.form.BasicForm#findField}. */
31816         getName: function() {
31817                 return this.name;
31818         },
31819
31820 /**     Loads the View from a JSON string representing the Records to put into the Store. */
31821         setValue: function(v) {
31822                 if (!this.store) {
31823                         throw "DDView.setValue(). DDView must be constructed with a valid Store";
31824                 }
31825                 var data = {};
31826                 data[this.store.reader.meta.root] = v ? [].concat(v) : [];
31827                 this.store.proxy = new Roo.data.MemoryProxy(data);
31828                 this.store.load();
31829         },
31830
31831 /**     @return {String} a parenthesised list of the ids of the Records in the View. */
31832         getValue: function() {
31833                 var result = '(';
31834                 this.store.each(function(rec) {
31835                         result += rec.id + ',';
31836                 });
31837                 return result.substr(0, result.length - 1) + ')';
31838         },
31839         
31840         getIds: function() {
31841                 var i = 0, result = new Array(this.store.getCount());
31842                 this.store.each(function(rec) {
31843                         result[i++] = rec.id;
31844                 });
31845                 return result;
31846         },
31847         
31848         isDirty: function() {
31849                 return this.isDirtyFlag;
31850         },
31851
31852 /**
31853  *      Part of the Roo.dd.DropZone interface. If no target node is found, the
31854  *      whole Element becomes the target, and this causes the drop gesture to append.
31855  */
31856     getTargetFromEvent : function(e) {
31857                 var target = e.getTarget();
31858                 while ((target !== null) && (target.parentNode != this.el.dom)) {
31859                 target = target.parentNode;
31860                 }
31861                 if (!target) {
31862                         target = this.el.dom.lastChild || this.el.dom;
31863                 }
31864                 return target;
31865     },
31866
31867 /**
31868  *      Create the drag data which consists of an object which has the property "ddel" as
31869  *      the drag proxy element. 
31870  */
31871     getDragData : function(e) {
31872         var target = this.findItemFromChild(e.getTarget());
31873                 if(target) {
31874                         this.handleSelection(e);
31875                         var selNodes = this.getSelectedNodes();
31876             var dragData = {
31877                 source: this,
31878                 copy: this.copy || (this.allowCopy && e.ctrlKey),
31879                 nodes: selNodes,
31880                 records: []
31881                         };
31882                         var selectedIndices = this.getSelectedIndexes();
31883                         for (var i = 0; i < selectedIndices.length; i++) {
31884                                 dragData.records.push(this.store.getAt(selectedIndices[i]));
31885                         }
31886                         if (selNodes.length == 1) {
31887                                 dragData.ddel = target.cloneNode(true); // the div element
31888                         } else {
31889                                 var div = document.createElement('div'); // create the multi element drag "ghost"
31890                                 div.className = 'multi-proxy';
31891                                 for (var i = 0, len = selNodes.length; i < len; i++) {
31892                                         div.appendChild(selNodes[i].cloneNode(true));
31893                                 }
31894                                 dragData.ddel = div;
31895                         }
31896             //console.log(dragData)
31897             //console.log(dragData.ddel.innerHTML)
31898                         return dragData;
31899                 }
31900         //console.log('nodragData')
31901                 return false;
31902     },
31903     
31904 /**     Specify to which ddGroup items in this DDView may be dragged. */
31905     setDraggable: function(ddGroup) {
31906         if (ddGroup instanceof Array) {
31907                 Roo.each(ddGroup, this.setDraggable, this);
31908                 return;
31909         }
31910         if (this.dragZone) {
31911                 this.dragZone.addToGroup(ddGroup);
31912         } else {
31913                         this.dragZone = new Roo.dd.DragZone(this.getEl(), {
31914                                 containerScroll: true,
31915                                 ddGroup: ddGroup 
31916
31917                         });
31918 //                      Draggability implies selection. DragZone's mousedown selects the element.
31919                         if (!this.multiSelect) { this.singleSelect = true; }
31920
31921 //                      Wire the DragZone's handlers up to methods in *this*
31922                         this.dragZone.getDragData = this.getDragData.createDelegate(this);
31923                 }
31924     },
31925
31926 /**     Specify from which ddGroup this DDView accepts drops. */
31927     setDroppable: function(ddGroup) {
31928         if (ddGroup instanceof Array) {
31929                 Roo.each(ddGroup, this.setDroppable, this);
31930                 return;
31931         }
31932         if (this.dropZone) {
31933                 this.dropZone.addToGroup(ddGroup);
31934         } else {
31935                         this.dropZone = new Roo.dd.DropZone(this.getEl(), {
31936                                 containerScroll: true,
31937                                 ddGroup: ddGroup
31938                         });
31939
31940 //                      Wire the DropZone's handlers up to methods in *this*
31941                         this.dropZone.getTargetFromEvent = this.getTargetFromEvent.createDelegate(this);
31942                         this.dropZone.onNodeEnter = this.onNodeEnter.createDelegate(this);
31943                         this.dropZone.onNodeOver = this.onNodeOver.createDelegate(this);
31944                         this.dropZone.onNodeOut = this.onNodeOut.createDelegate(this);
31945                         this.dropZone.onNodeDrop = this.onNodeDrop.createDelegate(this);
31946                 }
31947     },
31948
31949 /**     Decide whether to drop above or below a View node. */
31950     getDropPoint : function(e, n, dd){
31951         if (n == this.el.dom) { return "above"; }
31952                 var t = Roo.lib.Dom.getY(n), b = t + n.offsetHeight;
31953                 var c = t + (b - t) / 2;
31954                 var y = Roo.lib.Event.getPageY(e);
31955                 if(y <= c) {
31956                         return "above";
31957                 }else{
31958                         return "below";
31959                 }
31960     },
31961
31962     onNodeEnter : function(n, dd, e, data){
31963                 return false;
31964     },
31965     
31966     onNodeOver : function(n, dd, e, data){
31967                 var pt = this.getDropPoint(e, n, dd);
31968                 // set the insert point style on the target node
31969                 var dragElClass = this.dropNotAllowed;
31970                 if (pt) {
31971                         var targetElClass;
31972                         if (pt == "above"){
31973                                 dragElClass = n.previousSibling ? "x-tree-drop-ok-between" : "x-tree-drop-ok-above";
31974                                 targetElClass = "x-view-drag-insert-above";
31975                         } else {
31976                                 dragElClass = n.nextSibling ? "x-tree-drop-ok-between" : "x-tree-drop-ok-below";
31977                                 targetElClass = "x-view-drag-insert-below";
31978                         }
31979                         if (this.lastInsertClass != targetElClass){
31980                                 Roo.fly(n).replaceClass(this.lastInsertClass, targetElClass);
31981                                 this.lastInsertClass = targetElClass;
31982                         }
31983                 }
31984                 return dragElClass;
31985         },
31986
31987     onNodeOut : function(n, dd, e, data){
31988                 this.removeDropIndicators(n);
31989     },
31990
31991     onNodeDrop : function(n, dd, e, data){
31992         if (this.fireEvent("drop", this, n, dd, e, data) === false) {
31993                 return false;
31994         }
31995         var pt = this.getDropPoint(e, n, dd);
31996                 var insertAt = (n == this.el.dom) ? this.nodes.length : n.nodeIndex;
31997                 if (pt == "below") { insertAt++; }
31998                 for (var i = 0; i < data.records.length; i++) {
31999                         var r = data.records[i];
32000                         var dup = this.store.getById(r.id);
32001                         if (dup && (dd != this.dragZone)) {
32002                                 Roo.fly(this.getNode(this.store.indexOf(dup))).frame("red", 1);
32003                         } else {
32004                                 if (data.copy) {
32005                                         this.store.insert(insertAt++, r.copy());
32006                                 } else {
32007                                         data.source.isDirtyFlag = true;
32008                                         r.store.remove(r);
32009                                         this.store.insert(insertAt++, r);
32010                                 }
32011                                 this.isDirtyFlag = true;
32012                         }
32013                 }
32014                 this.dragZone.cachedTarget = null;
32015                 return true;
32016     },
32017
32018     removeDropIndicators : function(n){
32019                 if(n){
32020                         Roo.fly(n).removeClass([
32021                                 "x-view-drag-insert-above",
32022                                 "x-view-drag-insert-below"]);
32023                         this.lastInsertClass = "_noclass";
32024                 }
32025     },
32026
32027 /**
32028  *      Utility method. Add a delete option to the DDView's context menu.
32029  *      @param {String} imageUrl The URL of the "delete" icon image.
32030  */
32031         setDeletable: function(imageUrl) {
32032                 if (!this.singleSelect && !this.multiSelect) {
32033                         this.singleSelect = true;
32034                 }
32035                 var c = this.getContextMenu();
32036                 this.contextMenu.on("itemclick", function(item) {
32037                         switch (item.id) {
32038                                 case "delete":
32039                                         this.remove(this.getSelectedIndexes());
32040                                         break;
32041                         }
32042                 }, this);
32043                 this.contextMenu.add({
32044                         icon: imageUrl,
32045                         id: "delete",
32046                         text: 'Delete'
32047                 });
32048         },
32049         
32050 /**     Return the context menu for this DDView. */
32051         getContextMenu: function() {
32052                 if (!this.contextMenu) {
32053 //                      Create the View's context menu
32054                         this.contextMenu = new Roo.menu.Menu({
32055                                 id: this.id + "-contextmenu"
32056                         });
32057                         this.el.on("contextmenu", this.showContextMenu, this);
32058                 }
32059                 return this.contextMenu;
32060         },
32061         
32062         disableContextMenu: function() {
32063                 if (this.contextMenu) {
32064                         this.el.un("contextmenu", this.showContextMenu, this);
32065                 }
32066         },
32067
32068         showContextMenu: function(e, item) {
32069         item = this.findItemFromChild(e.getTarget());
32070                 if (item) {
32071                         e.stopEvent();
32072                         this.select(this.getNode(item), this.multiSelect && e.ctrlKey, true);
32073                         this.contextMenu.showAt(e.getXY());
32074             }
32075     },
32076
32077 /**
32078  *      Remove {@link Roo.data.Record}s at the specified indices.
32079  *      @param {Array/Number} selectedIndices The index (or Array of indices) of Records to remove.
32080  */
32081     remove: function(selectedIndices) {
32082                 selectedIndices = [].concat(selectedIndices);
32083                 for (var i = 0; i < selectedIndices.length; i++) {
32084                         var rec = this.store.getAt(selectedIndices[i]);
32085                         this.store.remove(rec);
32086                 }
32087     },
32088
32089 /**
32090  *      Double click fires the event, but also, if this is draggable, and there is only one other
32091  *      related DropZone, it transfers the selected node.
32092  */
32093     onDblClick : function(e){
32094         var item = this.findItemFromChild(e.getTarget());
32095         if(item){
32096             if (this.fireEvent("dblclick", this, this.indexOf(item), item, e) === false) {
32097                 return false;
32098             }
32099             if (this.dragGroup) {
32100                     var targets = Roo.dd.DragDropMgr.getRelated(this.dragZone, true);
32101                     while (targets.indexOf(this.dropZone) > -1) {
32102                             targets.remove(this.dropZone);
32103                                 }
32104                     if (targets.length == 1) {
32105                                         this.dragZone.cachedTarget = null;
32106                         var el = Roo.get(targets[0].getEl());
32107                         var box = el.getBox(true);
32108                         targets[0].onNodeDrop(el.dom, {
32109                                 target: el.dom,
32110                                 xy: [box.x, box.y + box.height - 1]
32111                         }, null, this.getDragData(e));
32112                     }
32113                 }
32114         }
32115     },
32116     
32117     handleSelection: function(e) {
32118                 this.dragZone.cachedTarget = null;
32119         var item = this.findItemFromChild(e.getTarget());
32120         if (!item) {
32121                 this.clearSelections(true);
32122                 return;
32123         }
32124                 if (item && (this.multiSelect || this.singleSelect)){
32125                         if(this.multiSelect && e.shiftKey && (!e.ctrlKey) && this.lastSelection){
32126                                 this.select(this.getNodes(this.indexOf(this.lastSelection), item.nodeIndex), false);
32127                         }else if (this.isSelected(this.getNode(item)) && e.ctrlKey){
32128                                 this.unselect(item);
32129                         } else {
32130                                 this.select(item, this.multiSelect && e.ctrlKey);
32131                                 this.lastSelection = item;
32132                         }
32133                 }
32134     },
32135
32136     onItemClick : function(item, index, e){
32137                 if(this.fireEvent("beforeclick", this, index, item, e) === false){
32138                         return false;
32139                 }
32140                 return true;
32141     },
32142
32143     unselect : function(nodeInfo, suppressEvent){
32144                 var node = this.getNode(nodeInfo);
32145                 if(node && this.isSelected(node)){
32146                         if(this.fireEvent("beforeselect", this, node, this.selections) !== false){
32147                                 Roo.fly(node).removeClass(this.selectedClass);
32148                                 this.selections.remove(node);
32149                                 if(!suppressEvent){
32150                                         this.fireEvent("selectionchange", this, this.selections);
32151                                 }
32152                         }
32153                 }
32154     }
32155 });
32156 /*
32157  * Based on:
32158  * Ext JS Library 1.1.1
32159  * Copyright(c) 2006-2007, Ext JS, LLC.
32160  *
32161  * Originally Released Under LGPL - original licence link has changed is not relivant.
32162  *
32163  * Fork - LGPL
32164  * <script type="text/javascript">
32165  */
32166  
32167 /**
32168  * @class Roo.LayoutManager
32169  * @extends Roo.util.Observable
32170  * Base class for layout managers.
32171  */
32172 Roo.LayoutManager = function(container, config){
32173     Roo.LayoutManager.superclass.constructor.call(this);
32174     this.el = Roo.get(container);
32175     // ie scrollbar fix
32176     if(this.el.dom == document.body && Roo.isIE && !config.allowScroll){
32177         document.body.scroll = "no";
32178     }else if(this.el.dom != document.body && this.el.getStyle('position') == 'static'){
32179         this.el.position('relative');
32180     }
32181     this.id = this.el.id;
32182     this.el.addClass("x-layout-container");
32183     /** false to disable window resize monitoring @type Boolean */
32184     this.monitorWindowResize = true;
32185     this.regions = {};
32186     this.addEvents({
32187         /**
32188          * @event layout
32189          * Fires when a layout is performed. 
32190          * @param {Roo.LayoutManager} this
32191          */
32192         "layout" : true,
32193         /**
32194          * @event regionresized
32195          * Fires when the user resizes a region. 
32196          * @param {Roo.LayoutRegion} region The resized region
32197          * @param {Number} newSize The new size (width for east/west, height for north/south)
32198          */
32199         "regionresized" : true,
32200         /**
32201          * @event regioncollapsed
32202          * Fires when a region is collapsed. 
32203          * @param {Roo.LayoutRegion} region The collapsed region
32204          */
32205         "regioncollapsed" : true,
32206         /**
32207          * @event regionexpanded
32208          * Fires when a region is expanded.  
32209          * @param {Roo.LayoutRegion} region The expanded region
32210          */
32211         "regionexpanded" : true
32212     });
32213     this.updating = false;
32214     Roo.EventManager.onWindowResize(this.onWindowResize, this, true);
32215 };
32216
32217 Roo.extend(Roo.LayoutManager, Roo.util.Observable, {
32218     /**
32219      * Returns true if this layout is currently being updated
32220      * @return {Boolean}
32221      */
32222     isUpdating : function(){
32223         return this.updating; 
32224     },
32225     
32226     /**
32227      * Suspend the LayoutManager from doing auto-layouts while
32228      * making multiple add or remove calls
32229      */
32230     beginUpdate : function(){
32231         this.updating = true;    
32232     },
32233     
32234     /**
32235      * Restore auto-layouts and optionally disable the manager from performing a layout
32236      * @param {Boolean} noLayout true to disable a layout update 
32237      */
32238     endUpdate : function(noLayout){
32239         this.updating = false;
32240         if(!noLayout){
32241             this.layout();
32242         }    
32243     },
32244     
32245     layout: function(){
32246         
32247     },
32248     
32249     onRegionResized : function(region, newSize){
32250         this.fireEvent("regionresized", region, newSize);
32251         this.layout();
32252     },
32253     
32254     onRegionCollapsed : function(region){
32255         this.fireEvent("regioncollapsed", region);
32256     },
32257     
32258     onRegionExpanded : function(region){
32259         this.fireEvent("regionexpanded", region);
32260     },
32261         
32262     /**
32263      * Returns the size of the current view. This method normalizes document.body and element embedded layouts and
32264      * performs box-model adjustments.
32265      * @return {Object} The size as an object {width: (the width), height: (the height)}
32266      */
32267     getViewSize : function(){
32268         var size;
32269         if(this.el.dom != document.body){
32270             size = this.el.getSize();
32271         }else{
32272             size = {width: Roo.lib.Dom.getViewWidth(), height: Roo.lib.Dom.getViewHeight()};
32273         }
32274         size.width -= this.el.getBorderWidth("lr")-this.el.getPadding("lr");
32275         size.height -= this.el.getBorderWidth("tb")-this.el.getPadding("tb");
32276         return size;
32277     },
32278     
32279     /**
32280      * Returns the Element this layout is bound to.
32281      * @return {Roo.Element}
32282      */
32283     getEl : function(){
32284         return this.el;
32285     },
32286     
32287     /**
32288      * Returns the specified region.
32289      * @param {String} target The region key ('center', 'north', 'south', 'east' or 'west')
32290      * @return {Roo.LayoutRegion}
32291      */
32292     getRegion : function(target){
32293         return this.regions[target.toLowerCase()];
32294     },
32295     
32296     onWindowResize : function(){
32297         if(this.monitorWindowResize){
32298             this.layout();
32299         }
32300     }
32301 });/*
32302  * Based on:
32303  * Ext JS Library 1.1.1
32304  * Copyright(c) 2006-2007, Ext JS, LLC.
32305  *
32306  * Originally Released Under LGPL - original licence link has changed is not relivant.
32307  *
32308  * Fork - LGPL
32309  * <script type="text/javascript">
32310  */
32311 /**
32312  * @class Roo.BorderLayout
32313  * @extends Roo.LayoutManager
32314  * This class represents a common layout manager used in desktop applications. For screenshots and more details,
32315  * please see: <br><br>
32316  * <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>
32317  * <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>
32318  * Example:
32319  <pre><code>
32320  var layout = new Roo.BorderLayout(document.body, {
32321     north: {
32322         initialSize: 25,
32323         titlebar: false
32324     },
32325     west: {
32326         split:true,
32327         initialSize: 200,
32328         minSize: 175,
32329         maxSize: 400,
32330         titlebar: true,
32331         collapsible: true
32332     },
32333     east: {
32334         split:true,
32335         initialSize: 202,
32336         minSize: 175,
32337         maxSize: 400,
32338         titlebar: true,
32339         collapsible: true
32340     },
32341     south: {
32342         split:true,
32343         initialSize: 100,
32344         minSize: 100,
32345         maxSize: 200,
32346         titlebar: true,
32347         collapsible: true
32348     },
32349     center: {
32350         titlebar: true,
32351         autoScroll:true,
32352         resizeTabs: true,
32353         minTabWidth: 50,
32354         preferredTabWidth: 150
32355     }
32356 });
32357
32358 // shorthand
32359 var CP = Roo.ContentPanel;
32360
32361 layout.beginUpdate();
32362 layout.add("north", new CP("north", "North"));
32363 layout.add("south", new CP("south", {title: "South", closable: true}));
32364 layout.add("west", new CP("west", {title: "West"}));
32365 layout.add("east", new CP("autoTabs", {title: "Auto Tabs", closable: true}));
32366 layout.add("center", new CP("center1", {title: "Close Me", closable: true}));
32367 layout.add("center", new CP("center2", {title: "Center Panel", closable: false}));
32368 layout.getRegion("center").showPanel("center1");
32369 layout.endUpdate();
32370 </code></pre>
32371
32372 <b>The container the layout is rendered into can be either the body element or any other element.
32373 If it is not the body element, the container needs to either be an absolute positioned element,
32374 or you will need to add "position:relative" to the css of the container.  You will also need to specify
32375 the container size if it is not the body element.</b>
32376
32377 * @constructor
32378 * Create a new BorderLayout
32379 * @param {String/HTMLElement/Element} container The container this layout is bound to
32380 * @param {Object} config Configuration options
32381  */
32382 Roo.BorderLayout = function(container, config){
32383     config = config || {};
32384     Roo.BorderLayout.superclass.constructor.call(this, container, config);
32385     this.factory = config.factory || Roo.BorderLayout.RegionFactory;
32386     for(var i = 0, len = this.factory.validRegions.length; i < len; i++) {
32387         var target = this.factory.validRegions[i];
32388         if(config[target]){
32389             this.addRegion(target, config[target]);
32390         }
32391     }
32392 };
32393
32394 Roo.extend(Roo.BorderLayout, Roo.LayoutManager, {
32395     /**
32396      * Creates and adds a new region if it doesn't already exist.
32397      * @param {String} target The target region key (north, south, east, west or center).
32398      * @param {Object} config The regions config object
32399      * @return {BorderLayoutRegion} The new region
32400      */
32401     addRegion : function(target, config){
32402         if(!this.regions[target]){
32403             var r = this.factory.create(target, this, config);
32404             this.bindRegion(target, r);
32405         }
32406         return this.regions[target];
32407     },
32408
32409     // private (kinda)
32410     bindRegion : function(name, r){
32411         this.regions[name] = r;
32412         r.on("visibilitychange", this.layout, this);
32413         r.on("paneladded", this.layout, this);
32414         r.on("panelremoved", this.layout, this);
32415         r.on("invalidated", this.layout, this);
32416         r.on("resized", this.onRegionResized, this);
32417         r.on("collapsed", this.onRegionCollapsed, this);
32418         r.on("expanded", this.onRegionExpanded, this);
32419     },
32420
32421     /**
32422      * Performs a layout update.
32423      */
32424     layout : function(){
32425         if(this.updating) return;
32426         var size = this.getViewSize();
32427         var w = size.width;
32428         var h = size.height;
32429         var centerW = w;
32430         var centerH = h;
32431         var centerY = 0;
32432         var centerX = 0;
32433         //var x = 0, y = 0;
32434
32435         var rs = this.regions;
32436         var north = rs["north"];
32437         var south = rs["south"]; 
32438         var west = rs["west"];
32439         var east = rs["east"];
32440         var center = rs["center"];
32441         //if(this.hideOnLayout){ // not supported anymore
32442             //c.el.setStyle("display", "none");
32443         //}
32444         if(north && north.isVisible()){
32445             var b = north.getBox();
32446             var m = north.getMargins();
32447             b.width = w - (m.left+m.right);
32448             b.x = m.left;
32449             b.y = m.top;
32450             centerY = b.height + b.y + m.bottom;
32451             centerH -= centerY;
32452             north.updateBox(this.safeBox(b));
32453         }
32454         if(south && south.isVisible()){
32455             var b = south.getBox();
32456             var m = south.getMargins();
32457             b.width = w - (m.left+m.right);
32458             b.x = m.left;
32459             var totalHeight = (b.height + m.top + m.bottom);
32460             b.y = h - totalHeight + m.top;
32461             centerH -= totalHeight;
32462             south.updateBox(this.safeBox(b));
32463         }
32464         if(west && west.isVisible()){
32465             var b = west.getBox();
32466             var m = west.getMargins();
32467             b.height = centerH - (m.top+m.bottom);
32468             b.x = m.left;
32469             b.y = centerY + m.top;
32470             var totalWidth = (b.width + m.left + m.right);
32471             centerX += totalWidth;
32472             centerW -= totalWidth;
32473             west.updateBox(this.safeBox(b));
32474         }
32475         if(east && east.isVisible()){
32476             var b = east.getBox();
32477             var m = east.getMargins();
32478             b.height = centerH - (m.top+m.bottom);
32479             var totalWidth = (b.width + m.left + m.right);
32480             b.x = w - totalWidth + m.left;
32481             b.y = centerY + m.top;
32482             centerW -= totalWidth;
32483             east.updateBox(this.safeBox(b));
32484         }
32485         if(center){
32486             var m = center.getMargins();
32487             var centerBox = {
32488                 x: centerX + m.left,
32489                 y: centerY + m.top,
32490                 width: centerW - (m.left+m.right),
32491                 height: centerH - (m.top+m.bottom)
32492             };
32493             //if(this.hideOnLayout){
32494                 //center.el.setStyle("display", "block");
32495             //}
32496             center.updateBox(this.safeBox(centerBox));
32497         }
32498         this.el.repaint();
32499         this.fireEvent("layout", this);
32500     },
32501
32502     // private
32503     safeBox : function(box){
32504         box.width = Math.max(0, box.width);
32505         box.height = Math.max(0, box.height);
32506         return box;
32507     },
32508
32509     /**
32510      * Adds a ContentPanel (or subclass) to this layout.
32511      * @param {String} target The target region key (north, south, east, west or center).
32512      * @param {Roo.ContentPanel} panel The panel to add
32513      * @return {Roo.ContentPanel} The added panel
32514      */
32515     add : function(target, panel){
32516          
32517         target = target.toLowerCase();
32518         return this.regions[target].add(panel);
32519     },
32520
32521     /**
32522      * Remove a ContentPanel (or subclass) to this layout.
32523      * @param {String} target The target region key (north, south, east, west or center).
32524      * @param {Number/String/Roo.ContentPanel} panel The index, id or panel to remove
32525      * @return {Roo.ContentPanel} The removed panel
32526      */
32527     remove : function(target, panel){
32528         target = target.toLowerCase();
32529         return this.regions[target].remove(panel);
32530     },
32531
32532     /**
32533      * Searches all regions for a panel with the specified id
32534      * @param {String} panelId
32535      * @return {Roo.ContentPanel} The panel or null if it wasn't found
32536      */
32537     findPanel : function(panelId){
32538         var rs = this.regions;
32539         for(var target in rs){
32540             if(typeof rs[target] != "function"){
32541                 var p = rs[target].getPanel(panelId);
32542                 if(p){
32543                     return p;
32544                 }
32545             }
32546         }
32547         return null;
32548     },
32549
32550     /**
32551      * Searches all regions for a panel with the specified id and activates (shows) it.
32552      * @param {String/ContentPanel} panelId The panels id or the panel itself
32553      * @return {Roo.ContentPanel} The shown panel or null
32554      */
32555     showPanel : function(panelId) {
32556       var rs = this.regions;
32557       for(var target in rs){
32558          var r = rs[target];
32559          if(typeof r != "function"){
32560             if(r.hasPanel(panelId)){
32561                return r.showPanel(panelId);
32562             }
32563          }
32564       }
32565       return null;
32566    },
32567
32568    /**
32569      * Restores this layout's state using Roo.state.Manager or the state provided by the passed provider.
32570      * @param {Roo.state.Provider} provider (optional) An alternate state provider
32571      */
32572     restoreState : function(provider){
32573         if(!provider){
32574             provider = Roo.state.Manager;
32575         }
32576         var sm = new Roo.LayoutStateManager();
32577         sm.init(this, provider);
32578     },
32579
32580     /**
32581      * Adds a batch of multiple ContentPanels dynamically by passing a special regions config object.  This config
32582      * object should contain properties for each region to add ContentPanels to, and each property's value should be
32583      * a valid ContentPanel config object.  Example:
32584      * <pre><code>
32585 // Create the main layout
32586 var layout = new Roo.BorderLayout('main-ct', {
32587     west: {
32588         split:true,
32589         minSize: 175,
32590         titlebar: true
32591     },
32592     center: {
32593         title:'Components'
32594     }
32595 }, 'main-ct');
32596
32597 // Create and add multiple ContentPanels at once via configs
32598 layout.batchAdd({
32599    west: {
32600        id: 'source-files',
32601        autoCreate:true,
32602        title:'Ext Source Files',
32603        autoScroll:true,
32604        fitToFrame:true
32605    },
32606    center : {
32607        el: cview,
32608        autoScroll:true,
32609        fitToFrame:true,
32610        toolbar: tb,
32611        resizeEl:'cbody'
32612    }
32613 });
32614 </code></pre>
32615      * @param {Object} regions An object containing ContentPanel configs by region name
32616      */
32617     batchAdd : function(regions){
32618         this.beginUpdate();
32619         for(var rname in regions){
32620             var lr = this.regions[rname];
32621             if(lr){
32622                 this.addTypedPanels(lr, regions[rname]);
32623             }
32624         }
32625         this.endUpdate();
32626     },
32627
32628     // private
32629     addTypedPanels : function(lr, ps){
32630         if(typeof ps == 'string'){
32631             lr.add(new Roo.ContentPanel(ps));
32632         }
32633         else if(ps instanceof Array){
32634             for(var i =0, len = ps.length; i < len; i++){
32635                 this.addTypedPanels(lr, ps[i]);
32636             }
32637         }
32638         else if(!ps.events){ // raw config?
32639             var el = ps.el;
32640             delete ps.el; // prevent conflict
32641             lr.add(new Roo.ContentPanel(el || Roo.id(), ps));
32642         }
32643         else {  // panel object assumed!
32644             lr.add(ps);
32645         }
32646     },
32647     /**
32648      * Adds a xtype elements to the layout.
32649      * <pre><code>
32650
32651 layout.addxtype({
32652        xtype : 'ContentPanel',
32653        region: 'west',
32654        items: [ .... ]
32655    }
32656 );
32657
32658 layout.addxtype({
32659         xtype : 'NestedLayoutPanel',
32660         region: 'west',
32661         layout: {
32662            center: { },
32663            west: { }   
32664         },
32665         items : [ ... list of content panels or nested layout panels.. ]
32666    }
32667 );
32668 </code></pre>
32669      * @param {Object} cfg Xtype definition of item to add.
32670      */
32671     addxtype : function(cfg)
32672     {
32673         // basically accepts a pannel...
32674         // can accept a layout region..!?!?
32675         //Roo.log('Roo.BorderLayout add ' + cfg.xtype)
32676         
32677         if (!cfg.xtype.match(/Panel$/)) {
32678             return false;
32679         }
32680         var ret = false;
32681         
32682         if (typeof(cfg.region) == 'undefined') {
32683             Roo.log("Failed to add Panel, region was not set");
32684             Roo.log(cfg);
32685             return false;
32686         }
32687         var region = cfg.region;
32688         delete cfg.region;
32689         
32690           
32691         var xitems = [];
32692         if (cfg.items) {
32693             xitems = cfg.items;
32694             delete cfg.items;
32695         }
32696         var nb = false;
32697         
32698         switch(cfg.xtype) 
32699         {
32700             case 'ContentPanel':  // ContentPanel (el, cfg)
32701             case 'ScrollPanel':  // ContentPanel (el, cfg)
32702             case 'ViewPanel': 
32703                 if(cfg.autoCreate) {
32704                     ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
32705                 } else {
32706                     var el = this.el.createChild();
32707                     ret = new Roo[cfg.xtype](el, cfg); // new panel!!!!!
32708                 }
32709                 
32710                 this.add(region, ret);
32711                 break;
32712             
32713             
32714             case 'TreePanel': // our new panel!
32715                 cfg.el = this.el.createChild();
32716                 ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
32717                 this.add(region, ret);
32718                 break;
32719             
32720             case 'NestedLayoutPanel': 
32721                 // create a new Layout (which is  a Border Layout...
32722                 var el = this.el.createChild();
32723                 var clayout = cfg.layout;
32724                 delete cfg.layout;
32725                 clayout.items   = clayout.items  || [];
32726                 // replace this exitems with the clayout ones..
32727                 xitems = clayout.items;
32728                  
32729                 
32730                 if (region == 'center' && this.active && this.getRegion('center').panels.length < 1) {
32731                     cfg.background = false;
32732                 }
32733                 var layout = new Roo.BorderLayout(el, clayout);
32734                 
32735                 ret = new Roo[cfg.xtype](layout, cfg); // new panel!!!!!
32736                 //console.log('adding nested layout panel '  + cfg.toSource());
32737                 this.add(region, ret);
32738                 nb = {}; /// find first...
32739                 break;
32740                 
32741             case 'GridPanel': 
32742             
32743                 // needs grid and region
32744                 
32745                 //var el = this.getRegion(region).el.createChild();
32746                 var el = this.el.createChild();
32747                 // create the grid first...
32748                 
32749                 var grid = new Roo.grid[cfg.grid.xtype](el, cfg.grid);
32750                 delete cfg.grid;
32751                 if (region == 'center' && this.active ) {
32752                     cfg.background = false;
32753                 }
32754                 ret = new Roo[cfg.xtype](grid, cfg); // new panel!!!!!
32755                 
32756                 this.add(region, ret);
32757                 if (cfg.background) {
32758                     ret.on('activate', function(gp) {
32759                         if (!gp.grid.rendered) {
32760                             gp.grid.render();
32761                         }
32762                     });
32763                 } else {
32764                     grid.render();
32765                 }
32766                 break;
32767            
32768            
32769            
32770                 
32771                 
32772                 
32773             default: 
32774                 alert("Can not add '" + cfg.xtype + "' to BorderLayout");
32775                 return null;
32776              // GridPanel (grid, cfg)
32777             
32778         }
32779         this.beginUpdate();
32780         // add children..
32781         var region = '';
32782         var abn = {};
32783         Roo.each(xitems, function(i)  {
32784             region = nb && i.region ? i.region : false;
32785             
32786             var add = ret.addxtype(i);
32787            
32788             if (region) {
32789                 nb[region] = nb[region] == undefined ? 0 : nb[region]+1;
32790                 if (!i.background) {
32791                     abn[region] = nb[region] ;
32792                 }
32793             }
32794             
32795         });
32796         this.endUpdate();
32797
32798         // make the last non-background panel active..
32799         //if (nb) { Roo.log(abn); }
32800         if (nb) {
32801             
32802             for(var r in abn) {
32803                 region = this.getRegion(r);
32804                 if (region) {
32805                     // tried using nb[r], but it does not work..
32806                      
32807                     region.showPanel(abn[r]);
32808                    
32809                 }
32810             }
32811         }
32812         return ret;
32813         
32814     }
32815 });
32816
32817 /**
32818  * Shortcut for creating a new BorderLayout object and adding one or more ContentPanels to it in a single step, handling
32819  * the beginUpdate and endUpdate calls internally.  The key to this method is the <b>panels</b> property that can be
32820  * provided with each region config, which allows you to add ContentPanel configs in addition to the region configs
32821  * during creation.  The following code is equivalent to the constructor-based example at the beginning of this class:
32822  * <pre><code>
32823 // shorthand
32824 var CP = Roo.ContentPanel;
32825
32826 var layout = Roo.BorderLayout.create({
32827     north: {
32828         initialSize: 25,
32829         titlebar: false,
32830         panels: [new CP("north", "North")]
32831     },
32832     west: {
32833         split:true,
32834         initialSize: 200,
32835         minSize: 175,
32836         maxSize: 400,
32837         titlebar: true,
32838         collapsible: true,
32839         panels: [new CP("west", {title: "West"})]
32840     },
32841     east: {
32842         split:true,
32843         initialSize: 202,
32844         minSize: 175,
32845         maxSize: 400,
32846         titlebar: true,
32847         collapsible: true,
32848         panels: [new CP("autoTabs", {title: "Auto Tabs", closable: true})]
32849     },
32850     south: {
32851         split:true,
32852         initialSize: 100,
32853         minSize: 100,
32854         maxSize: 200,
32855         titlebar: true,
32856         collapsible: true,
32857         panels: [new CP("south", {title: "South", closable: true})]
32858     },
32859     center: {
32860         titlebar: true,
32861         autoScroll:true,
32862         resizeTabs: true,
32863         minTabWidth: 50,
32864         preferredTabWidth: 150,
32865         panels: [
32866             new CP("center1", {title: "Close Me", closable: true}),
32867             new CP("center2", {title: "Center Panel", closable: false})
32868         ]
32869     }
32870 }, document.body);
32871
32872 layout.getRegion("center").showPanel("center1");
32873 </code></pre>
32874  * @param config
32875  * @param targetEl
32876  */
32877 Roo.BorderLayout.create = function(config, targetEl){
32878     var layout = new Roo.BorderLayout(targetEl || document.body, config);
32879     layout.beginUpdate();
32880     var regions = Roo.BorderLayout.RegionFactory.validRegions;
32881     for(var j = 0, jlen = regions.length; j < jlen; j++){
32882         var lr = regions[j];
32883         if(layout.regions[lr] && config[lr].panels){
32884             var r = layout.regions[lr];
32885             var ps = config[lr].panels;
32886             layout.addTypedPanels(r, ps);
32887         }
32888     }
32889     layout.endUpdate();
32890     return layout;
32891 };
32892
32893 // private
32894 Roo.BorderLayout.RegionFactory = {
32895     // private
32896     validRegions : ["north","south","east","west","center"],
32897
32898     // private
32899     create : function(target, mgr, config){
32900         target = target.toLowerCase();
32901         if(config.lightweight || config.basic){
32902             return new Roo.BasicLayoutRegion(mgr, config, target);
32903         }
32904         switch(target){
32905             case "north":
32906                 return new Roo.NorthLayoutRegion(mgr, config);
32907             case "south":
32908                 return new Roo.SouthLayoutRegion(mgr, config);
32909             case "east":
32910                 return new Roo.EastLayoutRegion(mgr, config);
32911             case "west":
32912                 return new Roo.WestLayoutRegion(mgr, config);
32913             case "center":
32914                 return new Roo.CenterLayoutRegion(mgr, config);
32915         }
32916         throw 'Layout region "'+target+'" not supported.';
32917     }
32918 };/*
32919  * Based on:
32920  * Ext JS Library 1.1.1
32921  * Copyright(c) 2006-2007, Ext JS, LLC.
32922  *
32923  * Originally Released Under LGPL - original licence link has changed is not relivant.
32924  *
32925  * Fork - LGPL
32926  * <script type="text/javascript">
32927  */
32928  
32929 /**
32930  * @class Roo.BasicLayoutRegion
32931  * @extends Roo.util.Observable
32932  * This class represents a lightweight region in a layout manager. This region does not move dom nodes
32933  * and does not have a titlebar, tabs or any other features. All it does is size and position 
32934  * panels. To create a BasicLayoutRegion, add lightweight:true or basic:true to your regions config.
32935  */
32936 Roo.BasicLayoutRegion = function(mgr, config, pos, skipConfig){
32937     this.mgr = mgr;
32938     this.position  = pos;
32939     this.events = {
32940         /**
32941          * @scope Roo.BasicLayoutRegion
32942          */
32943         
32944         /**
32945          * @event beforeremove
32946          * Fires before a panel is removed (or closed). To cancel the removal set "e.cancel = true" on the event argument.
32947          * @param {Roo.LayoutRegion} this
32948          * @param {Roo.ContentPanel} panel The panel
32949          * @param {Object} e The cancel event object
32950          */
32951         "beforeremove" : true,
32952         /**
32953          * @event invalidated
32954          * Fires when the layout for this region is changed.
32955          * @param {Roo.LayoutRegion} this
32956          */
32957         "invalidated" : true,
32958         /**
32959          * @event visibilitychange
32960          * Fires when this region is shown or hidden 
32961          * @param {Roo.LayoutRegion} this
32962          * @param {Boolean} visibility true or false
32963          */
32964         "visibilitychange" : true,
32965         /**
32966          * @event paneladded
32967          * Fires when a panel is added. 
32968          * @param {Roo.LayoutRegion} this
32969          * @param {Roo.ContentPanel} panel The panel
32970          */
32971         "paneladded" : true,
32972         /**
32973          * @event panelremoved
32974          * Fires when a panel is removed. 
32975          * @param {Roo.LayoutRegion} this
32976          * @param {Roo.ContentPanel} panel The panel
32977          */
32978         "panelremoved" : true,
32979         /**
32980          * @event collapsed
32981          * Fires when this region is collapsed.
32982          * @param {Roo.LayoutRegion} this
32983          */
32984         "collapsed" : true,
32985         /**
32986          * @event expanded
32987          * Fires when this region is expanded.
32988          * @param {Roo.LayoutRegion} this
32989          */
32990         "expanded" : true,
32991         /**
32992          * @event slideshow
32993          * Fires when this region is slid into view.
32994          * @param {Roo.LayoutRegion} this
32995          */
32996         "slideshow" : true,
32997         /**
32998          * @event slidehide
32999          * Fires when this region slides out of view. 
33000          * @param {Roo.LayoutRegion} this
33001          */
33002         "slidehide" : true,
33003         /**
33004          * @event panelactivated
33005          * Fires when a panel is activated. 
33006          * @param {Roo.LayoutRegion} this
33007          * @param {Roo.ContentPanel} panel The activated panel
33008          */
33009         "panelactivated" : true,
33010         /**
33011          * @event resized
33012          * Fires when the user resizes this region. 
33013          * @param {Roo.LayoutRegion} this
33014          * @param {Number} newSize The new size (width for east/west, height for north/south)
33015          */
33016         "resized" : true
33017     };
33018     /** A collection of panels in this region. @type Roo.util.MixedCollection */
33019     this.panels = new Roo.util.MixedCollection();
33020     this.panels.getKey = this.getPanelId.createDelegate(this);
33021     this.box = null;
33022     this.activePanel = null;
33023     // ensure listeners are added...
33024     
33025     if (config.listeners || config.events) {
33026         Roo.BasicLayoutRegion.superclass.constructor.call(this, {
33027             listeners : config.listeners || {},
33028             events : config.events || {}
33029         });
33030     }
33031     
33032     if(skipConfig !== true){
33033         this.applyConfig(config);
33034     }
33035 };
33036
33037 Roo.extend(Roo.BasicLayoutRegion, Roo.util.Observable, {
33038     getPanelId : function(p){
33039         return p.getId();
33040     },
33041     
33042     applyConfig : function(config){
33043         this.margins = config.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
33044         this.config = config;
33045         
33046     },
33047     
33048     /**
33049      * Resizes the region to the specified size. For vertical regions (west, east) this adjusts 
33050      * the width, for horizontal (north, south) the height.
33051      * @param {Number} newSize The new width or height
33052      */
33053     resizeTo : function(newSize){
33054         var el = this.el ? this.el :
33055                  (this.activePanel ? this.activePanel.getEl() : null);
33056         if(el){
33057             switch(this.position){
33058                 case "east":
33059                 case "west":
33060                     el.setWidth(newSize);
33061                     this.fireEvent("resized", this, newSize);
33062                 break;
33063                 case "north":
33064                 case "south":
33065                     el.setHeight(newSize);
33066                     this.fireEvent("resized", this, newSize);
33067                 break;                
33068             }
33069         }
33070     },
33071     
33072     getBox : function(){
33073         return this.activePanel ? this.activePanel.getEl().getBox(false, true) : null;
33074     },
33075     
33076     getMargins : function(){
33077         return this.margins;
33078     },
33079     
33080     updateBox : function(box){
33081         this.box = box;
33082         var el = this.activePanel.getEl();
33083         el.dom.style.left = box.x + "px";
33084         el.dom.style.top = box.y + "px";
33085         this.activePanel.setSize(box.width, box.height);
33086     },
33087     
33088     /**
33089      * Returns the container element for this region.
33090      * @return {Roo.Element}
33091      */
33092     getEl : function(){
33093         return this.activePanel;
33094     },
33095     
33096     /**
33097      * Returns true if this region is currently visible.
33098      * @return {Boolean}
33099      */
33100     isVisible : function(){
33101         return this.activePanel ? true : false;
33102     },
33103     
33104     setActivePanel : function(panel){
33105         panel = this.getPanel(panel);
33106         if(this.activePanel && this.activePanel != panel){
33107             this.activePanel.setActiveState(false);
33108             this.activePanel.getEl().setLeftTop(-10000,-10000);
33109         }
33110         this.activePanel = panel;
33111         panel.setActiveState(true);
33112         if(this.box){
33113             panel.setSize(this.box.width, this.box.height);
33114         }
33115         this.fireEvent("panelactivated", this, panel);
33116         this.fireEvent("invalidated");
33117     },
33118     
33119     /**
33120      * Show the specified panel.
33121      * @param {Number/String/ContentPanel} panelId The panels index, id or the panel itself
33122      * @return {Roo.ContentPanel} The shown panel or null
33123      */
33124     showPanel : function(panel){
33125         if(panel = this.getPanel(panel)){
33126             this.setActivePanel(panel);
33127         }
33128         return panel;
33129     },
33130     
33131     /**
33132      * Get the active panel for this region.
33133      * @return {Roo.ContentPanel} The active panel or null
33134      */
33135     getActivePanel : function(){
33136         return this.activePanel;
33137     },
33138     
33139     /**
33140      * Add the passed ContentPanel(s)
33141      * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
33142      * @return {Roo.ContentPanel} The panel added (if only one was added)
33143      */
33144     add : function(panel){
33145         if(arguments.length > 1){
33146             for(var i = 0, len = arguments.length; i < len; i++) {
33147                 this.add(arguments[i]);
33148             }
33149             return null;
33150         }
33151         if(this.hasPanel(panel)){
33152             this.showPanel(panel);
33153             return panel;
33154         }
33155         var el = panel.getEl();
33156         if(el.dom.parentNode != this.mgr.el.dom){
33157             this.mgr.el.dom.appendChild(el.dom);
33158         }
33159         if(panel.setRegion){
33160             panel.setRegion(this);
33161         }
33162         this.panels.add(panel);
33163         el.setStyle("position", "absolute");
33164         if(!panel.background){
33165             this.setActivePanel(panel);
33166             if(this.config.initialSize && this.panels.getCount()==1){
33167                 this.resizeTo(this.config.initialSize);
33168             }
33169         }
33170         this.fireEvent("paneladded", this, panel);
33171         return panel;
33172     },
33173     
33174     /**
33175      * Returns true if the panel is in this region.
33176      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
33177      * @return {Boolean}
33178      */
33179     hasPanel : function(panel){
33180         if(typeof panel == "object"){ // must be panel obj
33181             panel = panel.getId();
33182         }
33183         return this.getPanel(panel) ? true : false;
33184     },
33185     
33186     /**
33187      * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
33188      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
33189      * @param {Boolean} preservePanel Overrides the config preservePanel option
33190      * @return {Roo.ContentPanel} The panel that was removed
33191      */
33192     remove : function(panel, preservePanel){
33193         panel = this.getPanel(panel);
33194         if(!panel){
33195             return null;
33196         }
33197         var e = {};
33198         this.fireEvent("beforeremove", this, panel, e);
33199         if(e.cancel === true){
33200             return null;
33201         }
33202         var panelId = panel.getId();
33203         this.panels.removeKey(panelId);
33204         return panel;
33205     },
33206     
33207     /**
33208      * Returns the panel specified or null if it's not in this region.
33209      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
33210      * @return {Roo.ContentPanel}
33211      */
33212     getPanel : function(id){
33213         if(typeof id == "object"){ // must be panel obj
33214             return id;
33215         }
33216         return this.panels.get(id);
33217     },
33218     
33219     /**
33220      * Returns this regions position (north/south/east/west/center).
33221      * @return {String} 
33222      */
33223     getPosition: function(){
33224         return this.position;    
33225     }
33226 });/*
33227  * Based on:
33228  * Ext JS Library 1.1.1
33229  * Copyright(c) 2006-2007, Ext JS, LLC.
33230  *
33231  * Originally Released Under LGPL - original licence link has changed is not relivant.
33232  *
33233  * Fork - LGPL
33234  * <script type="text/javascript">
33235  */
33236  
33237 /**
33238  * @class Roo.LayoutRegion
33239  * @extends Roo.BasicLayoutRegion
33240  * This class represents a region in a layout manager.
33241  * @cfg {Boolean}   collapsible     False to disable collapsing (defaults to true)
33242  * @cfg {Boolean}   collapsed       True to set the initial display to collapsed (defaults to false)
33243  * @cfg {Boolean}   floatable       False to disable floating (defaults to true)
33244  * @cfg {Object}    margins         Margins for the element (defaults to {top: 0, left: 0, right:0, bottom: 0})
33245  * @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})
33246  * @cfg {String}    tabPosition     "top" or "bottom" (defaults to "bottom")
33247  * @cfg {String}    collapsedTitle  Optional string message to display in the collapsed block of a north or south region
33248  * @cfg {Boolean}   alwaysShowTabs  True to always display tabs even when there is only 1 panel (defaults to false)
33249  * @cfg {Boolean}   autoScroll      True to enable overflow scrolling (defaults to false)
33250  * @cfg {Boolean}   titlebar        True to display a title bar (defaults to true)
33251  * @cfg {String}    title           The title for the region (overrides panel titles)
33252  * @cfg {Boolean}   animate         True to animate expand/collapse (defaults to false)
33253  * @cfg {Boolean}   autoHide        False to disable auto hiding when the mouse leaves the "floated" region (defaults to true)
33254  * @cfg {Boolean}   preservePanels  True to preserve removed panels so they can be readded later (defaults to false)
33255  * @cfg {Boolean}   closeOnTab      True to place the close icon on the tabs instead of the region titlebar (defaults to false)
33256  * @cfg {Boolean}   hideTabs        True to hide the tab strip (defaults to false)
33257  * @cfg {Boolean}   resizeTabs      True to enable automatic tab resizing. This will resize the tabs so they are all the same size and fit within
33258  *                      the space available, similar to FireFox 1.5 tabs (defaults to false)
33259  * @cfg {Number}    minTabWidth     The minimum tab width (defaults to 40)
33260  * @cfg {Number}    preferredTabWidth The preferred tab width (defaults to 150)
33261  * @cfg {Boolean}   showPin         True to show a pin button
33262  * @cfg {Boolean}   hidden          True to start the region hidden (defaults to false)
33263  * @cfg {Boolean}   hideWhenEmpty   True to hide the region when it has no panels
33264  * @cfg {Boolean}   disableTabTips  True to disable tab tooltips
33265  * @cfg {Number}    width           For East/West panels
33266  * @cfg {Number}    height          For North/South panels
33267  * @cfg {Boolean}   split           To show the splitter
33268  * @cfg {Boolean}   toolbar         xtype configuration for a toolbar - shows on right of tabbar
33269  */
33270 Roo.LayoutRegion = function(mgr, config, pos){
33271     Roo.LayoutRegion.superclass.constructor.call(this, mgr, config, pos, true);
33272     var dh = Roo.DomHelper;
33273     /** This region's container element 
33274     * @type Roo.Element */
33275     this.el = dh.append(mgr.el.dom, {tag: "div", cls: "x-layout-panel x-layout-panel-" + this.position}, true);
33276     /** This region's title element 
33277     * @type Roo.Element */
33278
33279     this.titleEl = dh.append(this.el.dom, {tag: "div", unselectable: "on", cls: "x-unselectable x-layout-panel-hd x-layout-title-"+this.position, children:[
33280         {tag: "span", cls: "x-unselectable x-layout-panel-hd-text", unselectable: "on", html: "&#160;"},
33281         {tag: "div", cls: "x-unselectable x-layout-panel-hd-tools", unselectable: "on"}
33282     ]}, true);
33283     this.titleEl.enableDisplayMode();
33284     /** This region's title text element 
33285     * @type HTMLElement */
33286     this.titleTextEl = this.titleEl.dom.firstChild;
33287     this.tools = Roo.get(this.titleEl.dom.childNodes[1], true);
33288     this.closeBtn = this.createTool(this.tools.dom, "x-layout-close");
33289     this.closeBtn.enableDisplayMode();
33290     this.closeBtn.on("click", this.closeClicked, this);
33291     this.closeBtn.hide();
33292
33293     this.createBody(config);
33294     this.visible = true;
33295     this.collapsed = false;
33296
33297     if(config.hideWhenEmpty){
33298         this.hide();
33299         this.on("paneladded", this.validateVisibility, this);
33300         this.on("panelremoved", this.validateVisibility, this);
33301     }
33302     this.applyConfig(config);
33303 };
33304
33305 Roo.extend(Roo.LayoutRegion, Roo.BasicLayoutRegion, {
33306
33307     createBody : function(){
33308         /** This region's body element 
33309         * @type Roo.Element */
33310         this.bodyEl = this.el.createChild({tag: "div", cls: "x-layout-panel-body"});
33311     },
33312
33313     applyConfig : function(c){
33314         if(c.collapsible && this.position != "center" && !this.collapsedEl){
33315             var dh = Roo.DomHelper;
33316             if(c.titlebar !== false){
33317                 this.collapseBtn = this.createTool(this.tools.dom, "x-layout-collapse-"+this.position);
33318                 this.collapseBtn.on("click", this.collapse, this);
33319                 this.collapseBtn.enableDisplayMode();
33320
33321                 if(c.showPin === true || this.showPin){
33322                     this.stickBtn = this.createTool(this.tools.dom, "x-layout-stick");
33323                     this.stickBtn.enableDisplayMode();
33324                     this.stickBtn.on("click", this.expand, this);
33325                     this.stickBtn.hide();
33326                 }
33327             }
33328             /** This region's collapsed element
33329             * @type Roo.Element */
33330             this.collapsedEl = dh.append(this.mgr.el.dom, {cls: "x-layout-collapsed x-layout-collapsed-"+this.position, children:[
33331                 {cls: "x-layout-collapsed-tools", children:[{cls: "x-layout-ctools-inner"}]}
33332             ]}, true);
33333             if(c.floatable !== false){
33334                this.collapsedEl.addClassOnOver("x-layout-collapsed-over");
33335                this.collapsedEl.on("click", this.collapseClick, this);
33336             }
33337
33338             if(c.collapsedTitle && (this.position == "north" || this.position== "south")) {
33339                 this.collapsedTitleTextEl = dh.append(this.collapsedEl.dom, {tag: "div", cls: "x-unselectable x-layout-panel-hd-text",
33340                    id: "message", unselectable: "on", style:{"float":"left"}});
33341                this.collapsedTitleTextEl.innerHTML = c.collapsedTitle;
33342              }
33343             this.expandBtn = this.createTool(this.collapsedEl.dom.firstChild.firstChild, "x-layout-expand-"+this.position);
33344             this.expandBtn.on("click", this.expand, this);
33345         }
33346         if(this.collapseBtn){
33347             this.collapseBtn.setVisible(c.collapsible == true);
33348         }
33349         this.cmargins = c.cmargins || this.cmargins ||
33350                          (this.position == "west" || this.position == "east" ?
33351                              {top: 0, left: 2, right:2, bottom: 0} :
33352                              {top: 2, left: 0, right:0, bottom: 2});
33353         this.margins = c.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
33354         this.bottomTabs = c.tabPosition != "top";
33355         this.autoScroll = c.autoScroll || false;
33356         if(this.autoScroll){
33357             this.bodyEl.setStyle("overflow", "auto");
33358         }else{
33359             this.bodyEl.setStyle("overflow", "hidden");
33360         }
33361         //if(c.titlebar !== false){
33362             if((!c.titlebar && !c.title) || c.titlebar === false){
33363                 this.titleEl.hide();
33364             }else{
33365                 this.titleEl.show();
33366                 if(c.title){
33367                     this.titleTextEl.innerHTML = c.title;
33368                 }
33369             }
33370         //}
33371         this.duration = c.duration || .30;
33372         this.slideDuration = c.slideDuration || .45;
33373         this.config = c;
33374         if(c.collapsed){
33375             this.collapse(true);
33376         }
33377         if(c.hidden){
33378             this.hide();
33379         }
33380     },
33381     /**
33382      * Returns true if this region is currently visible.
33383      * @return {Boolean}
33384      */
33385     isVisible : function(){
33386         return this.visible;
33387     },
33388
33389     /**
33390      * Updates the title for collapsed north/south regions (used with {@link #collapsedTitle} config option)
33391      * @param {String} title (optional) The title text (accepts HTML markup, defaults to the numeric character reference for a non-breaking space, "&amp;#160;")
33392      */
33393     setCollapsedTitle : function(title){
33394         title = title || "&#160;";
33395         if(this.collapsedTitleTextEl){
33396             this.collapsedTitleTextEl.innerHTML = title;
33397         }
33398     },
33399
33400     getBox : function(){
33401         var b;
33402         if(!this.collapsed){
33403             b = this.el.getBox(false, true);
33404         }else{
33405             b = this.collapsedEl.getBox(false, true);
33406         }
33407         return b;
33408     },
33409
33410     getMargins : function(){
33411         return this.collapsed ? this.cmargins : this.margins;
33412     },
33413
33414     highlight : function(){
33415         this.el.addClass("x-layout-panel-dragover");
33416     },
33417
33418     unhighlight : function(){
33419         this.el.removeClass("x-layout-panel-dragover");
33420     },
33421
33422     updateBox : function(box){
33423         this.box = box;
33424         if(!this.collapsed){
33425             this.el.dom.style.left = box.x + "px";
33426             this.el.dom.style.top = box.y + "px";
33427             this.updateBody(box.width, box.height);
33428         }else{
33429             this.collapsedEl.dom.style.left = box.x + "px";
33430             this.collapsedEl.dom.style.top = box.y + "px";
33431             this.collapsedEl.setSize(box.width, box.height);
33432         }
33433         if(this.tabs){
33434             this.tabs.autoSizeTabs();
33435         }
33436     },
33437
33438     updateBody : function(w, h){
33439         if(w !== null){
33440             this.el.setWidth(w);
33441             w -= this.el.getBorderWidth("rl");
33442             if(this.config.adjustments){
33443                 w += this.config.adjustments[0];
33444             }
33445         }
33446         if(h !== null){
33447             this.el.setHeight(h);
33448             h = this.titleEl && this.titleEl.isDisplayed() ? h - (this.titleEl.getHeight()||0) : h;
33449             h -= this.el.getBorderWidth("tb");
33450             if(this.config.adjustments){
33451                 h += this.config.adjustments[1];
33452             }
33453             this.bodyEl.setHeight(h);
33454             if(this.tabs){
33455                 h = this.tabs.syncHeight(h);
33456             }
33457         }
33458         if(this.panelSize){
33459             w = w !== null ? w : this.panelSize.width;
33460             h = h !== null ? h : this.panelSize.height;
33461         }
33462         if(this.activePanel){
33463             var el = this.activePanel.getEl();
33464             w = w !== null ? w : el.getWidth();
33465             h = h !== null ? h : el.getHeight();
33466             this.panelSize = {width: w, height: h};
33467             this.activePanel.setSize(w, h);
33468         }
33469         if(Roo.isIE && this.tabs){
33470             this.tabs.el.repaint();
33471         }
33472     },
33473
33474     /**
33475      * Returns the container element for this region.
33476      * @return {Roo.Element}
33477      */
33478     getEl : function(){
33479         return this.el;
33480     },
33481
33482     /**
33483      * Hides this region.
33484      */
33485     hide : function(){
33486         if(!this.collapsed){
33487             this.el.dom.style.left = "-2000px";
33488             this.el.hide();
33489         }else{
33490             this.collapsedEl.dom.style.left = "-2000px";
33491             this.collapsedEl.hide();
33492         }
33493         this.visible = false;
33494         this.fireEvent("visibilitychange", this, false);
33495     },
33496
33497     /**
33498      * Shows this region if it was previously hidden.
33499      */
33500     show : function(){
33501         if(!this.collapsed){
33502             this.el.show();
33503         }else{
33504             this.collapsedEl.show();
33505         }
33506         this.visible = true;
33507         this.fireEvent("visibilitychange", this, true);
33508     },
33509
33510     closeClicked : function(){
33511         if(this.activePanel){
33512             this.remove(this.activePanel);
33513         }
33514     },
33515
33516     collapseClick : function(e){
33517         if(this.isSlid){
33518            e.stopPropagation();
33519            this.slideIn();
33520         }else{
33521            e.stopPropagation();
33522            this.slideOut();
33523         }
33524     },
33525
33526     /**
33527      * Collapses this region.
33528      * @param {Boolean} skipAnim (optional) true to collapse the element without animation (if animate is true)
33529      */
33530     collapse : function(skipAnim){
33531         if(this.collapsed) return;
33532         this.collapsed = true;
33533         if(this.split){
33534             this.split.el.hide();
33535         }
33536         if(this.config.animate && skipAnim !== true){
33537             this.fireEvent("invalidated", this);
33538             this.animateCollapse();
33539         }else{
33540             this.el.setLocation(-20000,-20000);
33541             this.el.hide();
33542             this.collapsedEl.show();
33543             this.fireEvent("collapsed", this);
33544             this.fireEvent("invalidated", this);
33545         }
33546     },
33547
33548     animateCollapse : function(){
33549         // overridden
33550     },
33551
33552     /**
33553      * Expands this region if it was previously collapsed.
33554      * @param {Roo.EventObject} e The event that triggered the expand (or null if calling manually)
33555      * @param {Boolean} skipAnim (optional) true to expand the element without animation (if animate is true)
33556      */
33557     expand : function(e, skipAnim){
33558         if(e) e.stopPropagation();
33559         if(!this.collapsed || this.el.hasActiveFx()) return;
33560         if(this.isSlid){
33561             this.afterSlideIn();
33562             skipAnim = true;
33563         }
33564         this.collapsed = false;
33565         if(this.config.animate && skipAnim !== true){
33566             this.animateExpand();
33567         }else{
33568             this.el.show();
33569             if(this.split){
33570                 this.split.el.show();
33571             }
33572             this.collapsedEl.setLocation(-2000,-2000);
33573             this.collapsedEl.hide();
33574             this.fireEvent("invalidated", this);
33575             this.fireEvent("expanded", this);
33576         }
33577     },
33578
33579     animateExpand : function(){
33580         // overridden
33581     },
33582
33583     initTabs : function()
33584     {
33585         this.bodyEl.setStyle("overflow", "hidden");
33586         var ts = new Roo.TabPanel(
33587                 this.bodyEl.dom,
33588                 {
33589                     tabPosition: this.bottomTabs ? 'bottom' : 'top',
33590                     disableTooltips: this.config.disableTabTips,
33591                     toolbar : this.config.toolbar
33592                 }
33593         );
33594         if(this.config.hideTabs){
33595             ts.stripWrap.setDisplayed(false);
33596         }
33597         this.tabs = ts;
33598         ts.resizeTabs = this.config.resizeTabs === true;
33599         ts.minTabWidth = this.config.minTabWidth || 40;
33600         ts.maxTabWidth = this.config.maxTabWidth || 250;
33601         ts.preferredTabWidth = this.config.preferredTabWidth || 150;
33602         ts.monitorResize = false;
33603         ts.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden");
33604         ts.bodyEl.addClass('x-layout-tabs-body');
33605         this.panels.each(this.initPanelAsTab, this);
33606     },
33607
33608     initPanelAsTab : function(panel){
33609         var ti = this.tabs.addTab(panel.getEl().id, panel.getTitle(), null,
33610                     this.config.closeOnTab && panel.isClosable());
33611         if(panel.tabTip !== undefined){
33612             ti.setTooltip(panel.tabTip);
33613         }
33614         ti.on("activate", function(){
33615               this.setActivePanel(panel);
33616         }, this);
33617         if(this.config.closeOnTab){
33618             ti.on("beforeclose", function(t, e){
33619                 e.cancel = true;
33620                 this.remove(panel);
33621             }, this);
33622         }
33623         return ti;
33624     },
33625
33626     updatePanelTitle : function(panel, title){
33627         if(this.activePanel == panel){
33628             this.updateTitle(title);
33629         }
33630         if(this.tabs){
33631             var ti = this.tabs.getTab(panel.getEl().id);
33632             ti.setText(title);
33633             if(panel.tabTip !== undefined){
33634                 ti.setTooltip(panel.tabTip);
33635             }
33636         }
33637     },
33638
33639     updateTitle : function(title){
33640         if(this.titleTextEl && !this.config.title){
33641             this.titleTextEl.innerHTML = (typeof title != "undefined" && title.length > 0 ? title : "&#160;");
33642         }
33643     },
33644
33645     setActivePanel : function(panel){
33646         panel = this.getPanel(panel);
33647         if(this.activePanel && this.activePanel != panel){
33648             this.activePanel.setActiveState(false);
33649         }
33650         this.activePanel = panel;
33651         panel.setActiveState(true);
33652         if(this.panelSize){
33653             panel.setSize(this.panelSize.width, this.panelSize.height);
33654         }
33655         if(this.closeBtn){
33656             this.closeBtn.setVisible(!this.config.closeOnTab && !this.isSlid && panel.isClosable());
33657         }
33658         this.updateTitle(panel.getTitle());
33659         if(this.tabs){
33660             this.fireEvent("invalidated", this);
33661         }
33662         this.fireEvent("panelactivated", this, panel);
33663     },
33664
33665     /**
33666      * Shows the specified panel.
33667      * @param {Number/String/ContentPanel} panelId The panel's index, id or the panel itself
33668      * @return {Roo.ContentPanel} The shown panel, or null if a panel could not be found from panelId
33669      */
33670     showPanel : function(panel){
33671         if(panel = this.getPanel(panel)){
33672             if(this.tabs){
33673                 var tab = this.tabs.getTab(panel.getEl().id);
33674                 if(tab.isHidden()){
33675                     this.tabs.unhideTab(tab.id);
33676                 }
33677                 tab.activate();
33678             }else{
33679                 this.setActivePanel(panel);
33680             }
33681         }
33682         return panel;
33683     },
33684
33685     /**
33686      * Get the active panel for this region.
33687      * @return {Roo.ContentPanel} The active panel or null
33688      */
33689     getActivePanel : function(){
33690         return this.activePanel;
33691     },
33692
33693     validateVisibility : function(){
33694         if(this.panels.getCount() < 1){
33695             this.updateTitle("&#160;");
33696             this.closeBtn.hide();
33697             this.hide();
33698         }else{
33699             if(!this.isVisible()){
33700                 this.show();
33701             }
33702         }
33703     },
33704
33705     /**
33706      * Adds the passed ContentPanel(s) to this region.
33707      * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
33708      * @return {Roo.ContentPanel} The panel added (if only one was added; null otherwise)
33709      */
33710     add : function(panel){
33711         if(arguments.length > 1){
33712             for(var i = 0, len = arguments.length; i < len; i++) {
33713                 this.add(arguments[i]);
33714             }
33715             return null;
33716         }
33717         if(this.hasPanel(panel)){
33718             this.showPanel(panel);
33719             return panel;
33720         }
33721         panel.setRegion(this);
33722         this.panels.add(panel);
33723         if(this.panels.getCount() == 1 && !this.config.alwaysShowTabs){
33724             this.bodyEl.dom.appendChild(panel.getEl().dom);
33725             if(panel.background !== true){
33726                 this.setActivePanel(panel);
33727             }
33728             this.fireEvent("paneladded", this, panel);
33729             return panel;
33730         }
33731         if(!this.tabs){
33732             this.initTabs();
33733         }else{
33734             this.initPanelAsTab(panel);
33735         }
33736         if(panel.background !== true){
33737             this.tabs.activate(panel.getEl().id);
33738         }
33739         this.fireEvent("paneladded", this, panel);
33740         return panel;
33741     },
33742
33743     /**
33744      * Hides the tab for the specified panel.
33745      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
33746      */
33747     hidePanel : function(panel){
33748         if(this.tabs && (panel = this.getPanel(panel))){
33749             this.tabs.hideTab(panel.getEl().id);
33750         }
33751     },
33752
33753     /**
33754      * Unhides the tab for a previously hidden panel.
33755      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
33756      */
33757     unhidePanel : function(panel){
33758         if(this.tabs && (panel = this.getPanel(panel))){
33759             this.tabs.unhideTab(panel.getEl().id);
33760         }
33761     },
33762
33763     clearPanels : function(){
33764         while(this.panels.getCount() > 0){
33765              this.remove(this.panels.first());
33766         }
33767     },
33768
33769     /**
33770      * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
33771      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
33772      * @param {Boolean} preservePanel Overrides the config preservePanel option
33773      * @return {Roo.ContentPanel} The panel that was removed
33774      */
33775     remove : function(panel, preservePanel){
33776         panel = this.getPanel(panel);
33777         if(!panel){
33778             return null;
33779         }
33780         var e = {};
33781         this.fireEvent("beforeremove", this, panel, e);
33782         if(e.cancel === true){
33783             return null;
33784         }
33785         preservePanel = (typeof preservePanel != "undefined" ? preservePanel : (this.config.preservePanels === true || panel.preserve === true));
33786         var panelId = panel.getId();
33787         this.panels.removeKey(panelId);
33788         if(preservePanel){
33789             document.body.appendChild(panel.getEl().dom);
33790         }
33791         if(this.tabs){
33792             this.tabs.removeTab(panel.getEl().id);
33793         }else if (!preservePanel){
33794             this.bodyEl.dom.removeChild(panel.getEl().dom);
33795         }
33796         if(this.panels.getCount() == 1 && this.tabs && !this.config.alwaysShowTabs){
33797             var p = this.panels.first();
33798             var tempEl = document.createElement("div"); // temp holder to keep IE from deleting the node
33799             tempEl.appendChild(p.getEl().dom);
33800             this.bodyEl.update("");
33801             this.bodyEl.dom.appendChild(p.getEl().dom);
33802             tempEl = null;
33803             this.updateTitle(p.getTitle());
33804             this.tabs = null;
33805             this.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden");
33806             this.setActivePanel(p);
33807         }
33808         panel.setRegion(null);
33809         if(this.activePanel == panel){
33810             this.activePanel = null;
33811         }
33812         if(this.config.autoDestroy !== false && preservePanel !== true){
33813             try{panel.destroy();}catch(e){}
33814         }
33815         this.fireEvent("panelremoved", this, panel);
33816         return panel;
33817     },
33818
33819     /**
33820      * Returns the TabPanel component used by this region
33821      * @return {Roo.TabPanel}
33822      */
33823     getTabs : function(){
33824         return this.tabs;
33825     },
33826
33827     createTool : function(parentEl, className){
33828         var btn = Roo.DomHelper.append(parentEl, {tag: "div", cls: "x-layout-tools-button",
33829             children: [{tag: "div", cls: "x-layout-tools-button-inner " + className, html: "&#160;"}]}, true);
33830         btn.addClassOnOver("x-layout-tools-button-over");
33831         return btn;
33832     }
33833 });/*
33834  * Based on:
33835  * Ext JS Library 1.1.1
33836  * Copyright(c) 2006-2007, Ext JS, LLC.
33837  *
33838  * Originally Released Under LGPL - original licence link has changed is not relivant.
33839  *
33840  * Fork - LGPL
33841  * <script type="text/javascript">
33842  */
33843  
33844
33845
33846 /**
33847  * @class Roo.SplitLayoutRegion
33848  * @extends Roo.LayoutRegion
33849  * Adds a splitbar and other (private) useful functionality to a {@link Roo.LayoutRegion}.
33850  */
33851 Roo.SplitLayoutRegion = function(mgr, config, pos, cursor){
33852     this.cursor = cursor;
33853     Roo.SplitLayoutRegion.superclass.constructor.call(this, mgr, config, pos);
33854 };
33855
33856 Roo.extend(Roo.SplitLayoutRegion, Roo.LayoutRegion, {
33857     splitTip : "Drag to resize.",
33858     collapsibleSplitTip : "Drag to resize. Double click to hide.",
33859     useSplitTips : false,
33860
33861     applyConfig : function(config){
33862         Roo.SplitLayoutRegion.superclass.applyConfig.call(this, config);
33863         if(config.split){
33864             if(!this.split){
33865                 var splitEl = Roo.DomHelper.append(this.mgr.el.dom, 
33866                         {tag: "div", id: this.el.id + "-split", cls: "x-layout-split x-layout-split-"+this.position, html: "&#160;"});
33867                 /** The SplitBar for this region 
33868                 * @type Roo.SplitBar */
33869                 this.split = new Roo.SplitBar(splitEl, this.el, this.orientation);
33870                 this.split.on("moved", this.onSplitMove, this);
33871                 this.split.useShim = config.useShim === true;
33872                 this.split.getMaximumSize = this[this.position == 'north' || this.position == 'south' ? 'getVMaxSize' : 'getHMaxSize'].createDelegate(this);
33873                 if(this.useSplitTips){
33874                     this.split.el.dom.title = config.collapsible ? this.collapsibleSplitTip : this.splitTip;
33875                 }
33876                 if(config.collapsible){
33877                     this.split.el.on("dblclick", this.collapse,  this);
33878                 }
33879             }
33880             if(typeof config.minSize != "undefined"){
33881                 this.split.minSize = config.minSize;
33882             }
33883             if(typeof config.maxSize != "undefined"){
33884                 this.split.maxSize = config.maxSize;
33885             }
33886             if(config.hideWhenEmpty || config.hidden || config.collapsed){
33887                 this.hideSplitter();
33888             }
33889         }
33890     },
33891
33892     getHMaxSize : function(){
33893          var cmax = this.config.maxSize || 10000;
33894          var center = this.mgr.getRegion("center");
33895          return Math.min(cmax, (this.el.getWidth()+center.getEl().getWidth())-center.getMinWidth());
33896     },
33897
33898     getVMaxSize : function(){
33899          var cmax = this.config.maxSize || 10000;
33900          var center = this.mgr.getRegion("center");
33901          return Math.min(cmax, (this.el.getHeight()+center.getEl().getHeight())-center.getMinHeight());
33902     },
33903
33904     onSplitMove : function(split, newSize){
33905         this.fireEvent("resized", this, newSize);
33906     },
33907     
33908     /** 
33909      * Returns the {@link Roo.SplitBar} for this region.
33910      * @return {Roo.SplitBar}
33911      */
33912     getSplitBar : function(){
33913         return this.split;
33914     },
33915     
33916     hide : function(){
33917         this.hideSplitter();
33918         Roo.SplitLayoutRegion.superclass.hide.call(this);
33919     },
33920
33921     hideSplitter : function(){
33922         if(this.split){
33923             this.split.el.setLocation(-2000,-2000);
33924             this.split.el.hide();
33925         }
33926     },
33927
33928     show : function(){
33929         if(this.split){
33930             this.split.el.show();
33931         }
33932         Roo.SplitLayoutRegion.superclass.show.call(this);
33933     },
33934     
33935     beforeSlide: function(){
33936         if(Roo.isGecko){// firefox overflow auto bug workaround
33937             this.bodyEl.clip();
33938             if(this.tabs) this.tabs.bodyEl.clip();
33939             if(this.activePanel){
33940                 this.activePanel.getEl().clip();
33941                 
33942                 if(this.activePanel.beforeSlide){
33943                     this.activePanel.beforeSlide();
33944                 }
33945             }
33946         }
33947     },
33948     
33949     afterSlide : function(){
33950         if(Roo.isGecko){// firefox overflow auto bug workaround
33951             this.bodyEl.unclip();
33952             if(this.tabs) this.tabs.bodyEl.unclip();
33953             if(this.activePanel){
33954                 this.activePanel.getEl().unclip();
33955                 if(this.activePanel.afterSlide){
33956                     this.activePanel.afterSlide();
33957                 }
33958             }
33959         }
33960     },
33961
33962     initAutoHide : function(){
33963         if(this.autoHide !== false){
33964             if(!this.autoHideHd){
33965                 var st = new Roo.util.DelayedTask(this.slideIn, this);
33966                 this.autoHideHd = {
33967                     "mouseout": function(e){
33968                         if(!e.within(this.el, true)){
33969                             st.delay(500);
33970                         }
33971                     },
33972                     "mouseover" : function(e){
33973                         st.cancel();
33974                     },
33975                     scope : this
33976                 };
33977             }
33978             this.el.on(this.autoHideHd);
33979         }
33980     },
33981
33982     clearAutoHide : function(){
33983         if(this.autoHide !== false){
33984             this.el.un("mouseout", this.autoHideHd.mouseout);
33985             this.el.un("mouseover", this.autoHideHd.mouseover);
33986         }
33987     },
33988
33989     clearMonitor : function(){
33990         Roo.get(document).un("click", this.slideInIf, this);
33991     },
33992
33993     // these names are backwards but not changed for compat
33994     slideOut : function(){
33995         if(this.isSlid || this.el.hasActiveFx()){
33996             return;
33997         }
33998         this.isSlid = true;
33999         if(this.collapseBtn){
34000             this.collapseBtn.hide();
34001         }
34002         this.closeBtnState = this.closeBtn.getStyle('display');
34003         this.closeBtn.hide();
34004         if(this.stickBtn){
34005             this.stickBtn.show();
34006         }
34007         this.el.show();
34008         this.el.alignTo(this.collapsedEl, this.getCollapseAnchor());
34009         this.beforeSlide();
34010         this.el.setStyle("z-index", 10001);
34011         this.el.slideIn(this.getSlideAnchor(), {
34012             callback: function(){
34013                 this.afterSlide();
34014                 this.initAutoHide();
34015                 Roo.get(document).on("click", this.slideInIf, this);
34016                 this.fireEvent("slideshow", this);
34017             },
34018             scope: this,
34019             block: true
34020         });
34021     },
34022
34023     afterSlideIn : function(){
34024         this.clearAutoHide();
34025         this.isSlid = false;
34026         this.clearMonitor();
34027         this.el.setStyle("z-index", "");
34028         if(this.collapseBtn){
34029             this.collapseBtn.show();
34030         }
34031         this.closeBtn.setStyle('display', this.closeBtnState);
34032         if(this.stickBtn){
34033             this.stickBtn.hide();
34034         }
34035         this.fireEvent("slidehide", this);
34036     },
34037
34038     slideIn : function(cb){
34039         if(!this.isSlid || this.el.hasActiveFx()){
34040             Roo.callback(cb);
34041             return;
34042         }
34043         this.isSlid = false;
34044         this.beforeSlide();
34045         this.el.slideOut(this.getSlideAnchor(), {
34046             callback: function(){
34047                 this.el.setLeftTop(-10000, -10000);
34048                 this.afterSlide();
34049                 this.afterSlideIn();
34050                 Roo.callback(cb);
34051             },
34052             scope: this,
34053             block: true
34054         });
34055     },
34056     
34057     slideInIf : function(e){
34058         if(!e.within(this.el)){
34059             this.slideIn();
34060         }
34061     },
34062
34063     animateCollapse : function(){
34064         this.beforeSlide();
34065         this.el.setStyle("z-index", 20000);
34066         var anchor = this.getSlideAnchor();
34067         this.el.slideOut(anchor, {
34068             callback : function(){
34069                 this.el.setStyle("z-index", "");
34070                 this.collapsedEl.slideIn(anchor, {duration:.3});
34071                 this.afterSlide();
34072                 this.el.setLocation(-10000,-10000);
34073                 this.el.hide();
34074                 this.fireEvent("collapsed", this);
34075             },
34076             scope: this,
34077             block: true
34078         });
34079     },
34080
34081     animateExpand : function(){
34082         this.beforeSlide();
34083         this.el.alignTo(this.collapsedEl, this.getCollapseAnchor(), this.getExpandAdj());
34084         this.el.setStyle("z-index", 20000);
34085         this.collapsedEl.hide({
34086             duration:.1
34087         });
34088         this.el.slideIn(this.getSlideAnchor(), {
34089             callback : function(){
34090                 this.el.setStyle("z-index", "");
34091                 this.afterSlide();
34092                 if(this.split){
34093                     this.split.el.show();
34094                 }
34095                 this.fireEvent("invalidated", this);
34096                 this.fireEvent("expanded", this);
34097             },
34098             scope: this,
34099             block: true
34100         });
34101     },
34102
34103     anchors : {
34104         "west" : "left",
34105         "east" : "right",
34106         "north" : "top",
34107         "south" : "bottom"
34108     },
34109
34110     sanchors : {
34111         "west" : "l",
34112         "east" : "r",
34113         "north" : "t",
34114         "south" : "b"
34115     },
34116
34117     canchors : {
34118         "west" : "tl-tr",
34119         "east" : "tr-tl",
34120         "north" : "tl-bl",
34121         "south" : "bl-tl"
34122     },
34123
34124     getAnchor : function(){
34125         return this.anchors[this.position];
34126     },
34127
34128     getCollapseAnchor : function(){
34129         return this.canchors[this.position];
34130     },
34131
34132     getSlideAnchor : function(){
34133         return this.sanchors[this.position];
34134     },
34135
34136     getAlignAdj : function(){
34137         var cm = this.cmargins;
34138         switch(this.position){
34139             case "west":
34140                 return [0, 0];
34141             break;
34142             case "east":
34143                 return [0, 0];
34144             break;
34145             case "north":
34146                 return [0, 0];
34147             break;
34148             case "south":
34149                 return [0, 0];
34150             break;
34151         }
34152     },
34153
34154     getExpandAdj : function(){
34155         var c = this.collapsedEl, cm = this.cmargins;
34156         switch(this.position){
34157             case "west":
34158                 return [-(cm.right+c.getWidth()+cm.left), 0];
34159             break;
34160             case "east":
34161                 return [cm.right+c.getWidth()+cm.left, 0];
34162             break;
34163             case "north":
34164                 return [0, -(cm.top+cm.bottom+c.getHeight())];
34165             break;
34166             case "south":
34167                 return [0, cm.top+cm.bottom+c.getHeight()];
34168             break;
34169         }
34170     }
34171 });/*
34172  * Based on:
34173  * Ext JS Library 1.1.1
34174  * Copyright(c) 2006-2007, Ext JS, LLC.
34175  *
34176  * Originally Released Under LGPL - original licence link has changed is not relivant.
34177  *
34178  * Fork - LGPL
34179  * <script type="text/javascript">
34180  */
34181 /*
34182  * These classes are private internal classes
34183  */
34184 Roo.CenterLayoutRegion = function(mgr, config){
34185     Roo.LayoutRegion.call(this, mgr, config, "center");
34186     this.visible = true;
34187     this.minWidth = config.minWidth || 20;
34188     this.minHeight = config.minHeight || 20;
34189 };
34190
34191 Roo.extend(Roo.CenterLayoutRegion, Roo.LayoutRegion, {
34192     hide : function(){
34193         // center panel can't be hidden
34194     },
34195     
34196     show : function(){
34197         // center panel can't be hidden
34198     },
34199     
34200     getMinWidth: function(){
34201         return this.minWidth;
34202     },
34203     
34204     getMinHeight: function(){
34205         return this.minHeight;
34206     }
34207 });
34208
34209
34210 Roo.NorthLayoutRegion = function(mgr, config){
34211     Roo.LayoutRegion.call(this, mgr, config, "north", "n-resize");
34212     if(this.split){
34213         this.split.placement = Roo.SplitBar.TOP;
34214         this.split.orientation = Roo.SplitBar.VERTICAL;
34215         this.split.el.addClass("x-layout-split-v");
34216     }
34217     var size = config.initialSize || config.height;
34218     if(typeof size != "undefined"){
34219         this.el.setHeight(size);
34220     }
34221 };
34222 Roo.extend(Roo.NorthLayoutRegion, Roo.SplitLayoutRegion, {
34223     orientation: Roo.SplitBar.VERTICAL,
34224     getBox : function(){
34225         if(this.collapsed){
34226             return this.collapsedEl.getBox();
34227         }
34228         var box = this.el.getBox();
34229         if(this.split){
34230             box.height += this.split.el.getHeight();
34231         }
34232         return box;
34233     },
34234     
34235     updateBox : function(box){
34236         if(this.split && !this.collapsed){
34237             box.height -= this.split.el.getHeight();
34238             this.split.el.setLeft(box.x);
34239             this.split.el.setTop(box.y+box.height);
34240             this.split.el.setWidth(box.width);
34241         }
34242         if(this.collapsed){
34243             this.updateBody(box.width, null);
34244         }
34245         Roo.LayoutRegion.prototype.updateBox.call(this, box);
34246     }
34247 });
34248
34249 Roo.SouthLayoutRegion = function(mgr, config){
34250     Roo.SplitLayoutRegion.call(this, mgr, config, "south", "s-resize");
34251     if(this.split){
34252         this.split.placement = Roo.SplitBar.BOTTOM;
34253         this.split.orientation = Roo.SplitBar.VERTICAL;
34254         this.split.el.addClass("x-layout-split-v");
34255     }
34256     var size = config.initialSize || config.height;
34257     if(typeof size != "undefined"){
34258         this.el.setHeight(size);
34259     }
34260 };
34261 Roo.extend(Roo.SouthLayoutRegion, Roo.SplitLayoutRegion, {
34262     orientation: Roo.SplitBar.VERTICAL,
34263     getBox : function(){
34264         if(this.collapsed){
34265             return this.collapsedEl.getBox();
34266         }
34267         var box = this.el.getBox();
34268         if(this.split){
34269             var sh = this.split.el.getHeight();
34270             box.height += sh;
34271             box.y -= sh;
34272         }
34273         return box;
34274     },
34275     
34276     updateBox : function(box){
34277         if(this.split && !this.collapsed){
34278             var sh = this.split.el.getHeight();
34279             box.height -= sh;
34280             box.y += sh;
34281             this.split.el.setLeft(box.x);
34282             this.split.el.setTop(box.y-sh);
34283             this.split.el.setWidth(box.width);
34284         }
34285         if(this.collapsed){
34286             this.updateBody(box.width, null);
34287         }
34288         Roo.LayoutRegion.prototype.updateBox.call(this, box);
34289     }
34290 });
34291
34292 Roo.EastLayoutRegion = function(mgr, config){
34293     Roo.SplitLayoutRegion.call(this, mgr, config, "east", "e-resize");
34294     if(this.split){
34295         this.split.placement = Roo.SplitBar.RIGHT;
34296         this.split.orientation = Roo.SplitBar.HORIZONTAL;
34297         this.split.el.addClass("x-layout-split-h");
34298     }
34299     var size = config.initialSize || config.width;
34300     if(typeof size != "undefined"){
34301         this.el.setWidth(size);
34302     }
34303 };
34304 Roo.extend(Roo.EastLayoutRegion, Roo.SplitLayoutRegion, {
34305     orientation: Roo.SplitBar.HORIZONTAL,
34306     getBox : function(){
34307         if(this.collapsed){
34308             return this.collapsedEl.getBox();
34309         }
34310         var box = this.el.getBox();
34311         if(this.split){
34312             var sw = this.split.el.getWidth();
34313             box.width += sw;
34314             box.x -= sw;
34315         }
34316         return box;
34317     },
34318
34319     updateBox : function(box){
34320         if(this.split && !this.collapsed){
34321             var sw = this.split.el.getWidth();
34322             box.width -= sw;
34323             this.split.el.setLeft(box.x);
34324             this.split.el.setTop(box.y);
34325             this.split.el.setHeight(box.height);
34326             box.x += sw;
34327         }
34328         if(this.collapsed){
34329             this.updateBody(null, box.height);
34330         }
34331         Roo.LayoutRegion.prototype.updateBox.call(this, box);
34332     }
34333 });
34334
34335 Roo.WestLayoutRegion = function(mgr, config){
34336     Roo.SplitLayoutRegion.call(this, mgr, config, "west", "w-resize");
34337     if(this.split){
34338         this.split.placement = Roo.SplitBar.LEFT;
34339         this.split.orientation = Roo.SplitBar.HORIZONTAL;
34340         this.split.el.addClass("x-layout-split-h");
34341     }
34342     var size = config.initialSize || config.width;
34343     if(typeof size != "undefined"){
34344         this.el.setWidth(size);
34345     }
34346 };
34347 Roo.extend(Roo.WestLayoutRegion, Roo.SplitLayoutRegion, {
34348     orientation: Roo.SplitBar.HORIZONTAL,
34349     getBox : function(){
34350         if(this.collapsed){
34351             return this.collapsedEl.getBox();
34352         }
34353         var box = this.el.getBox();
34354         if(this.split){
34355             box.width += this.split.el.getWidth();
34356         }
34357         return box;
34358     },
34359     
34360     updateBox : function(box){
34361         if(this.split && !this.collapsed){
34362             var sw = this.split.el.getWidth();
34363             box.width -= sw;
34364             this.split.el.setLeft(box.x+box.width);
34365             this.split.el.setTop(box.y);
34366             this.split.el.setHeight(box.height);
34367         }
34368         if(this.collapsed){
34369             this.updateBody(null, box.height);
34370         }
34371         Roo.LayoutRegion.prototype.updateBox.call(this, box);
34372     }
34373 });
34374 /*
34375  * Based on:
34376  * Ext JS Library 1.1.1
34377  * Copyright(c) 2006-2007, Ext JS, LLC.
34378  *
34379  * Originally Released Under LGPL - original licence link has changed is not relivant.
34380  *
34381  * Fork - LGPL
34382  * <script type="text/javascript">
34383  */
34384  
34385  
34386 /*
34387  * Private internal class for reading and applying state
34388  */
34389 Roo.LayoutStateManager = function(layout){
34390      // default empty state
34391      this.state = {
34392         north: {},
34393         south: {},
34394         east: {},
34395         west: {}       
34396     };
34397 };
34398
34399 Roo.LayoutStateManager.prototype = {
34400     init : function(layout, provider){
34401         this.provider = provider;
34402         var state = provider.get(layout.id+"-layout-state");
34403         if(state){
34404             var wasUpdating = layout.isUpdating();
34405             if(!wasUpdating){
34406                 layout.beginUpdate();
34407             }
34408             for(var key in state){
34409                 if(typeof state[key] != "function"){
34410                     var rstate = state[key];
34411                     var r = layout.getRegion(key);
34412                     if(r && rstate){
34413                         if(rstate.size){
34414                             r.resizeTo(rstate.size);
34415                         }
34416                         if(rstate.collapsed == true){
34417                             r.collapse(true);
34418                         }else{
34419                             r.expand(null, true);
34420                         }
34421                     }
34422                 }
34423             }
34424             if(!wasUpdating){
34425                 layout.endUpdate();
34426             }
34427             this.state = state; 
34428         }
34429         this.layout = layout;
34430         layout.on("regionresized", this.onRegionResized, this);
34431         layout.on("regioncollapsed", this.onRegionCollapsed, this);
34432         layout.on("regionexpanded", this.onRegionExpanded, this);
34433     },
34434     
34435     storeState : function(){
34436         this.provider.set(this.layout.id+"-layout-state", this.state);
34437     },
34438     
34439     onRegionResized : function(region, newSize){
34440         this.state[region.getPosition()].size = newSize;
34441         this.storeState();
34442     },
34443     
34444     onRegionCollapsed : function(region){
34445         this.state[region.getPosition()].collapsed = true;
34446         this.storeState();
34447     },
34448     
34449     onRegionExpanded : function(region){
34450         this.state[region.getPosition()].collapsed = false;
34451         this.storeState();
34452     }
34453 };/*
34454  * Based on:
34455  * Ext JS Library 1.1.1
34456  * Copyright(c) 2006-2007, Ext JS, LLC.
34457  *
34458  * Originally Released Under LGPL - original licence link has changed is not relivant.
34459  *
34460  * Fork - LGPL
34461  * <script type="text/javascript">
34462  */
34463 /**
34464  * @class Roo.ContentPanel
34465  * @extends Roo.util.Observable
34466  * A basic ContentPanel element.
34467  * @cfg {Boolean}   fitToFrame    True for this panel to adjust its size to fit when the region resizes  (defaults to false)
34468  * @cfg {Boolean}   fitContainer   When using {@link #fitToFrame} and {@link #resizeEl}, you can also fit the parent container  (defaults to false)
34469  * @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
34470  * @cfg {Boolean}   closable      True if the panel can be closed/removed
34471  * @cfg {Boolean}   background    True if the panel should not be activated when it is added (defaults to false)
34472  * @cfg {String/HTMLElement/Element} resizeEl An element to resize if {@link #fitToFrame} is true (instead of this panel's element)
34473  * @cfg {Toolbar}   toolbar       A toolbar for this panel
34474  * @cfg {Boolean} autoScroll    True to scroll overflow in this panel (use with {@link #fitToFrame})
34475  * @cfg {String} title          The title for this panel
34476  * @cfg {Array} adjustments     Values to <b>add</b> to the width/height when doing a {@link #fitToFrame} (default is [0, 0])
34477  * @cfg {String} url            Calls {@link #setUrl} with this value
34478  * @cfg {String} region         (center|north|south|east|west) which region to put this panel on (when used with xtype constructors)
34479  * @cfg {String/Object} params  When used with {@link #url}, calls {@link #setUrl} with this value
34480  * @cfg {Boolean} loadOnce      When used with {@link #url}, calls {@link #setUrl} with this value
34481  * @cfg {String}    content        Raw content to fill content panel with (uses setContent on construction.)
34482
34483  * @constructor
34484  * Create a new ContentPanel.
34485  * @param {String/HTMLElement/Roo.Element} el The container element for this panel
34486  * @param {String/Object} config A string to set only the title or a config object
34487  * @param {String} content (optional) Set the HTML content for this panel
34488  * @param {String} region (optional) Used by xtype constructors to add to regions. (values center,east,west,south,north)
34489  */
34490 Roo.ContentPanel = function(el, config, content){
34491     
34492      
34493     /*
34494     if(el.autoCreate || el.xtype){ // xtype is available if this is called from factory
34495         config = el;
34496         el = Roo.id();
34497     }
34498     if (config && config.parentLayout) { 
34499         el = config.parentLayout.el.createChild(); 
34500     }
34501     */
34502     if(el.autoCreate){ // xtype is available if this is called from factory
34503         config = el;
34504         el = Roo.id();
34505     }
34506     this.el = Roo.get(el);
34507     if(!this.el && config && config.autoCreate){
34508         if(typeof config.autoCreate == "object"){
34509             if(!config.autoCreate.id){
34510                 config.autoCreate.id = config.id||el;
34511             }
34512             this.el = Roo.DomHelper.append(document.body,
34513                         config.autoCreate, true);
34514         }else{
34515             this.el = Roo.DomHelper.append(document.body,
34516                         {tag: "div", cls: "x-layout-inactive-content", id: config.id||el}, true);
34517         }
34518     }
34519     this.closable = false;
34520     this.loaded = false;
34521     this.active = false;
34522     if(typeof config == "string"){
34523         this.title = config;
34524     }else{
34525         Roo.apply(this, config);
34526     }
34527     
34528     if (this.toolbar && !this.toolbar.el && this.toolbar.xtype) {
34529         this.wrapEl = this.el.wrap();
34530         this.toolbar.container = this.el.insertSibling(false, 'before');
34531         this.toolbar = new Roo.Toolbar(this.toolbar);
34532     }
34533     
34534     // xtype created footer. - not sure if will work as we normally have to render first..
34535     if (this.footer && !this.footer.el && this.footer.xtype) {
34536         if (!this.wrapEl) {
34537             this.wrapEl = this.el.wrap();
34538         }
34539     
34540         this.footer.container = this.wrapEl.createChild();
34541          
34542         this.footer = Roo.factory(this.footer, Roo);
34543         
34544     }
34545     
34546     if(this.resizeEl){
34547         this.resizeEl = Roo.get(this.resizeEl, true);
34548     }else{
34549         this.resizeEl = this.el;
34550     }
34551     // handle view.xtype
34552     
34553  
34554     
34555     
34556     this.addEvents({
34557         /**
34558          * @event activate
34559          * Fires when this panel is activated. 
34560          * @param {Roo.ContentPanel} this
34561          */
34562         "activate" : true,
34563         /**
34564          * @event deactivate
34565          * Fires when this panel is activated. 
34566          * @param {Roo.ContentPanel} this
34567          */
34568         "deactivate" : true,
34569
34570         /**
34571          * @event resize
34572          * Fires when this panel is resized if fitToFrame is true.
34573          * @param {Roo.ContentPanel} this
34574          * @param {Number} width The width after any component adjustments
34575          * @param {Number} height The height after any component adjustments
34576          */
34577         "resize" : true,
34578         
34579          /**
34580          * @event render
34581          * Fires when this tab is created
34582          * @param {Roo.ContentPanel} this
34583          */
34584         "render" : true
34585         
34586         
34587         
34588     });
34589     
34590
34591     
34592     
34593     if(this.autoScroll){
34594         this.resizeEl.setStyle("overflow", "auto");
34595     } else {
34596         // fix randome scrolling
34597         this.el.on('scroll', function() {
34598             Roo.log('fix random scolling');
34599             this.scrollTo('top',0); 
34600         });
34601     }
34602     content = content || this.content;
34603     if(content){
34604         this.setContent(content);
34605     }
34606     if(config && config.url){
34607         this.setUrl(this.url, this.params, this.loadOnce);
34608     }
34609     
34610     
34611     
34612     Roo.ContentPanel.superclass.constructor.call(this);
34613     
34614     if (this.view && typeof(this.view.xtype) != 'undefined') {
34615         this.view.el = this.el.appendChild(document.createElement("div"));
34616         this.view = Roo.factory(this.view); 
34617         this.view.render  &&  this.view.render(false, '');  
34618     }
34619     
34620     
34621     this.fireEvent('render', this);
34622 };
34623
34624 Roo.extend(Roo.ContentPanel, Roo.util.Observable, {
34625     tabTip:'',
34626     setRegion : function(region){
34627         this.region = region;
34628         if(region){
34629            this.el.replaceClass("x-layout-inactive-content", "x-layout-active-content");
34630         }else{
34631            this.el.replaceClass("x-layout-active-content", "x-layout-inactive-content");
34632         } 
34633     },
34634     
34635     /**
34636      * Returns the toolbar for this Panel if one was configured. 
34637      * @return {Roo.Toolbar} 
34638      */
34639     getToolbar : function(){
34640         return this.toolbar;
34641     },
34642     
34643     setActiveState : function(active){
34644         this.active = active;
34645         if(!active){
34646             this.fireEvent("deactivate", this);
34647         }else{
34648             this.fireEvent("activate", this);
34649         }
34650     },
34651     /**
34652      * Updates this panel's element
34653      * @param {String} content The new content
34654      * @param {Boolean} loadScripts (optional) true to look for and process scripts
34655     */
34656     setContent : function(content, loadScripts){
34657         this.el.update(content, loadScripts);
34658     },
34659
34660     ignoreResize : function(w, h){
34661         if(this.lastSize && this.lastSize.width == w && this.lastSize.height == h){
34662             return true;
34663         }else{
34664             this.lastSize = {width: w, height: h};
34665             return false;
34666         }
34667     },
34668     /**
34669      * Get the {@link Roo.UpdateManager} for this panel. Enables you to perform Ajax updates.
34670      * @return {Roo.UpdateManager} The UpdateManager
34671      */
34672     getUpdateManager : function(){
34673         return this.el.getUpdateManager();
34674     },
34675      /**
34676      * Loads this content panel immediately with content from XHR. Note: to delay loading until the panel is activated, use {@link #setUrl}.
34677      * @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:
34678 <pre><code>
34679 panel.load({
34680     url: "your-url.php",
34681     params: {param1: "foo", param2: "bar"}, // or a URL encoded string
34682     callback: yourFunction,
34683     scope: yourObject, //(optional scope)
34684     discardUrl: false,
34685     nocache: false,
34686     text: "Loading...",
34687     timeout: 30,
34688     scripts: false
34689 });
34690 </code></pre>
34691      * The only required property is <i>url</i>. The optional properties <i>nocache</i>, <i>text</i> and <i>scripts</i>
34692      * 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.
34693      * @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}
34694      * @param {Function} callback (optional) Callback when transaction is complete -- called with signature (oElement, bSuccess, oResponse)
34695      * @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.
34696      * @return {Roo.ContentPanel} this
34697      */
34698     load : function(){
34699         var um = this.el.getUpdateManager();
34700         um.update.apply(um, arguments);
34701         return this;
34702     },
34703
34704
34705     /**
34706      * 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.
34707      * @param {String/Function} url The URL to load the content from or a function to call to get the URL
34708      * @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)
34709      * @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)
34710      * @return {Roo.UpdateManager} The UpdateManager
34711      */
34712     setUrl : function(url, params, loadOnce){
34713         if(this.refreshDelegate){
34714             this.removeListener("activate", this.refreshDelegate);
34715         }
34716         this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
34717         this.on("activate", this.refreshDelegate);
34718         return this.el.getUpdateManager();
34719     },
34720     
34721     _handleRefresh : function(url, params, loadOnce){
34722         if(!loadOnce || !this.loaded){
34723             var updater = this.el.getUpdateManager();
34724             updater.update(url, params, this._setLoaded.createDelegate(this));
34725         }
34726     },
34727     
34728     _setLoaded : function(){
34729         this.loaded = true;
34730     }, 
34731     
34732     /**
34733      * Returns this panel's id
34734      * @return {String} 
34735      */
34736     getId : function(){
34737         return this.el.id;
34738     },
34739     
34740     /** 
34741      * Returns this panel's element - used by regiosn to add.
34742      * @return {Roo.Element} 
34743      */
34744     getEl : function(){
34745         return this.wrapEl || this.el;
34746     },
34747     
34748     adjustForComponents : function(width, height)
34749     {
34750         //Roo.log('adjustForComponents ');
34751         if(this.resizeEl != this.el){
34752             width -= this.el.getFrameWidth('lr');
34753             height -= this.el.getFrameWidth('tb');
34754         }
34755         if(this.toolbar){
34756             var te = this.toolbar.getEl();
34757             height -= te.getHeight();
34758             te.setWidth(width);
34759         }
34760         if(this.footer){
34761             var te = this.footer.getEl();
34762             Roo.log("footer:" + te.getHeight());
34763             
34764             height -= te.getHeight();
34765             te.setWidth(width);
34766         }
34767         
34768         
34769         if(this.adjustments){
34770             width += this.adjustments[0];
34771             height += this.adjustments[1];
34772         }
34773         return {"width": width, "height": height};
34774     },
34775     
34776     setSize : function(width, height){
34777         if(this.fitToFrame && !this.ignoreResize(width, height)){
34778             if(this.fitContainer && this.resizeEl != this.el){
34779                 this.el.setSize(width, height);
34780             }
34781             var size = this.adjustForComponents(width, height);
34782             this.resizeEl.setSize(this.autoWidth ? "auto" : size.width, this.autoHeight ? "auto" : size.height);
34783             this.fireEvent('resize', this, size.width, size.height);
34784         }
34785     },
34786     
34787     /**
34788      * Returns this panel's title
34789      * @return {String} 
34790      */
34791     getTitle : function(){
34792         return this.title;
34793     },
34794     
34795     /**
34796      * Set this panel's title
34797      * @param {String} title
34798      */
34799     setTitle : function(title){
34800         this.title = title;
34801         if(this.region){
34802             this.region.updatePanelTitle(this, title);
34803         }
34804     },
34805     
34806     /**
34807      * Returns true is this panel was configured to be closable
34808      * @return {Boolean} 
34809      */
34810     isClosable : function(){
34811         return this.closable;
34812     },
34813     
34814     beforeSlide : function(){
34815         this.el.clip();
34816         this.resizeEl.clip();
34817     },
34818     
34819     afterSlide : function(){
34820         this.el.unclip();
34821         this.resizeEl.unclip();
34822     },
34823     
34824     /**
34825      *   Force a content refresh from the URL specified in the {@link #setUrl} method.
34826      *   Will fail silently if the {@link #setUrl} method has not been called.
34827      *   This does not activate the panel, just updates its content.
34828      */
34829     refresh : function(){
34830         if(this.refreshDelegate){
34831            this.loaded = false;
34832            this.refreshDelegate();
34833         }
34834     },
34835     
34836     /**
34837      * Destroys this panel
34838      */
34839     destroy : function(){
34840         this.el.removeAllListeners();
34841         var tempEl = document.createElement("span");
34842         tempEl.appendChild(this.el.dom);
34843         tempEl.innerHTML = "";
34844         this.el.remove();
34845         this.el = null;
34846     },
34847     
34848     /**
34849      * form - if the content panel contains a form - this is a reference to it.
34850      * @type {Roo.form.Form}
34851      */
34852     form : false,
34853     /**
34854      * view - if the content panel contains a view (Roo.DatePicker / Roo.View / Roo.JsonView)
34855      *    This contains a reference to it.
34856      * @type {Roo.View}
34857      */
34858     view : false,
34859     
34860       /**
34861      * Adds a xtype elements to the panel - currently only supports Forms, View, JsonView.
34862      * <pre><code>
34863
34864 layout.addxtype({
34865        xtype : 'Form',
34866        items: [ .... ]
34867    }
34868 );
34869
34870 </code></pre>
34871      * @param {Object} cfg Xtype definition of item to add.
34872      */
34873     
34874     addxtype : function(cfg) {
34875         // add form..
34876         if (cfg.xtype.match(/^Form$/)) {
34877             
34878             var el;
34879             //if (this.footer) {
34880             //    el = this.footer.container.insertSibling(false, 'before');
34881             //} else {
34882                 el = this.el.createChild();
34883             //}
34884
34885             this.form = new  Roo.form.Form(cfg);
34886             
34887             
34888             if ( this.form.allItems.length) this.form.render(el.dom);
34889             return this.form;
34890         }
34891         // should only have one of theses..
34892         if ([ 'View', 'JsonView', 'DatePicker'].indexOf(cfg.xtype) > -1) {
34893             // views.. should not be just added - used named prop 'view''
34894             
34895             cfg.el = this.el.appendChild(document.createElement("div"));
34896             // factory?
34897             
34898             var ret = new Roo.factory(cfg);
34899              
34900              ret.render && ret.render(false, ''); // render blank..
34901             this.view = ret;
34902             return ret;
34903         }
34904         return false;
34905     }
34906 });
34907
34908 /**
34909  * @class Roo.GridPanel
34910  * @extends Roo.ContentPanel
34911  * @constructor
34912  * Create a new GridPanel.
34913  * @param {Roo.grid.Grid} grid The grid for this panel
34914  * @param {String/Object} config A string to set only the panel's title, or a config object
34915  */
34916 Roo.GridPanel = function(grid, config){
34917     
34918   
34919     this.wrapper = Roo.DomHelper.append(document.body, // wrapper for IE7 strict & safari scroll issue
34920         {tag: "div", cls: "x-layout-grid-wrapper x-layout-inactive-content"}, true);
34921         
34922     this.wrapper.dom.appendChild(grid.getGridEl().dom);
34923     
34924     Roo.GridPanel.superclass.constructor.call(this, this.wrapper, config);
34925     
34926     if(this.toolbar){
34927         this.toolbar.el.insertBefore(this.wrapper.dom.firstChild);
34928     }
34929     // xtype created footer. - not sure if will work as we normally have to render first..
34930     if (this.footer && !this.footer.el && this.footer.xtype) {
34931         
34932         this.footer.container = this.grid.getView().getFooterPanel(true);
34933         this.footer.dataSource = this.grid.dataSource;
34934         this.footer = Roo.factory(this.footer, Roo);
34935         
34936     }
34937     
34938     grid.monitorWindowResize = false; // turn off autosizing
34939     grid.autoHeight = false;
34940     grid.autoWidth = false;
34941     this.grid = grid;
34942     this.grid.getGridEl().replaceClass("x-layout-inactive-content", "x-layout-component-panel");
34943 };
34944
34945 Roo.extend(Roo.GridPanel, Roo.ContentPanel, {
34946     getId : function(){
34947         return this.grid.id;
34948     },
34949     
34950     /**
34951      * Returns the grid for this panel
34952      * @return {Roo.grid.Grid} 
34953      */
34954     getGrid : function(){
34955         return this.grid;    
34956     },
34957     
34958     setSize : function(width, height){
34959         if(!this.ignoreResize(width, height)){
34960             var grid = this.grid;
34961             var size = this.adjustForComponents(width, height);
34962             grid.getGridEl().setSize(size.width, size.height);
34963             grid.autoSize();
34964         }
34965     },
34966     
34967     beforeSlide : function(){
34968         this.grid.getView().scroller.clip();
34969     },
34970     
34971     afterSlide : function(){
34972         this.grid.getView().scroller.unclip();
34973     },
34974     
34975     destroy : function(){
34976         this.grid.destroy();
34977         delete this.grid;
34978         Roo.GridPanel.superclass.destroy.call(this); 
34979     }
34980 });
34981
34982
34983 /**
34984  * @class Roo.NestedLayoutPanel
34985  * @extends Roo.ContentPanel
34986  * @constructor
34987  * Create a new NestedLayoutPanel.
34988  * 
34989  * 
34990  * @param {Roo.BorderLayout} layout The layout for this panel
34991  * @param {String/Object} config A string to set only the title or a config object
34992  */
34993 Roo.NestedLayoutPanel = function(layout, config)
34994 {
34995     // construct with only one argument..
34996     /* FIXME - implement nicer consturctors
34997     if (layout.layout) {
34998         config = layout;
34999         layout = config.layout;
35000         delete config.layout;
35001     }
35002     if (layout.xtype && !layout.getEl) {
35003         // then layout needs constructing..
35004         layout = Roo.factory(layout, Roo);
35005     }
35006     */
35007     
35008     
35009     Roo.NestedLayoutPanel.superclass.constructor.call(this, layout.getEl(), config);
35010     
35011     layout.monitorWindowResize = false; // turn off autosizing
35012     this.layout = layout;
35013     this.layout.getEl().addClass("x-layout-nested-layout");
35014     
35015     
35016     
35017     
35018 };
35019
35020 Roo.extend(Roo.NestedLayoutPanel, Roo.ContentPanel, {
35021
35022     setSize : function(width, height){
35023         if(!this.ignoreResize(width, height)){
35024             var size = this.adjustForComponents(width, height);
35025             var el = this.layout.getEl();
35026             el.setSize(size.width, size.height);
35027             var touch = el.dom.offsetWidth;
35028             this.layout.layout();
35029             // ie requires a double layout on the first pass
35030             if(Roo.isIE && !this.initialized){
35031                 this.initialized = true;
35032                 this.layout.layout();
35033             }
35034         }
35035     },
35036     
35037     // activate all subpanels if not currently active..
35038     
35039     setActiveState : function(active){
35040         this.active = active;
35041         if(!active){
35042             this.fireEvent("deactivate", this);
35043             return;
35044         }
35045         
35046         this.fireEvent("activate", this);
35047         // not sure if this should happen before or after..
35048         if (!this.layout) {
35049             return; // should not happen..
35050         }
35051         var reg = false;
35052         for (var r in this.layout.regions) {
35053             reg = this.layout.getRegion(r);
35054             if (reg.getActivePanel()) {
35055                 //reg.showPanel(reg.getActivePanel()); // force it to activate.. 
35056                 reg.setActivePanel(reg.getActivePanel());
35057                 continue;
35058             }
35059             if (!reg.panels.length) {
35060                 continue;
35061             }
35062             reg.showPanel(reg.getPanel(0));
35063         }
35064         
35065         
35066         
35067         
35068     },
35069     
35070     /**
35071      * Returns the nested BorderLayout for this panel
35072      * @return {Roo.BorderLayout} 
35073      */
35074     getLayout : function(){
35075         return this.layout;
35076     },
35077     
35078      /**
35079      * Adds a xtype elements to the layout of the nested panel
35080      * <pre><code>
35081
35082 panel.addxtype({
35083        xtype : 'ContentPanel',
35084        region: 'west',
35085        items: [ .... ]
35086    }
35087 );
35088
35089 panel.addxtype({
35090         xtype : 'NestedLayoutPanel',
35091         region: 'west',
35092         layout: {
35093            center: { },
35094            west: { }   
35095         },
35096         items : [ ... list of content panels or nested layout panels.. ]
35097    }
35098 );
35099 </code></pre>
35100      * @param {Object} cfg Xtype definition of item to add.
35101      */
35102     addxtype : function(cfg) {
35103         return this.layout.addxtype(cfg);
35104     
35105     }
35106 });
35107
35108 Roo.ScrollPanel = function(el, config, content){
35109     config = config || {};
35110     config.fitToFrame = true;
35111     Roo.ScrollPanel.superclass.constructor.call(this, el, config, content);
35112     
35113     this.el.dom.style.overflow = "hidden";
35114     var wrap = this.el.wrap({cls: "x-scroller x-layout-inactive-content"});
35115     this.el.removeClass("x-layout-inactive-content");
35116     this.el.on("mousewheel", this.onWheel, this);
35117
35118     var up = wrap.createChild({cls: "x-scroller-up", html: "&#160;"}, this.el.dom);
35119     var down = wrap.createChild({cls: "x-scroller-down", html: "&#160;"});
35120     up.unselectable(); down.unselectable();
35121     up.on("click", this.scrollUp, this);
35122     down.on("click", this.scrollDown, this);
35123     up.addClassOnOver("x-scroller-btn-over");
35124     down.addClassOnOver("x-scroller-btn-over");
35125     up.addClassOnClick("x-scroller-btn-click");
35126     down.addClassOnClick("x-scroller-btn-click");
35127     this.adjustments = [0, -(up.getHeight() + down.getHeight())];
35128
35129     this.resizeEl = this.el;
35130     this.el = wrap; this.up = up; this.down = down;
35131 };
35132
35133 Roo.extend(Roo.ScrollPanel, Roo.ContentPanel, {
35134     increment : 100,
35135     wheelIncrement : 5,
35136     scrollUp : function(){
35137         this.resizeEl.scroll("up", this.increment, {callback: this.afterScroll, scope: this});
35138     },
35139
35140     scrollDown : function(){
35141         this.resizeEl.scroll("down", this.increment, {callback: this.afterScroll, scope: this});
35142     },
35143
35144     afterScroll : function(){
35145         var el = this.resizeEl;
35146         var t = el.dom.scrollTop, h = el.dom.scrollHeight, ch = el.dom.clientHeight;
35147         this.up[t == 0 ? "addClass" : "removeClass"]("x-scroller-btn-disabled");
35148         this.down[h - t <= ch ? "addClass" : "removeClass"]("x-scroller-btn-disabled");
35149     },
35150
35151     setSize : function(){
35152         Roo.ScrollPanel.superclass.setSize.apply(this, arguments);
35153         this.afterScroll();
35154     },
35155
35156     onWheel : function(e){
35157         var d = e.getWheelDelta();
35158         this.resizeEl.dom.scrollTop -= (d*this.wheelIncrement);
35159         this.afterScroll();
35160         e.stopEvent();
35161     },
35162
35163     setContent : function(content, loadScripts){
35164         this.resizeEl.update(content, loadScripts);
35165     }
35166
35167 });
35168
35169
35170
35171
35172
35173
35174
35175
35176
35177 /**
35178  * @class Roo.TreePanel
35179  * @extends Roo.ContentPanel
35180  * @constructor
35181  * Create a new TreePanel. - defaults to fit/scoll contents.
35182  * @param {String/Object} config A string to set only the panel's title, or a config object
35183  * @cfg {Roo.tree.TreePanel} tree The tree TreePanel, with config etc.
35184  */
35185 Roo.TreePanel = function(config){
35186     var el = config.el;
35187     var tree = config.tree;
35188     delete config.tree; 
35189     delete config.el; // hopefull!
35190     
35191     // wrapper for IE7 strict & safari scroll issue
35192     
35193     var treeEl = el.createChild();
35194     config.resizeEl = treeEl;
35195     
35196     
35197     
35198     Roo.TreePanel.superclass.constructor.call(this, el, config);
35199  
35200  
35201     this.tree = new Roo.tree.TreePanel(treeEl , tree);
35202     //console.log(tree);
35203     this.on('activate', function()
35204     {
35205         if (this.tree.rendered) {
35206             return;
35207         }
35208         //console.log('render tree');
35209         this.tree.render();
35210     });
35211     // this should not be needed.. - it's actually the 'el' that resizes?
35212     // actuall it breaks the containerScroll - dragging nodes auto scroll at top
35213     
35214     //this.on('resize',  function (cp, w, h) {
35215     //        this.tree.innerCt.setWidth(w);
35216     //        this.tree.innerCt.setHeight(h);
35217     //        //this.tree.innerCt.setStyle('overflow-y', 'auto');
35218     //});
35219
35220         
35221     
35222 };
35223
35224 Roo.extend(Roo.TreePanel, Roo.ContentPanel, {   
35225     fitToFrame : true,
35226     autoScroll : true
35227 });
35228
35229
35230
35231
35232
35233
35234
35235
35236
35237
35238
35239 /*
35240  * Based on:
35241  * Ext JS Library 1.1.1
35242  * Copyright(c) 2006-2007, Ext JS, LLC.
35243  *
35244  * Originally Released Under LGPL - original licence link has changed is not relivant.
35245  *
35246  * Fork - LGPL
35247  * <script type="text/javascript">
35248  */
35249  
35250
35251 /**
35252  * @class Roo.ReaderLayout
35253  * @extends Roo.BorderLayout
35254  * This is a pre-built layout that represents a classic, 5-pane application.  It consists of a header, a primary
35255  * center region containing two nested regions (a top one for a list view and one for item preview below),
35256  * and regions on either side that can be used for navigation, application commands, informational displays, etc.
35257  * The setup and configuration work exactly the same as it does for a {@link Roo.BorderLayout} - this class simply
35258  * expedites the setup of the overall layout and regions for this common application style.
35259  * Example:
35260  <pre><code>
35261 var reader = new Roo.ReaderLayout();
35262 var CP = Roo.ContentPanel;  // shortcut for adding
35263
35264 reader.beginUpdate();
35265 reader.add("north", new CP("north", "North"));
35266 reader.add("west", new CP("west", {title: "West"}));
35267 reader.add("east", new CP("east", {title: "East"}));
35268
35269 reader.regions.listView.add(new CP("listView", "List"));
35270 reader.regions.preview.add(new CP("preview", "Preview"));
35271 reader.endUpdate();
35272 </code></pre>
35273 * @constructor
35274 * Create a new ReaderLayout
35275 * @param {Object} config Configuration options
35276 * @param {String/HTMLElement/Element} container (optional) The container this layout is bound to (defaults to
35277 * document.body if omitted)
35278 */
35279 Roo.ReaderLayout = function(config, renderTo){
35280     var c = config || {size:{}};
35281     Roo.ReaderLayout.superclass.constructor.call(this, renderTo || document.body, {
35282         north: c.north !== false ? Roo.apply({
35283             split:false,
35284             initialSize: 32,
35285             titlebar: false
35286         }, c.north) : false,
35287         west: c.west !== false ? Roo.apply({
35288             split:true,
35289             initialSize: 200,
35290             minSize: 175,
35291             maxSize: 400,
35292             titlebar: true,
35293             collapsible: true,
35294             animate: true,
35295             margins:{left:5,right:0,bottom:5,top:5},
35296             cmargins:{left:5,right:5,bottom:5,top:5}
35297         }, c.west) : false,
35298         east: c.east !== false ? Roo.apply({
35299             split:true,
35300             initialSize: 200,
35301             minSize: 175,
35302             maxSize: 400,
35303             titlebar: true,
35304             collapsible: true,
35305             animate: true,
35306             margins:{left:0,right:5,bottom:5,top:5},
35307             cmargins:{left:5,right:5,bottom:5,top:5}
35308         }, c.east) : false,
35309         center: Roo.apply({
35310             tabPosition: 'top',
35311             autoScroll:false,
35312             closeOnTab: true,
35313             titlebar:false,
35314             margins:{left:c.west!==false ? 0 : 5,right:c.east!==false ? 0 : 5,bottom:5,top:2}
35315         }, c.center)
35316     });
35317
35318     this.el.addClass('x-reader');
35319
35320     this.beginUpdate();
35321
35322     var inner = new Roo.BorderLayout(Roo.get(document.body).createChild(), {
35323         south: c.preview !== false ? Roo.apply({
35324             split:true,
35325             initialSize: 200,
35326             minSize: 100,
35327             autoScroll:true,
35328             collapsible:true,
35329             titlebar: true,
35330             cmargins:{top:5,left:0, right:0, bottom:0}
35331         }, c.preview) : false,
35332         center: Roo.apply({
35333             autoScroll:false,
35334             titlebar:false,
35335             minHeight:200
35336         }, c.listView)
35337     });
35338     this.add('center', new Roo.NestedLayoutPanel(inner,
35339             Roo.apply({title: c.mainTitle || '',tabTip:''},c.innerPanelCfg)));
35340
35341     this.endUpdate();
35342
35343     this.regions.preview = inner.getRegion('south');
35344     this.regions.listView = inner.getRegion('center');
35345 };
35346
35347 Roo.extend(Roo.ReaderLayout, Roo.BorderLayout);/*
35348  * Based on:
35349  * Ext JS Library 1.1.1
35350  * Copyright(c) 2006-2007, Ext JS, LLC.
35351  *
35352  * Originally Released Under LGPL - original licence link has changed is not relivant.
35353  *
35354  * Fork - LGPL
35355  * <script type="text/javascript">
35356  */
35357  
35358 /**
35359  * @class Roo.grid.Grid
35360  * @extends Roo.util.Observable
35361  * This class represents the primary interface of a component based grid control.
35362  * <br><br>Usage:<pre><code>
35363  var grid = new Roo.grid.Grid("my-container-id", {
35364      ds: myDataStore,
35365      cm: myColModel,
35366      selModel: mySelectionModel,
35367      autoSizeColumns: true,
35368      monitorWindowResize: false,
35369      trackMouseOver: true
35370  });
35371  // set any options
35372  grid.render();
35373  * </code></pre>
35374  * <b>Common Problems:</b><br/>
35375  * - Grid does not resize properly when going smaller: Setting overflow hidden on the container
35376  * element will correct this<br/>
35377  * - If you get el.style[camel]= NaNpx or -2px or something related, be certain you have given your container element
35378  * dimensions. The grid adapts to your container's size, if your container has no size defined then the results
35379  * are unpredictable.<br/>
35380  * - Do not render the grid into an element with display:none. Try using visibility:hidden. Otherwise there is no way for the
35381  * grid to calculate dimensions/offsets.<br/>
35382   * @constructor
35383  * @param {String/HTMLElement/Roo.Element} container The element into which this grid will be rendered -
35384  * The container MUST have some type of size defined for the grid to fill. The container will be
35385  * automatically set to position relative if it isn't already.
35386  * @param {Object} config A config object that sets properties on this grid.
35387  */
35388 Roo.grid.Grid = function(container, config){
35389         // initialize the container
35390         this.container = Roo.get(container);
35391         this.container.update("");
35392         this.container.setStyle("overflow", "hidden");
35393     this.container.addClass('x-grid-container');
35394
35395     this.id = this.container.id;
35396
35397     Roo.apply(this, config);
35398     // check and correct shorthanded configs
35399     if(this.ds){
35400         this.dataSource = this.ds;
35401         delete this.ds;
35402     }
35403     if(this.cm){
35404         this.colModel = this.cm;
35405         delete this.cm;
35406     }
35407     if(this.sm){
35408         this.selModel = this.sm;
35409         delete this.sm;
35410     }
35411
35412     if (this.selModel) {
35413         this.selModel = Roo.factory(this.selModel, Roo.grid);
35414         this.sm = this.selModel;
35415         this.sm.xmodule = this.xmodule || false;
35416     }
35417     if (typeof(this.colModel.config) == 'undefined') {
35418         this.colModel = new Roo.grid.ColumnModel(this.colModel);
35419         this.cm = this.colModel;
35420         this.cm.xmodule = this.xmodule || false;
35421     }
35422     if (this.dataSource) {
35423         this.dataSource= Roo.factory(this.dataSource, Roo.data);
35424         this.ds = this.dataSource;
35425         this.ds.xmodule = this.xmodule || false;
35426          
35427     }
35428     
35429     
35430     
35431     if(this.width){
35432         this.container.setWidth(this.width);
35433     }
35434
35435     if(this.height){
35436         this.container.setHeight(this.height);
35437     }
35438     /** @private */
35439         this.addEvents({
35440         // raw events
35441         /**
35442          * @event click
35443          * The raw click event for the entire grid.
35444          * @param {Roo.EventObject} e
35445          */
35446         "click" : true,
35447         /**
35448          * @event dblclick
35449          * The raw dblclick event for the entire grid.
35450          * @param {Roo.EventObject} e
35451          */
35452         "dblclick" : true,
35453         /**
35454          * @event contextmenu
35455          * The raw contextmenu event for the entire grid.
35456          * @param {Roo.EventObject} e
35457          */
35458         "contextmenu" : true,
35459         /**
35460          * @event mousedown
35461          * The raw mousedown event for the entire grid.
35462          * @param {Roo.EventObject} e
35463          */
35464         "mousedown" : true,
35465         /**
35466          * @event mouseup
35467          * The raw mouseup event for the entire grid.
35468          * @param {Roo.EventObject} e
35469          */
35470         "mouseup" : true,
35471         /**
35472          * @event mouseover
35473          * The raw mouseover event for the entire grid.
35474          * @param {Roo.EventObject} e
35475          */
35476         "mouseover" : true,
35477         /**
35478          * @event mouseout
35479          * The raw mouseout event for the entire grid.
35480          * @param {Roo.EventObject} e
35481          */
35482         "mouseout" : true,
35483         /**
35484          * @event keypress
35485          * The raw keypress event for the entire grid.
35486          * @param {Roo.EventObject} e
35487          */
35488         "keypress" : true,
35489         /**
35490          * @event keydown
35491          * The raw keydown event for the entire grid.
35492          * @param {Roo.EventObject} e
35493          */
35494         "keydown" : true,
35495
35496         // custom events
35497
35498         /**
35499          * @event cellclick
35500          * Fires when a cell is clicked
35501          * @param {Grid} this
35502          * @param {Number} rowIndex
35503          * @param {Number} columnIndex
35504          * @param {Roo.EventObject} e
35505          */
35506         "cellclick" : true,
35507         /**
35508          * @event celldblclick
35509          * Fires when a cell is double clicked
35510          * @param {Grid} this
35511          * @param {Number} rowIndex
35512          * @param {Number} columnIndex
35513          * @param {Roo.EventObject} e
35514          */
35515         "celldblclick" : true,
35516         /**
35517          * @event rowclick
35518          * Fires when a row is clicked
35519          * @param {Grid} this
35520          * @param {Number} rowIndex
35521          * @param {Roo.EventObject} e
35522          */
35523         "rowclick" : true,
35524         /**
35525          * @event rowdblclick
35526          * Fires when a row is double clicked
35527          * @param {Grid} this
35528          * @param {Number} rowIndex
35529          * @param {Roo.EventObject} e
35530          */
35531         "rowdblclick" : true,
35532         /**
35533          * @event headerclick
35534          * Fires when a header is clicked
35535          * @param {Grid} this
35536          * @param {Number} columnIndex
35537          * @param {Roo.EventObject} e
35538          */
35539         "headerclick" : true,
35540         /**
35541          * @event headerdblclick
35542          * Fires when a header cell is double clicked
35543          * @param {Grid} this
35544          * @param {Number} columnIndex
35545          * @param {Roo.EventObject} e
35546          */
35547         "headerdblclick" : true,
35548         /**
35549          * @event rowcontextmenu
35550          * Fires when a row is right clicked
35551          * @param {Grid} this
35552          * @param {Number} rowIndex
35553          * @param {Roo.EventObject} e
35554          */
35555         "rowcontextmenu" : true,
35556         /**
35557          * @event cellcontextmenu
35558          * Fires when a cell is right clicked
35559          * @param {Grid} this
35560          * @param {Number} rowIndex
35561          * @param {Number} cellIndex
35562          * @param {Roo.EventObject} e
35563          */
35564          "cellcontextmenu" : true,
35565         /**
35566          * @event headercontextmenu
35567          * Fires when a header is right clicked
35568          * @param {Grid} this
35569          * @param {Number} columnIndex
35570          * @param {Roo.EventObject} e
35571          */
35572         "headercontextmenu" : true,
35573         /**
35574          * @event bodyscroll
35575          * Fires when the body element is scrolled
35576          * @param {Number} scrollLeft
35577          * @param {Number} scrollTop
35578          */
35579         "bodyscroll" : true,
35580         /**
35581          * @event columnresize
35582          * Fires when the user resizes a column
35583          * @param {Number} columnIndex
35584          * @param {Number} newSize
35585          */
35586         "columnresize" : true,
35587         /**
35588          * @event columnmove
35589          * Fires when the user moves a column
35590          * @param {Number} oldIndex
35591          * @param {Number} newIndex
35592          */
35593         "columnmove" : true,
35594         /**
35595          * @event startdrag
35596          * Fires when row(s) start being dragged
35597          * @param {Grid} this
35598          * @param {Roo.GridDD} dd The drag drop object
35599          * @param {event} e The raw browser event
35600          */
35601         "startdrag" : true,
35602         /**
35603          * @event enddrag
35604          * Fires when a drag operation is complete
35605          * @param {Grid} this
35606          * @param {Roo.GridDD} dd The drag drop object
35607          * @param {event} e The raw browser event
35608          */
35609         "enddrag" : true,
35610         /**
35611          * @event dragdrop
35612          * Fires when dragged row(s) are dropped on a valid DD target
35613          * @param {Grid} this
35614          * @param {Roo.GridDD} dd The drag drop object
35615          * @param {String} targetId The target drag drop object
35616          * @param {event} e The raw browser event
35617          */
35618         "dragdrop" : true,
35619         /**
35620          * @event dragover
35621          * Fires while row(s) are being dragged. "targetId" is the id of the Yahoo.util.DD object the selected rows are being dragged over.
35622          * @param {Grid} this
35623          * @param {Roo.GridDD} dd The drag drop object
35624          * @param {String} targetId The target drag drop object
35625          * @param {event} e The raw browser event
35626          */
35627         "dragover" : true,
35628         /**
35629          * @event dragenter
35630          *  Fires when the dragged row(s) first cross another DD target while being dragged
35631          * @param {Grid} this
35632          * @param {Roo.GridDD} dd The drag drop object
35633          * @param {String} targetId The target drag drop object
35634          * @param {event} e The raw browser event
35635          */
35636         "dragenter" : true,
35637         /**
35638          * @event dragout
35639          * Fires when the dragged row(s) leave another DD target while being dragged
35640          * @param {Grid} this
35641          * @param {Roo.GridDD} dd The drag drop object
35642          * @param {String} targetId The target drag drop object
35643          * @param {event} e The raw browser event
35644          */
35645         "dragout" : true,
35646         /**
35647          * @event rowclass
35648          * Fires when a row is rendered, so you can change add a style to it.
35649          * @param {GridView} gridview   The grid view
35650          * @param {Object} rowcfg   contains record  rowIndex and rowClass - set rowClass to add a style.
35651          */
35652         'rowclass' : true,
35653
35654         /**
35655          * @event render
35656          * Fires when the grid is rendered
35657          * @param {Grid} grid
35658          */
35659         'render' : true
35660     });
35661
35662     Roo.grid.Grid.superclass.constructor.call(this);
35663 };
35664 Roo.extend(Roo.grid.Grid, Roo.util.Observable, {
35665     
35666     /**
35667      * @cfg {String} ddGroup - drag drop group.
35668      */
35669
35670     /**
35671      * @cfg {Number} minColumnWidth The minimum width a column can be resized to. Default is 25.
35672      */
35673     minColumnWidth : 25,
35674
35675     /**
35676      * @cfg {Boolean} autoSizeColumns True to automatically resize the columns to fit their content
35677      * <b>on initial render.</b> It is more efficient to explicitly size the columns
35678      * through the ColumnModel's {@link Roo.grid.ColumnModel#width} config option.  Default is false.
35679      */
35680     autoSizeColumns : false,
35681
35682     /**
35683      * @cfg {Boolean} autoSizeHeaders True to measure headers with column data when auto sizing columns. Default is true.
35684      */
35685     autoSizeHeaders : true,
35686
35687     /**
35688      * @cfg {Boolean} monitorWindowResize True to autoSize the grid when the window resizes. Default is true.
35689      */
35690     monitorWindowResize : true,
35691
35692     /**
35693      * @cfg {Boolean} maxRowsToMeasure If autoSizeColumns is on, maxRowsToMeasure can be used to limit the number of
35694      * rows measured to get a columns size. Default is 0 (all rows).
35695      */
35696     maxRowsToMeasure : 0,
35697
35698     /**
35699      * @cfg {Boolean} trackMouseOver True to highlight rows when the mouse is over. Default is true.
35700      */
35701     trackMouseOver : true,
35702
35703     /**
35704     * @cfg {Boolean} enableDrag  True to enable drag of rows. Default is false. (double check if this is needed?)
35705     */
35706     
35707     /**
35708     * @cfg {Boolean} enableDragDrop True to enable drag and drop of rows. Default is false.
35709     */
35710     enableDragDrop : false,
35711     
35712     /**
35713     * @cfg {Boolean} enableColumnMove True to enable drag and drop reorder of columns. Default is true.
35714     */
35715     enableColumnMove : true,
35716     
35717     /**
35718     * @cfg {Boolean} enableColumnHide True to enable hiding of columns with the header context menu. Default is true.
35719     */
35720     enableColumnHide : true,
35721     
35722     /**
35723     * @cfg {Boolean} enableRowHeightSync True to manually sync row heights across locked and not locked rows. Default is false.
35724     */
35725     enableRowHeightSync : false,
35726     
35727     /**
35728     * @cfg {Boolean} stripeRows True to stripe the rows.  Default is true.
35729     */
35730     stripeRows : true,
35731     
35732     /**
35733     * @cfg {Boolean} autoHeight True to fit the height of the grid container to the height of the data. Default is false.
35734     */
35735     autoHeight : false,
35736
35737     /**
35738      * @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.
35739      */
35740     autoExpandColumn : false,
35741
35742     /**
35743     * @cfg {Number} autoExpandMin The minimum width the autoExpandColumn can have (if enabled).
35744     * Default is 50.
35745     */
35746     autoExpandMin : 50,
35747
35748     /**
35749     * @cfg {Number} autoExpandMax The maximum width the autoExpandColumn can have (if enabled). Default is 1000.
35750     */
35751     autoExpandMax : 1000,
35752
35753     /**
35754     * @cfg {Object} view The {@link Roo.grid.GridView} used by the grid. This can be set before a call to render().
35755     */
35756     view : null,
35757
35758     /**
35759     * @cfg {Object} loadMask An {@link Roo.LoadMask} config or true to mask the grid while loading. Default is false.
35760     */
35761     loadMask : false,
35762     /**
35763     * @cfg {Roo.dd.DropTarget} dropTarget An {@link Roo.dd.DropTarget} config
35764     */
35765     dropTarget: false,
35766     
35767    
35768     
35769     // private
35770     rendered : false,
35771
35772     /**
35773     * @cfg {Boolean} autoWidth True to set the grid's width to the default total width of the grid's columns instead
35774     * of a fixed width. Default is false.
35775     */
35776     /**
35777     * @cfg {Number} maxHeight Sets the maximum height of the grid - ignored if autoHeight is not on.
35778     */
35779     /**
35780      * Called once after all setup has been completed and the grid is ready to be rendered.
35781      * @return {Roo.grid.Grid} this
35782      */
35783     render : function()
35784     {
35785         var c = this.container;
35786         // try to detect autoHeight/width mode
35787         if((!c.dom.offsetHeight || c.dom.offsetHeight < 20) || c.getStyle("height") == "auto"){
35788             this.autoHeight = true;
35789         }
35790         var view = this.getView();
35791         view.init(this);
35792
35793         c.on("click", this.onClick, this);
35794         c.on("dblclick", this.onDblClick, this);
35795         c.on("contextmenu", this.onContextMenu, this);
35796         c.on("keydown", this.onKeyDown, this);
35797         if (Roo.isTouch) {
35798             c.on("touchstart", this.onTouchStart, this);
35799         }
35800
35801         this.relayEvents(c, ["mousedown","mouseup","mouseover","mouseout","keypress"]);
35802
35803         this.getSelectionModel().init(this);
35804
35805         view.render();
35806
35807         if(this.loadMask){
35808             this.loadMask = new Roo.LoadMask(this.container,
35809                     Roo.apply({store:this.dataSource}, this.loadMask));
35810         }
35811         
35812         
35813         if (this.toolbar && this.toolbar.xtype) {
35814             this.toolbar.container = this.getView().getHeaderPanel(true);
35815             this.toolbar = new Roo.Toolbar(this.toolbar);
35816         }
35817         if (this.footer && this.footer.xtype) {
35818             this.footer.dataSource = this.getDataSource();
35819             this.footer.container = this.getView().getFooterPanel(true);
35820             this.footer = Roo.factory(this.footer, Roo);
35821         }
35822         if (this.dropTarget && this.dropTarget.xtype) {
35823             delete this.dropTarget.xtype;
35824             this.dropTarget =  new Roo.dd.DropTarget(this.getView().mainBody, this.dropTarget);
35825         }
35826         
35827         
35828         this.rendered = true;
35829         this.fireEvent('render', this);
35830         return this;
35831     },
35832
35833         /**
35834          * Reconfigures the grid to use a different Store and Column Model.
35835          * The View will be bound to the new objects and refreshed.
35836          * @param {Roo.data.Store} dataSource The new {@link Roo.data.Store} object
35837          * @param {Roo.grid.ColumnModel} The new {@link Roo.grid.ColumnModel} object
35838          */
35839     reconfigure : function(dataSource, colModel){
35840         if(this.loadMask){
35841             this.loadMask.destroy();
35842             this.loadMask = new Roo.LoadMask(this.container,
35843                     Roo.apply({store:dataSource}, this.loadMask));
35844         }
35845         this.view.bind(dataSource, colModel);
35846         this.dataSource = dataSource;
35847         this.colModel = colModel;
35848         this.view.refresh(true);
35849     },
35850
35851     // private
35852     onKeyDown : function(e){
35853         this.fireEvent("keydown", e);
35854     },
35855
35856     /**
35857      * Destroy this grid.
35858      * @param {Boolean} removeEl True to remove the element
35859      */
35860     destroy : function(removeEl, keepListeners){
35861         if(this.loadMask){
35862             this.loadMask.destroy();
35863         }
35864         var c = this.container;
35865         c.removeAllListeners();
35866         this.view.destroy();
35867         this.colModel.purgeListeners();
35868         if(!keepListeners){
35869             this.purgeListeners();
35870         }
35871         c.update("");
35872         if(removeEl === true){
35873             c.remove();
35874         }
35875     },
35876
35877     // private
35878     processEvent : function(name, e){
35879         // does this fire select???
35880         Roo.log('grid:processEvent '  + name);
35881         
35882         if (name != 'touchstart' ) {
35883             this.fireEvent(name, e);    
35884         }
35885         
35886         var t = e.getTarget();
35887         var v = this.view;
35888         var header = v.findHeaderIndex(t);
35889         if(header !== false){
35890             this.fireEvent("header" + (name == 'touchstart' ? 'click' : name), this, header, e);
35891         }else{
35892             var row = v.findRowIndex(t);
35893             var cell = v.findCellIndex(t);
35894             if (name == 'touchstart') {
35895                 // first touch is always a click.
35896                 // hopefull this happens after selection is updated.?
35897                 name = false;
35898                 
35899                 if (typeof(this.selModel.getSelectedCell) != 'undefined') {
35900                     var cs = this.selModel.getSelectedCell();
35901                     if (row == cs[0] && cell == cs[1]){
35902                         name = 'dblclick';
35903                     }
35904                 }
35905                 if (typeof(this.selModel.getSelections) != 'undefined') {
35906                     var cs = this.selModel.getSelections();
35907                     var ds = this.dataSource;
35908                     if (cs.length == 1 && ds.getAt(row) == cs[0]){
35909                         name = 'dblclick';
35910                     }
35911                 }
35912                 if (!name) {
35913                     return;
35914                 }
35915             }
35916             
35917             
35918             if(row !== false){
35919                 this.fireEvent("row" + name, this, row, e);
35920                 if(cell !== false){
35921                     this.fireEvent("cell" + name, this, row, cell, e);
35922                 }
35923             }
35924         }
35925     },
35926
35927     // private
35928     onClick : function(e){
35929         this.processEvent("click", e);
35930     },
35931    // private
35932     onTouchStart : function(e){
35933         this.processEvent("touchstart", e);
35934     },
35935
35936     // private
35937     onContextMenu : function(e, t){
35938         this.processEvent("contextmenu", e);
35939     },
35940
35941     // private
35942     onDblClick : function(e){
35943         this.processEvent("dblclick", e);
35944     },
35945
35946     // private
35947     walkCells : function(row, col, step, fn, scope){
35948         var cm = this.colModel, clen = cm.getColumnCount();
35949         var ds = this.dataSource, rlen = ds.getCount(), first = true;
35950         if(step < 0){
35951             if(col < 0){
35952                 row--;
35953                 first = false;
35954             }
35955             while(row >= 0){
35956                 if(!first){
35957                     col = clen-1;
35958                 }
35959                 first = false;
35960                 while(col >= 0){
35961                     if(fn.call(scope || this, row, col, cm) === true){
35962                         return [row, col];
35963                     }
35964                     col--;
35965                 }
35966                 row--;
35967             }
35968         } else {
35969             if(col >= clen){
35970                 row++;
35971                 first = false;
35972             }
35973             while(row < rlen){
35974                 if(!first){
35975                     col = 0;
35976                 }
35977                 first = false;
35978                 while(col < clen){
35979                     if(fn.call(scope || this, row, col, cm) === true){
35980                         return [row, col];
35981                     }
35982                     col++;
35983                 }
35984                 row++;
35985             }
35986         }
35987         return null;
35988     },
35989
35990     // private
35991     getSelections : function(){
35992         return this.selModel.getSelections();
35993     },
35994
35995     /**
35996      * Causes the grid to manually recalculate its dimensions. Generally this is done automatically,
35997      * but if manual update is required this method will initiate it.
35998      */
35999     autoSize : function(){
36000         if(this.rendered){
36001             this.view.layout();
36002             if(this.view.adjustForScroll){
36003                 this.view.adjustForScroll();
36004             }
36005         }
36006     },
36007
36008     /**
36009      * Returns the grid's underlying element.
36010      * @return {Element} The element
36011      */
36012     getGridEl : function(){
36013         return this.container;
36014     },
36015
36016     // private for compatibility, overridden by editor grid
36017     stopEditing : function(){},
36018
36019     /**
36020      * Returns the grid's SelectionModel.
36021      * @return {SelectionModel}
36022      */
36023     getSelectionModel : function(){
36024         if(!this.selModel){
36025             this.selModel = new Roo.grid.RowSelectionModel();
36026         }
36027         return this.selModel;
36028     },
36029
36030     /**
36031      * Returns the grid's DataSource.
36032      * @return {DataSource}
36033      */
36034     getDataSource : function(){
36035         return this.dataSource;
36036     },
36037
36038     /**
36039      * Returns the grid's ColumnModel.
36040      * @return {ColumnModel}
36041      */
36042     getColumnModel : function(){
36043         return this.colModel;
36044     },
36045
36046     /**
36047      * Returns the grid's GridView object.
36048      * @return {GridView}
36049      */
36050     getView : function(){
36051         if(!this.view){
36052             this.view = new Roo.grid.GridView(this.viewConfig);
36053         }
36054         return this.view;
36055     },
36056     /**
36057      * Called to get grid's drag proxy text, by default returns this.ddText.
36058      * @return {String}
36059      */
36060     getDragDropText : function(){
36061         var count = this.selModel.getCount();
36062         return String.format(this.ddText, count, count == 1 ? '' : 's');
36063     }
36064 });
36065 /**
36066  * Configures the text is the drag proxy (defaults to "%0 selected row(s)").
36067  * %0 is replaced with the number of selected rows.
36068  * @type String
36069  */
36070 Roo.grid.Grid.prototype.ddText = "{0} selected row{1}";/*
36071  * Based on:
36072  * Ext JS Library 1.1.1
36073  * Copyright(c) 2006-2007, Ext JS, LLC.
36074  *
36075  * Originally Released Under LGPL - original licence link has changed is not relivant.
36076  *
36077  * Fork - LGPL
36078  * <script type="text/javascript">
36079  */
36080  
36081 Roo.grid.AbstractGridView = function(){
36082         this.grid = null;
36083         
36084         this.events = {
36085             "beforerowremoved" : true,
36086             "beforerowsinserted" : true,
36087             "beforerefresh" : true,
36088             "rowremoved" : true,
36089             "rowsinserted" : true,
36090             "rowupdated" : true,
36091             "refresh" : true
36092         };
36093     Roo.grid.AbstractGridView.superclass.constructor.call(this);
36094 };
36095
36096 Roo.extend(Roo.grid.AbstractGridView, Roo.util.Observable, {
36097     rowClass : "x-grid-row",
36098     cellClass : "x-grid-cell",
36099     tdClass : "x-grid-td",
36100     hdClass : "x-grid-hd",
36101     splitClass : "x-grid-hd-split",
36102     
36103         init: function(grid){
36104         this.grid = grid;
36105                 var cid = this.grid.getGridEl().id;
36106         this.colSelector = "#" + cid + " ." + this.cellClass + "-";
36107         this.tdSelector = "#" + cid + " ." + this.tdClass + "-";
36108         this.hdSelector = "#" + cid + " ." + this.hdClass + "-";
36109         this.splitSelector = "#" + cid + " ." + this.splitClass + "-";
36110         },
36111         
36112         getColumnRenderers : function(){
36113         var renderers = [];
36114         var cm = this.grid.colModel;
36115         var colCount = cm.getColumnCount();
36116         for(var i = 0; i < colCount; i++){
36117             renderers[i] = cm.getRenderer(i);
36118         }
36119         return renderers;
36120     },
36121     
36122     getColumnIds : function(){
36123         var ids = [];
36124         var cm = this.grid.colModel;
36125         var colCount = cm.getColumnCount();
36126         for(var i = 0; i < colCount; i++){
36127             ids[i] = cm.getColumnId(i);
36128         }
36129         return ids;
36130     },
36131     
36132     getDataIndexes : function(){
36133         if(!this.indexMap){
36134             this.indexMap = this.buildIndexMap();
36135         }
36136         return this.indexMap.colToData;
36137     },
36138     
36139     getColumnIndexByDataIndex : function(dataIndex){
36140         if(!this.indexMap){
36141             this.indexMap = this.buildIndexMap();
36142         }
36143         return this.indexMap.dataToCol[dataIndex];
36144     },
36145     
36146     /**
36147      * Set a css style for a column dynamically. 
36148      * @param {Number} colIndex The index of the column
36149      * @param {String} name The css property name
36150      * @param {String} value The css value
36151      */
36152     setCSSStyle : function(colIndex, name, value){
36153         var selector = "#" + this.grid.id + " .x-grid-col-" + colIndex;
36154         Roo.util.CSS.updateRule(selector, name, value);
36155     },
36156     
36157     generateRules : function(cm){
36158         var ruleBuf = [], rulesId = this.grid.id + '-cssrules';
36159         Roo.util.CSS.removeStyleSheet(rulesId);
36160         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
36161             var cid = cm.getColumnId(i);
36162             ruleBuf.push(this.colSelector, cid, " {\n", cm.config[i].css, "}\n",
36163                          this.tdSelector, cid, " {\n}\n",
36164                          this.hdSelector, cid, " {\n}\n",
36165                          this.splitSelector, cid, " {\n}\n");
36166         }
36167         return Roo.util.CSS.createStyleSheet(ruleBuf.join(""), rulesId);
36168     }
36169 });/*
36170  * Based on:
36171  * Ext JS Library 1.1.1
36172  * Copyright(c) 2006-2007, Ext JS, LLC.
36173  *
36174  * Originally Released Under LGPL - original licence link has changed is not relivant.
36175  *
36176  * Fork - LGPL
36177  * <script type="text/javascript">
36178  */
36179
36180 // private
36181 // This is a support class used internally by the Grid components
36182 Roo.grid.HeaderDragZone = function(grid, hd, hd2){
36183     this.grid = grid;
36184     this.view = grid.getView();
36185     this.ddGroup = "gridHeader" + this.grid.getGridEl().id;
36186     Roo.grid.HeaderDragZone.superclass.constructor.call(this, hd);
36187     if(hd2){
36188         this.setHandleElId(Roo.id(hd));
36189         this.setOuterHandleElId(Roo.id(hd2));
36190     }
36191     this.scroll = false;
36192 };
36193 Roo.extend(Roo.grid.HeaderDragZone, Roo.dd.DragZone, {
36194     maxDragWidth: 120,
36195     getDragData : function(e){
36196         var t = Roo.lib.Event.getTarget(e);
36197         var h = this.view.findHeaderCell(t);
36198         if(h){
36199             return {ddel: h.firstChild, header:h};
36200         }
36201         return false;
36202     },
36203
36204     onInitDrag : function(e){
36205         this.view.headersDisabled = true;
36206         var clone = this.dragData.ddel.cloneNode(true);
36207         clone.id = Roo.id();
36208         clone.style.width = Math.min(this.dragData.header.offsetWidth,this.maxDragWidth) + "px";
36209         this.proxy.update(clone);
36210         return true;
36211     },
36212
36213     afterValidDrop : function(){
36214         var v = this.view;
36215         setTimeout(function(){
36216             v.headersDisabled = false;
36217         }, 50);
36218     },
36219
36220     afterInvalidDrop : function(){
36221         var v = this.view;
36222         setTimeout(function(){
36223             v.headersDisabled = false;
36224         }, 50);
36225     }
36226 });
36227 /*
36228  * Based on:
36229  * Ext JS Library 1.1.1
36230  * Copyright(c) 2006-2007, Ext JS, LLC.
36231  *
36232  * Originally Released Under LGPL - original licence link has changed is not relivant.
36233  *
36234  * Fork - LGPL
36235  * <script type="text/javascript">
36236  */
36237 // private
36238 // This is a support class used internally by the Grid components
36239 Roo.grid.HeaderDropZone = function(grid, hd, hd2){
36240     this.grid = grid;
36241     this.view = grid.getView();
36242     // split the proxies so they don't interfere with mouse events
36243     this.proxyTop = Roo.DomHelper.append(document.body, {
36244         cls:"col-move-top", html:"&#160;"
36245     }, true);
36246     this.proxyBottom = Roo.DomHelper.append(document.body, {
36247         cls:"col-move-bottom", html:"&#160;"
36248     }, true);
36249     this.proxyTop.hide = this.proxyBottom.hide = function(){
36250         this.setLeftTop(-100,-100);
36251         this.setStyle("visibility", "hidden");
36252     };
36253     this.ddGroup = "gridHeader" + this.grid.getGridEl().id;
36254     // temporarily disabled
36255     //Roo.dd.ScrollManager.register(this.view.scroller.dom);
36256     Roo.grid.HeaderDropZone.superclass.constructor.call(this, grid.getGridEl().dom);
36257 };
36258 Roo.extend(Roo.grid.HeaderDropZone, Roo.dd.DropZone, {
36259     proxyOffsets : [-4, -9],
36260     fly: Roo.Element.fly,
36261
36262     getTargetFromEvent : function(e){
36263         var t = Roo.lib.Event.getTarget(e);
36264         var cindex = this.view.findCellIndex(t);
36265         if(cindex !== false){
36266             return this.view.getHeaderCell(cindex);
36267         }
36268         return null;
36269     },
36270
36271     nextVisible : function(h){
36272         var v = this.view, cm = this.grid.colModel;
36273         h = h.nextSibling;
36274         while(h){
36275             if(!cm.isHidden(v.getCellIndex(h))){
36276                 return h;
36277             }
36278             h = h.nextSibling;
36279         }
36280         return null;
36281     },
36282
36283     prevVisible : function(h){
36284         var v = this.view, cm = this.grid.colModel;
36285         h = h.prevSibling;
36286         while(h){
36287             if(!cm.isHidden(v.getCellIndex(h))){
36288                 return h;
36289             }
36290             h = h.prevSibling;
36291         }
36292         return null;
36293     },
36294
36295     positionIndicator : function(h, n, e){
36296         var x = Roo.lib.Event.getPageX(e);
36297         var r = Roo.lib.Dom.getRegion(n.firstChild);
36298         var px, pt, py = r.top + this.proxyOffsets[1];
36299         if((r.right - x) <= (r.right-r.left)/2){
36300             px = r.right+this.view.borderWidth;
36301             pt = "after";
36302         }else{
36303             px = r.left;
36304             pt = "before";
36305         }
36306         var oldIndex = this.view.getCellIndex(h);
36307         var newIndex = this.view.getCellIndex(n);
36308
36309         if(this.grid.colModel.isFixed(newIndex)){
36310             return false;
36311         }
36312
36313         var locked = this.grid.colModel.isLocked(newIndex);
36314
36315         if(pt == "after"){
36316             newIndex++;
36317         }
36318         if(oldIndex < newIndex){
36319             newIndex--;
36320         }
36321         if(oldIndex == newIndex && (locked == this.grid.colModel.isLocked(oldIndex))){
36322             return false;
36323         }
36324         px +=  this.proxyOffsets[0];
36325         this.proxyTop.setLeftTop(px, py);
36326         this.proxyTop.show();
36327         if(!this.bottomOffset){
36328             this.bottomOffset = this.view.mainHd.getHeight();
36329         }
36330         this.proxyBottom.setLeftTop(px, py+this.proxyTop.dom.offsetHeight+this.bottomOffset);
36331         this.proxyBottom.show();
36332         return pt;
36333     },
36334
36335     onNodeEnter : function(n, dd, e, data){
36336         if(data.header != n){
36337             this.positionIndicator(data.header, n, e);
36338         }
36339     },
36340
36341     onNodeOver : function(n, dd, e, data){
36342         var result = false;
36343         if(data.header != n){
36344             result = this.positionIndicator(data.header, n, e);
36345         }
36346         if(!result){
36347             this.proxyTop.hide();
36348             this.proxyBottom.hide();
36349         }
36350         return result ? this.dropAllowed : this.dropNotAllowed;
36351     },
36352
36353     onNodeOut : function(n, dd, e, data){
36354         this.proxyTop.hide();
36355         this.proxyBottom.hide();
36356     },
36357
36358     onNodeDrop : function(n, dd, e, data){
36359         var h = data.header;
36360         if(h != n){
36361             var cm = this.grid.colModel;
36362             var x = Roo.lib.Event.getPageX(e);
36363             var r = Roo.lib.Dom.getRegion(n.firstChild);
36364             var pt = (r.right - x) <= ((r.right-r.left)/2) ? "after" : "before";
36365             var oldIndex = this.view.getCellIndex(h);
36366             var newIndex = this.view.getCellIndex(n);
36367             var locked = cm.isLocked(newIndex);
36368             if(pt == "after"){
36369                 newIndex++;
36370             }
36371             if(oldIndex < newIndex){
36372                 newIndex--;
36373             }
36374             if(oldIndex == newIndex && (locked == cm.isLocked(oldIndex))){
36375                 return false;
36376             }
36377             cm.setLocked(oldIndex, locked, true);
36378             cm.moveColumn(oldIndex, newIndex);
36379             this.grid.fireEvent("columnmove", oldIndex, newIndex);
36380             return true;
36381         }
36382         return false;
36383     }
36384 });
36385 /*
36386  * Based on:
36387  * Ext JS Library 1.1.1
36388  * Copyright(c) 2006-2007, Ext JS, LLC.
36389  *
36390  * Originally Released Under LGPL - original licence link has changed is not relivant.
36391  *
36392  * Fork - LGPL
36393  * <script type="text/javascript">
36394  */
36395   
36396 /**
36397  * @class Roo.grid.GridView
36398  * @extends Roo.util.Observable
36399  *
36400  * @constructor
36401  * @param {Object} config
36402  */
36403 Roo.grid.GridView = function(config){
36404     Roo.grid.GridView.superclass.constructor.call(this);
36405     this.el = null;
36406
36407     Roo.apply(this, config);
36408 };
36409
36410 Roo.extend(Roo.grid.GridView, Roo.grid.AbstractGridView, {
36411
36412     unselectable :  'unselectable="on"',
36413     unselectableCls :  'x-unselectable',
36414     
36415     
36416     rowClass : "x-grid-row",
36417
36418     cellClass : "x-grid-col",
36419
36420     tdClass : "x-grid-td",
36421
36422     hdClass : "x-grid-hd",
36423
36424     splitClass : "x-grid-split",
36425
36426     sortClasses : ["sort-asc", "sort-desc"],
36427
36428     enableMoveAnim : false,
36429
36430     hlColor: "C3DAF9",
36431
36432     dh : Roo.DomHelper,
36433
36434     fly : Roo.Element.fly,
36435
36436     css : Roo.util.CSS,
36437
36438     borderWidth: 1,
36439
36440     splitOffset: 3,
36441
36442     scrollIncrement : 22,
36443
36444     cellRE: /(?:.*?)x-grid-(?:hd|cell|csplit)-(?:[\d]+)-([\d]+)(?:.*?)/,
36445
36446     findRE: /\s?(?:x-grid-hd|x-grid-col|x-grid-csplit)\s/,
36447
36448     bind : function(ds, cm){
36449         if(this.ds){
36450             this.ds.un("load", this.onLoad, this);
36451             this.ds.un("datachanged", this.onDataChange, this);
36452             this.ds.un("add", this.onAdd, this);
36453             this.ds.un("remove", this.onRemove, this);
36454             this.ds.un("update", this.onUpdate, this);
36455             this.ds.un("clear", this.onClear, this);
36456         }
36457         if(ds){
36458             ds.on("load", this.onLoad, this);
36459             ds.on("datachanged", this.onDataChange, this);
36460             ds.on("add", this.onAdd, this);
36461             ds.on("remove", this.onRemove, this);
36462             ds.on("update", this.onUpdate, this);
36463             ds.on("clear", this.onClear, this);
36464         }
36465         this.ds = ds;
36466
36467         if(this.cm){
36468             this.cm.un("widthchange", this.onColWidthChange, this);
36469             this.cm.un("headerchange", this.onHeaderChange, this);
36470             this.cm.un("hiddenchange", this.onHiddenChange, this);
36471             this.cm.un("columnmoved", this.onColumnMove, this);
36472             this.cm.un("columnlockchange", this.onColumnLock, this);
36473         }
36474         if(cm){
36475             this.generateRules(cm);
36476             cm.on("widthchange", this.onColWidthChange, this);
36477             cm.on("headerchange", this.onHeaderChange, this);
36478             cm.on("hiddenchange", this.onHiddenChange, this);
36479             cm.on("columnmoved", this.onColumnMove, this);
36480             cm.on("columnlockchange", this.onColumnLock, this);
36481         }
36482         this.cm = cm;
36483     },
36484
36485     init: function(grid){
36486         Roo.grid.GridView.superclass.init.call(this, grid);
36487
36488         this.bind(grid.dataSource, grid.colModel);
36489
36490         grid.on("headerclick", this.handleHeaderClick, this);
36491
36492         if(grid.trackMouseOver){
36493             grid.on("mouseover", this.onRowOver, this);
36494             grid.on("mouseout", this.onRowOut, this);
36495         }
36496         grid.cancelTextSelection = function(){};
36497         this.gridId = grid.id;
36498
36499         var tpls = this.templates || {};
36500
36501         if(!tpls.master){
36502             tpls.master = new Roo.Template(
36503                '<div class="x-grid" hidefocus="true">',
36504                 '<a href="#" class="x-grid-focus" tabIndex="-1"></a>',
36505                   '<div class="x-grid-topbar"></div>',
36506                   '<div class="x-grid-scroller"><div></div></div>',
36507                   '<div class="x-grid-locked">',
36508                       '<div class="x-grid-header">{lockedHeader}</div>',
36509                       '<div class="x-grid-body">{lockedBody}</div>',
36510                   "</div>",
36511                   '<div class="x-grid-viewport">',
36512                       '<div class="x-grid-header">{header}</div>',
36513                       '<div class="x-grid-body">{body}</div>',
36514                   "</div>",
36515                   '<div class="x-grid-bottombar"></div>',
36516                  
36517                   '<div class="x-grid-resize-proxy">&#160;</div>',
36518                "</div>"
36519             );
36520             tpls.master.disableformats = true;
36521         }
36522
36523         if(!tpls.header){
36524             tpls.header = new Roo.Template(
36525                '<table border="0" cellspacing="0" cellpadding="0">',
36526                '<tbody><tr class="x-grid-hd-row">{cells}</tr></tbody>',
36527                "</table>{splits}"
36528             );
36529             tpls.header.disableformats = true;
36530         }
36531         tpls.header.compile();
36532
36533         if(!tpls.hcell){
36534             tpls.hcell = new Roo.Template(
36535                 '<td class="x-grid-hd x-grid-td-{id} {cellId}"><div title="{title}" class="x-grid-hd-inner x-grid-hd-{id}">',
36536                 '<div class="x-grid-hd-text ' + this.unselectableCls +  '" ' + this.unselectable +'>{value}<img class="x-grid-sort-icon" src="', Roo.BLANK_IMAGE_URL, '" /></div>',
36537                 "</div></td>"
36538              );
36539              tpls.hcell.disableFormats = true;
36540         }
36541         tpls.hcell.compile();
36542
36543         if(!tpls.hsplit){
36544             tpls.hsplit = new Roo.Template('<div class="x-grid-split {splitId} x-grid-split-{id}" style="{style} ' +
36545                                             this.unselectableCls +  '" ' + this.unselectable +'>&#160;</div>');
36546             tpls.hsplit.disableFormats = true;
36547         }
36548         tpls.hsplit.compile();
36549
36550         if(!tpls.body){
36551             tpls.body = new Roo.Template(
36552                '<table border="0" cellspacing="0" cellpadding="0">',
36553                "<tbody>{rows}</tbody>",
36554                "</table>"
36555             );
36556             tpls.body.disableFormats = true;
36557         }
36558         tpls.body.compile();
36559
36560         if(!tpls.row){
36561             tpls.row = new Roo.Template('<tr class="x-grid-row {alt}">{cells}</tr>');
36562             tpls.row.disableFormats = true;
36563         }
36564         tpls.row.compile();
36565
36566         if(!tpls.cell){
36567             tpls.cell = new Roo.Template(
36568                 '<td class="x-grid-col x-grid-td-{id} {cellId} {css}" tabIndex="0">',
36569                 '<div class="x-grid-col-{id} x-grid-cell-inner"><div class="x-grid-cell-text ' +
36570                     this.unselectableCls +  '" ' + this.unselectable +'" {attr}>{value}</div></div>',
36571                 "</td>"
36572             );
36573             tpls.cell.disableFormats = true;
36574         }
36575         tpls.cell.compile();
36576
36577         this.templates = tpls;
36578     },
36579
36580     // remap these for backwards compat
36581     onColWidthChange : function(){
36582         this.updateColumns.apply(this, arguments);
36583     },
36584     onHeaderChange : function(){
36585         this.updateHeaders.apply(this, arguments);
36586     }, 
36587     onHiddenChange : function(){
36588         this.handleHiddenChange.apply(this, arguments);
36589     },
36590     onColumnMove : function(){
36591         this.handleColumnMove.apply(this, arguments);
36592     },
36593     onColumnLock : function(){
36594         this.handleLockChange.apply(this, arguments);
36595     },
36596
36597     onDataChange : function(){
36598         this.refresh();
36599         this.updateHeaderSortState();
36600     },
36601
36602     onClear : function(){
36603         this.refresh();
36604     },
36605
36606     onUpdate : function(ds, record){
36607         this.refreshRow(record);
36608     },
36609
36610     refreshRow : function(record){
36611         var ds = this.ds, index;
36612         if(typeof record == 'number'){
36613             index = record;
36614             record = ds.getAt(index);
36615         }else{
36616             index = ds.indexOf(record);
36617         }
36618         this.insertRows(ds, index, index, true);
36619         this.onRemove(ds, record, index+1, true);
36620         this.syncRowHeights(index, index);
36621         this.layout();
36622         this.fireEvent("rowupdated", this, index, record);
36623     },
36624
36625     onAdd : function(ds, records, index){
36626         this.insertRows(ds, index, index + (records.length-1));
36627     },
36628
36629     onRemove : function(ds, record, index, isUpdate){
36630         if(isUpdate !== true){
36631             this.fireEvent("beforerowremoved", this, index, record);
36632         }
36633         var bt = this.getBodyTable(), lt = this.getLockedTable();
36634         if(bt.rows[index]){
36635             bt.firstChild.removeChild(bt.rows[index]);
36636         }
36637         if(lt.rows[index]){
36638             lt.firstChild.removeChild(lt.rows[index]);
36639         }
36640         if(isUpdate !== true){
36641             this.stripeRows(index);
36642             this.syncRowHeights(index, index);
36643             this.layout();
36644             this.fireEvent("rowremoved", this, index, record);
36645         }
36646     },
36647
36648     onLoad : function(){
36649         this.scrollToTop();
36650     },
36651
36652     /**
36653      * Scrolls the grid to the top
36654      */
36655     scrollToTop : function(){
36656         if(this.scroller){
36657             this.scroller.dom.scrollTop = 0;
36658             this.syncScroll();
36659         }
36660     },
36661
36662     /**
36663      * Gets a panel in the header of the grid that can be used for toolbars etc.
36664      * After modifying the contents of this panel a call to grid.autoSize() may be
36665      * required to register any changes in size.
36666      * @param {Boolean} doShow By default the header is hidden. Pass true to show the panel
36667      * @return Roo.Element
36668      */
36669     getHeaderPanel : function(doShow){
36670         if(doShow){
36671             this.headerPanel.show();
36672         }
36673         return this.headerPanel;
36674     },
36675
36676     /**
36677      * Gets a panel in the footer of the grid that can be used for toolbars etc.
36678      * After modifying the contents of this panel a call to grid.autoSize() may be
36679      * required to register any changes in size.
36680      * @param {Boolean} doShow By default the footer is hidden. Pass true to show the panel
36681      * @return Roo.Element
36682      */
36683     getFooterPanel : function(doShow){
36684         if(doShow){
36685             this.footerPanel.show();
36686         }
36687         return this.footerPanel;
36688     },
36689
36690     initElements : function(){
36691         var E = Roo.Element;
36692         var el = this.grid.getGridEl().dom.firstChild;
36693         var cs = el.childNodes;
36694
36695         this.el = new E(el);
36696         
36697          this.focusEl = new E(el.firstChild);
36698         this.focusEl.swallowEvent("click", true);
36699         
36700         this.headerPanel = new E(cs[1]);
36701         this.headerPanel.enableDisplayMode("block");
36702
36703         this.scroller = new E(cs[2]);
36704         this.scrollSizer = new E(this.scroller.dom.firstChild);
36705
36706         this.lockedWrap = new E(cs[3]);
36707         this.lockedHd = new E(this.lockedWrap.dom.firstChild);
36708         this.lockedBody = new E(this.lockedWrap.dom.childNodes[1]);
36709
36710         this.mainWrap = new E(cs[4]);
36711         this.mainHd = new E(this.mainWrap.dom.firstChild);
36712         this.mainBody = new E(this.mainWrap.dom.childNodes[1]);
36713
36714         this.footerPanel = new E(cs[5]);
36715         this.footerPanel.enableDisplayMode("block");
36716
36717         this.resizeProxy = new E(cs[6]);
36718
36719         this.headerSelector = String.format(
36720            '#{0} td.x-grid-hd, #{1} td.x-grid-hd',
36721            this.lockedHd.id, this.mainHd.id
36722         );
36723
36724         this.splitterSelector = String.format(
36725            '#{0} div.x-grid-split, #{1} div.x-grid-split',
36726            this.idToCssName(this.lockedHd.id), this.idToCssName(this.mainHd.id)
36727         );
36728     },
36729     idToCssName : function(s)
36730     {
36731         return s.replace(/[^a-z0-9]+/ig, '-');
36732     },
36733
36734     getHeaderCell : function(index){
36735         return Roo.DomQuery.select(this.headerSelector)[index];
36736     },
36737
36738     getHeaderCellMeasure : function(index){
36739         return this.getHeaderCell(index).firstChild;
36740     },
36741
36742     getHeaderCellText : function(index){
36743         return this.getHeaderCell(index).firstChild.firstChild;
36744     },
36745
36746     getLockedTable : function(){
36747         return this.lockedBody.dom.firstChild;
36748     },
36749
36750     getBodyTable : function(){
36751         return this.mainBody.dom.firstChild;
36752     },
36753
36754     getLockedRow : function(index){
36755         return this.getLockedTable().rows[index];
36756     },
36757
36758     getRow : function(index){
36759         return this.getBodyTable().rows[index];
36760     },
36761
36762     getRowComposite : function(index){
36763         if(!this.rowEl){
36764             this.rowEl = new Roo.CompositeElementLite();
36765         }
36766         var els = [], lrow, mrow;
36767         if(lrow = this.getLockedRow(index)){
36768             els.push(lrow);
36769         }
36770         if(mrow = this.getRow(index)){
36771             els.push(mrow);
36772         }
36773         this.rowEl.elements = els;
36774         return this.rowEl;
36775     },
36776     /**
36777      * Gets the 'td' of the cell
36778      * 
36779      * @param {Integer} rowIndex row to select
36780      * @param {Integer} colIndex column to select
36781      * 
36782      * @return {Object} 
36783      */
36784     getCell : function(rowIndex, colIndex){
36785         var locked = this.cm.getLockedCount();
36786         var source;
36787         if(colIndex < locked){
36788             source = this.lockedBody.dom.firstChild;
36789         }else{
36790             source = this.mainBody.dom.firstChild;
36791             colIndex -= locked;
36792         }
36793         return source.rows[rowIndex].childNodes[colIndex];
36794     },
36795
36796     getCellText : function(rowIndex, colIndex){
36797         return this.getCell(rowIndex, colIndex).firstChild.firstChild;
36798     },
36799
36800     getCellBox : function(cell){
36801         var b = this.fly(cell).getBox();
36802         if(Roo.isOpera){ // opera fails to report the Y
36803             b.y = cell.offsetTop + this.mainBody.getY();
36804         }
36805         return b;
36806     },
36807
36808     getCellIndex : function(cell){
36809         var id = String(cell.className).match(this.cellRE);
36810         if(id){
36811             return parseInt(id[1], 10);
36812         }
36813         return 0;
36814     },
36815
36816     findHeaderIndex : function(n){
36817         var r = Roo.fly(n).findParent("td." + this.hdClass, 6);
36818         return r ? this.getCellIndex(r) : false;
36819     },
36820
36821     findHeaderCell : function(n){
36822         var r = Roo.fly(n).findParent("td." + this.hdClass, 6);
36823         return r ? r : false;
36824     },
36825
36826     findRowIndex : function(n){
36827         if(!n){
36828             return false;
36829         }
36830         var r = Roo.fly(n).findParent("tr." + this.rowClass, 6);
36831         return r ? r.rowIndex : false;
36832     },
36833
36834     findCellIndex : function(node){
36835         var stop = this.el.dom;
36836         while(node && node != stop){
36837             if(this.findRE.test(node.className)){
36838                 return this.getCellIndex(node);
36839             }
36840             node = node.parentNode;
36841         }
36842         return false;
36843     },
36844
36845     getColumnId : function(index){
36846         return this.cm.getColumnId(index);
36847     },
36848
36849     getSplitters : function()
36850     {
36851         if(this.splitterSelector){
36852            return Roo.DomQuery.select(this.splitterSelector);
36853         }else{
36854             return null;
36855       }
36856     },
36857
36858     getSplitter : function(index){
36859         return this.getSplitters()[index];
36860     },
36861
36862     onRowOver : function(e, t){
36863         var row;
36864         if((row = this.findRowIndex(t)) !== false){
36865             this.getRowComposite(row).addClass("x-grid-row-over");
36866         }
36867     },
36868
36869     onRowOut : function(e, t){
36870         var row;
36871         if((row = this.findRowIndex(t)) !== false && row !== this.findRowIndex(e.getRelatedTarget())){
36872             this.getRowComposite(row).removeClass("x-grid-row-over");
36873         }
36874     },
36875
36876     renderHeaders : function(){
36877         var cm = this.cm;
36878         var ct = this.templates.hcell, ht = this.templates.header, st = this.templates.hsplit;
36879         var cb = [], lb = [], sb = [], lsb = [], p = {};
36880         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
36881             p.cellId = "x-grid-hd-0-" + i;
36882             p.splitId = "x-grid-csplit-0-" + i;
36883             p.id = cm.getColumnId(i);
36884             p.title = cm.getColumnTooltip(i) || "";
36885             p.value = cm.getColumnHeader(i) || "";
36886             p.style = (this.grid.enableColumnResize === false || !cm.isResizable(i) || cm.isFixed(i)) ? 'cursor:default' : '';
36887             if(!cm.isLocked(i)){
36888                 cb[cb.length] = ct.apply(p);
36889                 sb[sb.length] = st.apply(p);
36890             }else{
36891                 lb[lb.length] = ct.apply(p);
36892                 lsb[lsb.length] = st.apply(p);
36893             }
36894         }
36895         return [ht.apply({cells: lb.join(""), splits:lsb.join("")}),
36896                 ht.apply({cells: cb.join(""), splits:sb.join("")})];
36897     },
36898
36899     updateHeaders : function(){
36900         var html = this.renderHeaders();
36901         this.lockedHd.update(html[0]);
36902         this.mainHd.update(html[1]);
36903     },
36904
36905     /**
36906      * Focuses the specified row.
36907      * @param {Number} row The row index
36908      */
36909     focusRow : function(row)
36910     {
36911         //Roo.log('GridView.focusRow');
36912         var x = this.scroller.dom.scrollLeft;
36913         this.focusCell(row, 0, false);
36914         this.scroller.dom.scrollLeft = x;
36915     },
36916
36917     /**
36918      * Focuses the specified cell.
36919      * @param {Number} row The row index
36920      * @param {Number} col The column index
36921      * @param {Boolean} hscroll false to disable horizontal scrolling
36922      */
36923     focusCell : function(row, col, hscroll)
36924     {
36925         //Roo.log('GridView.focusCell');
36926         var el = this.ensureVisible(row, col, hscroll);
36927         this.focusEl.alignTo(el, "tl-tl");
36928         if(Roo.isGecko){
36929             this.focusEl.focus();
36930         }else{
36931             this.focusEl.focus.defer(1, this.focusEl);
36932         }
36933     },
36934
36935     /**
36936      * Scrolls the specified cell into view
36937      * @param {Number} row The row index
36938      * @param {Number} col The column index
36939      * @param {Boolean} hscroll false to disable horizontal scrolling
36940      */
36941     ensureVisible : function(row, col, hscroll)
36942     {
36943         //Roo.log('GridView.ensureVisible,' + row + ',' + col);
36944         //return null; //disable for testing.
36945         if(typeof row != "number"){
36946             row = row.rowIndex;
36947         }
36948         if(row < 0 && row >= this.ds.getCount()){
36949             return  null;
36950         }
36951         col = (col !== undefined ? col : 0);
36952         var cm = this.grid.colModel;
36953         while(cm.isHidden(col)){
36954             col++;
36955         }
36956
36957         var el = this.getCell(row, col);
36958         if(!el){
36959             return null;
36960         }
36961         var c = this.scroller.dom;
36962
36963         var ctop = parseInt(el.offsetTop, 10);
36964         var cleft = parseInt(el.offsetLeft, 10);
36965         var cbot = ctop + el.offsetHeight;
36966         var cright = cleft + el.offsetWidth;
36967         
36968         var ch = c.clientHeight - this.mainHd.dom.offsetHeight;
36969         var stop = parseInt(c.scrollTop, 10);
36970         var sleft = parseInt(c.scrollLeft, 10);
36971         var sbot = stop + ch;
36972         var sright = sleft + c.clientWidth;
36973         /*
36974         Roo.log('GridView.ensureVisible:' +
36975                 ' ctop:' + ctop +
36976                 ' c.clientHeight:' + c.clientHeight +
36977                 ' this.mainHd.dom.offsetHeight:' + this.mainHd.dom.offsetHeight +
36978                 ' stop:' + stop +
36979                 ' cbot:' + cbot +
36980                 ' sbot:' + sbot +
36981                 ' ch:' + ch  
36982                 );
36983         */
36984         if(ctop < stop){
36985              c.scrollTop = ctop;
36986             //Roo.log("set scrolltop to ctop DISABLE?");
36987         }else if(cbot > sbot){
36988             //Roo.log("set scrolltop to cbot-ch");
36989             c.scrollTop = cbot-ch;
36990         }
36991         
36992         if(hscroll !== false){
36993             if(cleft < sleft){
36994                 c.scrollLeft = cleft;
36995             }else if(cright > sright){
36996                 c.scrollLeft = cright-c.clientWidth;
36997             }
36998         }
36999          
37000         return el;
37001     },
37002
37003     updateColumns : function(){
37004         this.grid.stopEditing();
37005         var cm = this.grid.colModel, colIds = this.getColumnIds();
37006         //var totalWidth = cm.getTotalWidth();
37007         var pos = 0;
37008         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
37009             //if(cm.isHidden(i)) continue;
37010             var w = cm.getColumnWidth(i);
37011             this.css.updateRule(this.colSelector+this.idToCssName(colIds[i]), "width", (w - this.borderWidth) + "px");
37012             this.css.updateRule(this.hdSelector+this.idToCssName(colIds[i]), "width", (w - this.borderWidth) + "px");
37013         }
37014         this.updateSplitters();
37015     },
37016
37017     generateRules : function(cm){
37018         var ruleBuf = [], rulesId = this.idToCssName(this.grid.id)+ '-cssrules';
37019         Roo.util.CSS.removeStyleSheet(rulesId);
37020         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
37021             var cid = cm.getColumnId(i);
37022             var align = '';
37023             if(cm.config[i].align){
37024                 align = 'text-align:'+cm.config[i].align+';';
37025             }
37026             var hidden = '';
37027             if(cm.isHidden(i)){
37028                 hidden = 'display:none;';
37029             }
37030             var width = "width:" + (cm.getColumnWidth(i) - this.borderWidth) + "px;";
37031             ruleBuf.push(
37032                     this.colSelector, cid, " {\n", cm.config[i].css, align, width, "\n}\n",
37033                     this.hdSelector, cid, " {\n", align, width, "}\n",
37034                     this.tdSelector, cid, " {\n",hidden,"\n}\n",
37035                     this.splitSelector, cid, " {\n", hidden , "\n}\n");
37036         }
37037         return Roo.util.CSS.createStyleSheet(ruleBuf.join(""), rulesId);
37038     },
37039
37040     updateSplitters : function(){
37041         var cm = this.cm, s = this.getSplitters();
37042         if(s){ // splitters not created yet
37043             var pos = 0, locked = true;
37044             for(var i = 0, len = cm.getColumnCount(); i < len; i++){
37045                 if(cm.isHidden(i)) continue;
37046                 var w = cm.getColumnWidth(i); // make sure it's a number
37047                 if(!cm.isLocked(i) && locked){
37048                     pos = 0;
37049                     locked = false;
37050                 }
37051                 pos += w;
37052                 s[i].style.left = (pos-this.splitOffset) + "px";
37053             }
37054         }
37055     },
37056
37057     handleHiddenChange : function(colModel, colIndex, hidden){
37058         if(hidden){
37059             this.hideColumn(colIndex);
37060         }else{
37061             this.unhideColumn(colIndex);
37062         }
37063     },
37064
37065     hideColumn : function(colIndex){
37066         var cid = this.getColumnId(colIndex);
37067         this.css.updateRule(this.tdSelector+this.idToCssName(cid), "display", "none");
37068         this.css.updateRule(this.splitSelector+this.idToCssName(cid), "display", "none");
37069         if(Roo.isSafari){
37070             this.updateHeaders();
37071         }
37072         this.updateSplitters();
37073         this.layout();
37074     },
37075
37076     unhideColumn : function(colIndex){
37077         var cid = this.getColumnId(colIndex);
37078         this.css.updateRule(this.tdSelector+this.idToCssName(cid), "display", "");
37079         this.css.updateRule(this.splitSelector+this.idToCssName(cid), "display", "");
37080
37081         if(Roo.isSafari){
37082             this.updateHeaders();
37083         }
37084         this.updateSplitters();
37085         this.layout();
37086     },
37087
37088     insertRows : function(dm, firstRow, lastRow, isUpdate){
37089         if(firstRow == 0 && lastRow == dm.getCount()-1){
37090             this.refresh();
37091         }else{
37092             if(!isUpdate){
37093                 this.fireEvent("beforerowsinserted", this, firstRow, lastRow);
37094             }
37095             var s = this.getScrollState();
37096             var markup = this.renderRows(firstRow, lastRow);
37097             this.bufferRows(markup[0], this.getLockedTable(), firstRow);
37098             this.bufferRows(markup[1], this.getBodyTable(), firstRow);
37099             this.restoreScroll(s);
37100             if(!isUpdate){
37101                 this.fireEvent("rowsinserted", this, firstRow, lastRow);
37102                 this.syncRowHeights(firstRow, lastRow);
37103                 this.stripeRows(firstRow);
37104                 this.layout();
37105             }
37106         }
37107     },
37108
37109     bufferRows : function(markup, target, index){
37110         var before = null, trows = target.rows, tbody = target.tBodies[0];
37111         if(index < trows.length){
37112             before = trows[index];
37113         }
37114         var b = document.createElement("div");
37115         b.innerHTML = "<table><tbody>"+markup+"</tbody></table>";
37116         var rows = b.firstChild.rows;
37117         for(var i = 0, len = rows.length; i < len; i++){
37118             if(before){
37119                 tbody.insertBefore(rows[0], before);
37120             }else{
37121                 tbody.appendChild(rows[0]);
37122             }
37123         }
37124         b.innerHTML = "";
37125         b = null;
37126     },
37127
37128     deleteRows : function(dm, firstRow, lastRow){
37129         if(dm.getRowCount()<1){
37130             this.fireEvent("beforerefresh", this);
37131             this.mainBody.update("");
37132             this.lockedBody.update("");
37133             this.fireEvent("refresh", this);
37134         }else{
37135             this.fireEvent("beforerowsdeleted", this, firstRow, lastRow);
37136             var bt = this.getBodyTable();
37137             var tbody = bt.firstChild;
37138             var rows = bt.rows;
37139             for(var rowIndex = firstRow; rowIndex <= lastRow; rowIndex++){
37140                 tbody.removeChild(rows[firstRow]);
37141             }
37142             this.stripeRows(firstRow);
37143             this.fireEvent("rowsdeleted", this, firstRow, lastRow);
37144         }
37145     },
37146
37147     updateRows : function(dataSource, firstRow, lastRow){
37148         var s = this.getScrollState();
37149         this.refresh();
37150         this.restoreScroll(s);
37151     },
37152
37153     handleSort : function(dataSource, sortColumnIndex, sortDir, noRefresh){
37154         if(!noRefresh){
37155            this.refresh();
37156         }
37157         this.updateHeaderSortState();
37158     },
37159
37160     getScrollState : function(){
37161         
37162         var sb = this.scroller.dom;
37163         return {left: sb.scrollLeft, top: sb.scrollTop};
37164     },
37165
37166     stripeRows : function(startRow){
37167         if(!this.grid.stripeRows || this.ds.getCount() < 1){
37168             return;
37169         }
37170         startRow = startRow || 0;
37171         var rows = this.getBodyTable().rows;
37172         var lrows = this.getLockedTable().rows;
37173         var cls = ' x-grid-row-alt ';
37174         for(var i = startRow, len = rows.length; i < len; i++){
37175             var row = rows[i], lrow = lrows[i];
37176             var isAlt = ((i+1) % 2 == 0);
37177             var hasAlt = (' '+row.className + ' ').indexOf(cls) != -1;
37178             if(isAlt == hasAlt){
37179                 continue;
37180             }
37181             if(isAlt){
37182                 row.className += " x-grid-row-alt";
37183             }else{
37184                 row.className = row.className.replace("x-grid-row-alt", "");
37185             }
37186             if(lrow){
37187                 lrow.className = row.className;
37188             }
37189         }
37190     },
37191
37192     restoreScroll : function(state){
37193         //Roo.log('GridView.restoreScroll');
37194         var sb = this.scroller.dom;
37195         sb.scrollLeft = state.left;
37196         sb.scrollTop = state.top;
37197         this.syncScroll();
37198     },
37199
37200     syncScroll : function(){
37201         //Roo.log('GridView.syncScroll');
37202         var sb = this.scroller.dom;
37203         var sh = this.mainHd.dom;
37204         var bs = this.mainBody.dom;
37205         var lv = this.lockedBody.dom;
37206         sh.scrollLeft = bs.scrollLeft = sb.scrollLeft;
37207         lv.scrollTop = bs.scrollTop = sb.scrollTop;
37208     },
37209
37210     handleScroll : function(e){
37211         this.syncScroll();
37212         var sb = this.scroller.dom;
37213         this.grid.fireEvent("bodyscroll", sb.scrollLeft, sb.scrollTop);
37214         e.stopEvent();
37215     },
37216
37217     handleWheel : function(e){
37218         var d = e.getWheelDelta();
37219         this.scroller.dom.scrollTop -= d*22;
37220         // set this here to prevent jumpy scrolling on large tables
37221         this.lockedBody.dom.scrollTop = this.mainBody.dom.scrollTop = this.scroller.dom.scrollTop;
37222         e.stopEvent();
37223     },
37224
37225     renderRows : function(startRow, endRow){
37226         // pull in all the crap needed to render rows
37227         var g = this.grid, cm = g.colModel, ds = g.dataSource, stripe = g.stripeRows;
37228         var colCount = cm.getColumnCount();
37229
37230         if(ds.getCount() < 1){
37231             return ["", ""];
37232         }
37233
37234         // build a map for all the columns
37235         var cs = [];
37236         for(var i = 0; i < colCount; i++){
37237             var name = cm.getDataIndex(i);
37238             cs[i] = {
37239                 name : typeof name == 'undefined' ? ds.fields.get(i).name : name,
37240                 renderer : cm.getRenderer(i),
37241                 id : cm.getColumnId(i),
37242                 locked : cm.isLocked(i)
37243             };
37244         }
37245
37246         startRow = startRow || 0;
37247         endRow = typeof endRow == "undefined"? ds.getCount()-1 : endRow;
37248
37249         // records to render
37250         var rs = ds.getRange(startRow, endRow);
37251
37252         return this.doRender(cs, rs, ds, startRow, colCount, stripe);
37253     },
37254
37255     // As much as I hate to duplicate code, this was branched because FireFox really hates
37256     // [].join("") on strings. The performance difference was substantial enough to
37257     // branch this function
37258     doRender : Roo.isGecko ?
37259             function(cs, rs, ds, startRow, colCount, stripe){
37260                 var ts = this.templates, ct = ts.cell, rt = ts.row;
37261                 // buffers
37262                 var buf = "", lbuf = "", cb, lcb, c, p = {}, rp = {}, r, rowIndex;
37263                 
37264                 var hasListener = this.grid.hasListener('rowclass');
37265                 var rowcfg = {};
37266                 for(var j = 0, len = rs.length; j < len; j++){
37267                     r = rs[j]; cb = ""; lcb = ""; rowIndex = (j+startRow);
37268                     for(var i = 0; i < colCount; i++){
37269                         c = cs[i];
37270                         p.cellId = "x-grid-cell-" + rowIndex + "-" + i;
37271                         p.id = c.id;
37272                         p.css = p.attr = "";
37273                         p.value = c.renderer(r.data[c.name], p, r, rowIndex, i, ds);
37274                         if(p.value == undefined || p.value === "") p.value = "&#160;";
37275                         if(r.dirty && typeof r.modified[c.name] !== 'undefined'){
37276                             p.css += p.css ? ' x-grid-dirty-cell' : 'x-grid-dirty-cell';
37277                         }
37278                         var markup = ct.apply(p);
37279                         if(!c.locked){
37280                             cb+= markup;
37281                         }else{
37282                             lcb+= markup;
37283                         }
37284                     }
37285                     var alt = [];
37286                     if(stripe && ((rowIndex+1) % 2 == 0)){
37287                         alt.push("x-grid-row-alt")
37288                     }
37289                     if(r.dirty){
37290                         alt.push(  " x-grid-dirty-row");
37291                     }
37292                     rp.cells = lcb;
37293                     if(this.getRowClass){
37294                         alt.push(this.getRowClass(r, rowIndex));
37295                     }
37296                     if (hasListener) {
37297                         rowcfg = {
37298                              
37299                             record: r,
37300                             rowIndex : rowIndex,
37301                             rowClass : ''
37302                         }
37303                         this.grid.fireEvent('rowclass', this, rowcfg);
37304                         alt.push(rowcfg.rowClass);
37305                     }
37306                     rp.alt = alt.join(" ");
37307                     lbuf+= rt.apply(rp);
37308                     rp.cells = cb;
37309                     buf+=  rt.apply(rp);
37310                 }
37311                 return [lbuf, buf];
37312             } :
37313             function(cs, rs, ds, startRow, colCount, stripe){
37314                 var ts = this.templates, ct = ts.cell, rt = ts.row;
37315                 // buffers
37316                 var buf = [], lbuf = [], cb, lcb, c, p = {}, rp = {}, r, rowIndex;
37317                 var hasListener = this.grid.hasListener('rowclass');
37318  
37319                 var rowcfg = {};
37320                 for(var j = 0, len = rs.length; j < len; j++){
37321                     r = rs[j]; cb = []; lcb = []; rowIndex = (j+startRow);
37322                     for(var i = 0; i < colCount; i++){
37323                         c = cs[i];
37324                         p.cellId = "x-grid-cell-" + rowIndex + "-" + i;
37325                         p.id = c.id;
37326                         p.css = p.attr = "";
37327                         p.value = c.renderer(r.data[c.name], p, r, rowIndex, i, ds);
37328                         if(p.value == undefined || p.value === "") p.value = "&#160;";
37329                         if(r.dirty && typeof r.modified[c.name] !== 'undefined'){
37330                             p.css += p.css ? ' x-grid-dirty-cell' : 'x-grid-dirty-cell';
37331                         }
37332                         
37333                         var markup = ct.apply(p);
37334                         if(!c.locked){
37335                             cb[cb.length] = markup;
37336                         }else{
37337                             lcb[lcb.length] = markup;
37338                         }
37339                     }
37340                     var alt = [];
37341                     if(stripe && ((rowIndex+1) % 2 == 0)){
37342                         alt.push( "x-grid-row-alt");
37343                     }
37344                     if(r.dirty){
37345                         alt.push(" x-grid-dirty-row");
37346                     }
37347                     rp.cells = lcb;
37348                     if(this.getRowClass){
37349                         alt.push( this.getRowClass(r, rowIndex));
37350                     }
37351                     if (hasListener) {
37352                         rowcfg = {
37353                              
37354                             record: r,
37355                             rowIndex : rowIndex,
37356                             rowClass : ''
37357                         }
37358                         this.grid.fireEvent('rowclass', this, rowcfg);
37359                         alt.push(rowcfg.rowClass);
37360                     }
37361                     rp.alt = alt.join(" ");
37362                     rp.cells = lcb.join("");
37363                     lbuf[lbuf.length] = rt.apply(rp);
37364                     rp.cells = cb.join("");
37365                     buf[buf.length] =  rt.apply(rp);
37366                 }
37367                 return [lbuf.join(""), buf.join("")];
37368             },
37369
37370     renderBody : function(){
37371         var markup = this.renderRows();
37372         var bt = this.templates.body;
37373         return [bt.apply({rows: markup[0]}), bt.apply({rows: markup[1]})];
37374     },
37375
37376     /**
37377      * Refreshes the grid
37378      * @param {Boolean} headersToo
37379      */
37380     refresh : function(headersToo){
37381         this.fireEvent("beforerefresh", this);
37382         this.grid.stopEditing();
37383         var result = this.renderBody();
37384         this.lockedBody.update(result[0]);
37385         this.mainBody.update(result[1]);
37386         if(headersToo === true){
37387             this.updateHeaders();
37388             this.updateColumns();
37389             this.updateSplitters();
37390             this.updateHeaderSortState();
37391         }
37392         this.syncRowHeights();
37393         this.layout();
37394         this.fireEvent("refresh", this);
37395     },
37396
37397     handleColumnMove : function(cm, oldIndex, newIndex){
37398         this.indexMap = null;
37399         var s = this.getScrollState();
37400         this.refresh(true);
37401         this.restoreScroll(s);
37402         this.afterMove(newIndex);
37403     },
37404
37405     afterMove : function(colIndex){
37406         if(this.enableMoveAnim && Roo.enableFx){
37407             this.fly(this.getHeaderCell(colIndex).firstChild).highlight(this.hlColor);
37408         }
37409         // if multisort - fix sortOrder, and reload..
37410         if (this.grid.dataSource.multiSort) {
37411             // the we can call sort again..
37412             var dm = this.grid.dataSource;
37413             var cm = this.grid.colModel;
37414             var so = [];
37415             for(var i = 0; i < cm.config.length; i++ ) {
37416                 
37417                 if ((typeof(dm.sortToggle[cm.config[i].dataIndex]) == 'undefined')) {
37418                     continue; // dont' bother, it's not in sort list or being set.
37419                 }
37420                 
37421                 so.push(cm.config[i].dataIndex);
37422             };
37423             dm.sortOrder = so;
37424             dm.load(dm.lastOptions);
37425             
37426             
37427         }
37428         
37429     },
37430
37431     updateCell : function(dm, rowIndex, dataIndex){
37432         var colIndex = this.getColumnIndexByDataIndex(dataIndex);
37433         if(typeof colIndex == "undefined"){ // not present in grid
37434             return;
37435         }
37436         var cm = this.grid.colModel;
37437         var cell = this.getCell(rowIndex, colIndex);
37438         var cellText = this.getCellText(rowIndex, colIndex);
37439
37440         var p = {
37441             cellId : "x-grid-cell-" + rowIndex + "-" + colIndex,
37442             id : cm.getColumnId(colIndex),
37443             css: colIndex == cm.getColumnCount()-1 ? "x-grid-col-last" : ""
37444         };
37445         var renderer = cm.getRenderer(colIndex);
37446         var val = renderer(dm.getValueAt(rowIndex, dataIndex), p, rowIndex, colIndex, dm);
37447         if(typeof val == "undefined" || val === "") val = "&#160;";
37448         cellText.innerHTML = val;
37449         cell.className = this.cellClass + " " + this.idToCssName(p.cellId) + " " + p.css;
37450         this.syncRowHeights(rowIndex, rowIndex);
37451     },
37452
37453     calcColumnWidth : function(colIndex, maxRowsToMeasure){
37454         var maxWidth = 0;
37455         if(this.grid.autoSizeHeaders){
37456             var h = this.getHeaderCellMeasure(colIndex);
37457             maxWidth = Math.max(maxWidth, h.scrollWidth);
37458         }
37459         var tb, index;
37460         if(this.cm.isLocked(colIndex)){
37461             tb = this.getLockedTable();
37462             index = colIndex;
37463         }else{
37464             tb = this.getBodyTable();
37465             index = colIndex - this.cm.getLockedCount();
37466         }
37467         if(tb && tb.rows){
37468             var rows = tb.rows;
37469             var stopIndex = Math.min(maxRowsToMeasure || rows.length, rows.length);
37470             for(var i = 0; i < stopIndex; i++){
37471                 var cell = rows[i].childNodes[index].firstChild;
37472                 maxWidth = Math.max(maxWidth, cell.scrollWidth);
37473             }
37474         }
37475         return maxWidth + /*margin for error in IE*/ 5;
37476     },
37477     /**
37478      * Autofit a column to its content.
37479      * @param {Number} colIndex
37480      * @param {Boolean} forceMinSize true to force the column to go smaller if possible
37481      */
37482      autoSizeColumn : function(colIndex, forceMinSize, suppressEvent){
37483          if(this.cm.isHidden(colIndex)){
37484              return; // can't calc a hidden column
37485          }
37486         if(forceMinSize){
37487             var cid = this.cm.getColumnId(colIndex);
37488             this.css.updateRule(this.colSelector +this.idToCssName( cid), "width", this.grid.minColumnWidth + "px");
37489            if(this.grid.autoSizeHeaders){
37490                this.css.updateRule(this.hdSelector + this.idToCssName(cid), "width", this.grid.minColumnWidth + "px");
37491            }
37492         }
37493         var newWidth = this.calcColumnWidth(colIndex);
37494         this.cm.setColumnWidth(colIndex,
37495             Math.max(this.grid.minColumnWidth, newWidth), suppressEvent);
37496         if(!suppressEvent){
37497             this.grid.fireEvent("columnresize", colIndex, newWidth);
37498         }
37499     },
37500
37501     /**
37502      * Autofits all columns to their content and then expands to fit any extra space in the grid
37503      */
37504      autoSizeColumns : function(){
37505         var cm = this.grid.colModel;
37506         var colCount = cm.getColumnCount();
37507         for(var i = 0; i < colCount; i++){
37508             this.autoSizeColumn(i, true, true);
37509         }
37510         if(cm.getTotalWidth() < this.scroller.dom.clientWidth){
37511             this.fitColumns();
37512         }else{
37513             this.updateColumns();
37514             this.layout();
37515         }
37516     },
37517
37518     /**
37519      * Autofits all columns to the grid's width proportionate with their current size
37520      * @param {Boolean} reserveScrollSpace Reserve space for a scrollbar
37521      */
37522     fitColumns : function(reserveScrollSpace){
37523         var cm = this.grid.colModel;
37524         var colCount = cm.getColumnCount();
37525         var cols = [];
37526         var width = 0;
37527         var i, w;
37528         for (i = 0; i < colCount; i++){
37529             if(!cm.isHidden(i) && !cm.isFixed(i)){
37530                 w = cm.getColumnWidth(i);
37531                 cols.push(i);
37532                 cols.push(w);
37533                 width += w;
37534             }
37535         }
37536         var avail = Math.min(this.scroller.dom.clientWidth, this.el.getWidth());
37537         if(reserveScrollSpace){
37538             avail -= 17;
37539         }
37540         var frac = (avail - cm.getTotalWidth())/width;
37541         while (cols.length){
37542             w = cols.pop();
37543             i = cols.pop();
37544             cm.setColumnWidth(i, Math.floor(w + w*frac), true);
37545         }
37546         this.updateColumns();
37547         this.layout();
37548     },
37549
37550     onRowSelect : function(rowIndex){
37551         var row = this.getRowComposite(rowIndex);
37552         row.addClass("x-grid-row-selected");
37553     },
37554
37555     onRowDeselect : function(rowIndex){
37556         var row = this.getRowComposite(rowIndex);
37557         row.removeClass("x-grid-row-selected");
37558     },
37559
37560     onCellSelect : function(row, col){
37561         var cell = this.getCell(row, col);
37562         if(cell){
37563             Roo.fly(cell).addClass("x-grid-cell-selected");
37564         }
37565     },
37566
37567     onCellDeselect : function(row, col){
37568         var cell = this.getCell(row, col);
37569         if(cell){
37570             Roo.fly(cell).removeClass("x-grid-cell-selected");
37571         }
37572     },
37573
37574     updateHeaderSortState : function(){
37575         
37576         // sort state can be single { field: xxx, direction : yyy}
37577         // or   { xxx=>ASC , yyy : DESC ..... }
37578         
37579         var mstate = {};
37580         if (!this.ds.multiSort) { 
37581             var state = this.ds.getSortState();
37582             if(!state){
37583                 return;
37584             }
37585             mstate[state.field] = state.direction;
37586             // FIXME... - this is not used here.. but might be elsewhere..
37587             this.sortState = state;
37588             
37589         } else {
37590             mstate = this.ds.sortToggle;
37591         }
37592         //remove existing sort classes..
37593         
37594         var sc = this.sortClasses;
37595         var hds = this.el.select(this.headerSelector).removeClass(sc);
37596         
37597         for(var f in mstate) {
37598         
37599             var sortColumn = this.cm.findColumnIndex(f);
37600             
37601             if(sortColumn != -1){
37602                 var sortDir = mstate[f];        
37603                 hds.item(sortColumn).addClass(sc[sortDir == "DESC" ? 1 : 0]);
37604             }
37605         }
37606         
37607          
37608         
37609     },
37610
37611
37612     handleHeaderClick : function(g, index){
37613         if(this.headersDisabled){
37614             return;
37615         }
37616         var dm = g.dataSource, cm = g.colModel;
37617         if(!cm.isSortable(index)){
37618             return;
37619         }
37620         g.stopEditing();
37621         
37622         if (dm.multiSort) {
37623             // update the sortOrder
37624             var so = [];
37625             for(var i = 0; i < cm.config.length; i++ ) {
37626                 
37627                 if ((typeof(dm.sortToggle[cm.config[i].dataIndex]) == 'undefined') && (index != i)) {
37628                     continue; // dont' bother, it's not in sort list or being set.
37629                 }
37630                 
37631                 so.push(cm.config[i].dataIndex);
37632             };
37633             dm.sortOrder = so;
37634         }
37635         
37636         
37637         dm.sort(cm.getDataIndex(index));
37638     },
37639
37640
37641     destroy : function(){
37642         if(this.colMenu){
37643             this.colMenu.removeAll();
37644             Roo.menu.MenuMgr.unregister(this.colMenu);
37645             this.colMenu.getEl().remove();
37646             delete this.colMenu;
37647         }
37648         if(this.hmenu){
37649             this.hmenu.removeAll();
37650             Roo.menu.MenuMgr.unregister(this.hmenu);
37651             this.hmenu.getEl().remove();
37652             delete this.hmenu;
37653         }
37654         if(this.grid.enableColumnMove){
37655             var dds = Roo.dd.DDM.ids['gridHeader' + this.grid.getGridEl().id];
37656             if(dds){
37657                 for(var dd in dds){
37658                     if(!dds[dd].config.isTarget && dds[dd].dragElId){
37659                         var elid = dds[dd].dragElId;
37660                         dds[dd].unreg();
37661                         Roo.get(elid).remove();
37662                     } else if(dds[dd].config.isTarget){
37663                         dds[dd].proxyTop.remove();
37664                         dds[dd].proxyBottom.remove();
37665                         dds[dd].unreg();
37666                     }
37667                     if(Roo.dd.DDM.locationCache[dd]){
37668                         delete Roo.dd.DDM.locationCache[dd];
37669                     }
37670                 }
37671                 delete Roo.dd.DDM.ids['gridHeader' + this.grid.getGridEl().id];
37672             }
37673         }
37674         Roo.util.CSS.removeStyleSheet(this.idToCssName(this.grid.id) + '-cssrules');
37675         this.bind(null, null);
37676         Roo.EventManager.removeResizeListener(this.onWindowResize, this);
37677     },
37678
37679     handleLockChange : function(){
37680         this.refresh(true);
37681     },
37682
37683     onDenyColumnLock : function(){
37684
37685     },
37686
37687     onDenyColumnHide : function(){
37688
37689     },
37690
37691     handleHdMenuClick : function(item){
37692         var index = this.hdCtxIndex;
37693         var cm = this.cm, ds = this.ds;
37694         switch(item.id){
37695             case "asc":
37696                 ds.sort(cm.getDataIndex(index), "ASC");
37697                 break;
37698             case "desc":
37699                 ds.sort(cm.getDataIndex(index), "DESC");
37700                 break;
37701             case "lock":
37702                 var lc = cm.getLockedCount();
37703                 if(cm.getColumnCount(true) <= lc+1){
37704                     this.onDenyColumnLock();
37705                     return;
37706                 }
37707                 if(lc != index){
37708                     cm.setLocked(index, true, true);
37709                     cm.moveColumn(index, lc);
37710                     this.grid.fireEvent("columnmove", index, lc);
37711                 }else{
37712                     cm.setLocked(index, true);
37713                 }
37714             break;
37715             case "unlock":
37716                 var lc = cm.getLockedCount();
37717                 if((lc-1) != index){
37718                     cm.setLocked(index, false, true);
37719                     cm.moveColumn(index, lc-1);
37720                     this.grid.fireEvent("columnmove", index, lc-1);
37721                 }else{
37722                     cm.setLocked(index, false);
37723                 }
37724             break;
37725             default:
37726                 index = cm.getIndexById(item.id.substr(4));
37727                 if(index != -1){
37728                     if(item.checked && cm.getColumnCount(true) <= 1){
37729                         this.onDenyColumnHide();
37730                         return false;
37731                     }
37732                     cm.setHidden(index, item.checked);
37733                 }
37734         }
37735         return true;
37736     },
37737
37738     beforeColMenuShow : function(){
37739         var cm = this.cm,  colCount = cm.getColumnCount();
37740         this.colMenu.removeAll();
37741         for(var i = 0; i < colCount; i++){
37742             this.colMenu.add(new Roo.menu.CheckItem({
37743                 id: "col-"+cm.getColumnId(i),
37744                 text: cm.getColumnHeader(i),
37745                 checked: !cm.isHidden(i),
37746                 hideOnClick:false
37747             }));
37748         }
37749     },
37750
37751     handleHdCtx : function(g, index, e){
37752         e.stopEvent();
37753         var hd = this.getHeaderCell(index);
37754         this.hdCtxIndex = index;
37755         var ms = this.hmenu.items, cm = this.cm;
37756         ms.get("asc").setDisabled(!cm.isSortable(index));
37757         ms.get("desc").setDisabled(!cm.isSortable(index));
37758         if(this.grid.enableColLock !== false){
37759             ms.get("lock").setDisabled(cm.isLocked(index));
37760             ms.get("unlock").setDisabled(!cm.isLocked(index));
37761         }
37762         this.hmenu.show(hd, "tl-bl");
37763     },
37764
37765     handleHdOver : function(e){
37766         var hd = this.findHeaderCell(e.getTarget());
37767         if(hd && !this.headersDisabled){
37768             if(this.grid.colModel.isSortable(this.getCellIndex(hd))){
37769                this.fly(hd).addClass("x-grid-hd-over");
37770             }
37771         }
37772     },
37773
37774     handleHdOut : function(e){
37775         var hd = this.findHeaderCell(e.getTarget());
37776         if(hd){
37777             this.fly(hd).removeClass("x-grid-hd-over");
37778         }
37779     },
37780
37781     handleSplitDblClick : function(e, t){
37782         var i = this.getCellIndex(t);
37783         if(this.grid.enableColumnResize !== false && this.cm.isResizable(i) && !this.cm.isFixed(i)){
37784             this.autoSizeColumn(i, true);
37785             this.layout();
37786         }
37787     },
37788
37789     render : function(){
37790
37791         var cm = this.cm;
37792         var colCount = cm.getColumnCount();
37793
37794         if(this.grid.monitorWindowResize === true){
37795             Roo.EventManager.onWindowResize(this.onWindowResize, this, true);
37796         }
37797         var header = this.renderHeaders();
37798         var body = this.templates.body.apply({rows:""});
37799         var html = this.templates.master.apply({
37800             lockedBody: body,
37801             body: body,
37802             lockedHeader: header[0],
37803             header: header[1]
37804         });
37805
37806         //this.updateColumns();
37807
37808         this.grid.getGridEl().dom.innerHTML = html;
37809
37810         this.initElements();
37811         
37812         // a kludge to fix the random scolling effect in webkit
37813         this.el.on("scroll", function() {
37814             this.el.dom.scrollTop=0; // hopefully not recursive..
37815         },this);
37816
37817         this.scroller.on("scroll", this.handleScroll, this);
37818         this.lockedBody.on("mousewheel", this.handleWheel, this);
37819         this.mainBody.on("mousewheel", this.handleWheel, this);
37820
37821         this.mainHd.on("mouseover", this.handleHdOver, this);
37822         this.mainHd.on("mouseout", this.handleHdOut, this);
37823         this.mainHd.on("dblclick", this.handleSplitDblClick, this,
37824                 {delegate: "."+this.splitClass});
37825
37826         this.lockedHd.on("mouseover", this.handleHdOver, this);
37827         this.lockedHd.on("mouseout", this.handleHdOut, this);
37828         this.lockedHd.on("dblclick", this.handleSplitDblClick, this,
37829                 {delegate: "."+this.splitClass});
37830
37831         if(this.grid.enableColumnResize !== false && Roo.grid.SplitDragZone){
37832             new Roo.grid.SplitDragZone(this.grid, this.lockedHd.dom, this.mainHd.dom);
37833         }
37834
37835         this.updateSplitters();
37836
37837         if(this.grid.enableColumnMove && Roo.grid.HeaderDragZone){
37838             new Roo.grid.HeaderDragZone(this.grid, this.lockedHd.dom, this.mainHd.dom);
37839             new Roo.grid.HeaderDropZone(this.grid, this.lockedHd.dom, this.mainHd.dom);
37840         }
37841
37842         if(this.grid.enableCtxMenu !== false && Roo.menu.Menu){
37843             this.hmenu = new Roo.menu.Menu({id: this.grid.id + "-hctx"});
37844             this.hmenu.add(
37845                 {id:"asc", text: this.sortAscText, cls: "xg-hmenu-sort-asc"},
37846                 {id:"desc", text: this.sortDescText, cls: "xg-hmenu-sort-desc"}
37847             );
37848             if(this.grid.enableColLock !== false){
37849                 this.hmenu.add('-',
37850                     {id:"lock", text: this.lockText, cls: "xg-hmenu-lock"},
37851                     {id:"unlock", text: this.unlockText, cls: "xg-hmenu-unlock"}
37852                 );
37853             }
37854             if(this.grid.enableColumnHide !== false){
37855
37856                 this.colMenu = new Roo.menu.Menu({id:this.grid.id + "-hcols-menu"});
37857                 this.colMenu.on("beforeshow", this.beforeColMenuShow, this);
37858                 this.colMenu.on("itemclick", this.handleHdMenuClick, this);
37859
37860                 this.hmenu.add('-',
37861                     {id:"columns", text: this.columnsText, menu: this.colMenu}
37862                 );
37863             }
37864             this.hmenu.on("itemclick", this.handleHdMenuClick, this);
37865
37866             this.grid.on("headercontextmenu", this.handleHdCtx, this);
37867         }
37868
37869         if((this.grid.enableDragDrop || this.grid.enableDrag) && Roo.grid.GridDragZone){
37870             this.dd = new Roo.grid.GridDragZone(this.grid, {
37871                 ddGroup : this.grid.ddGroup || 'GridDD'
37872             });
37873             
37874         }
37875
37876         /*
37877         for(var i = 0; i < colCount; i++){
37878             if(cm.isHidden(i)){
37879                 this.hideColumn(i);
37880             }
37881             if(cm.config[i].align){
37882                 this.css.updateRule(this.colSelector + i, "textAlign", cm.config[i].align);
37883                 this.css.updateRule(this.hdSelector + i, "textAlign", cm.config[i].align);
37884             }
37885         }*/
37886         
37887         this.updateHeaderSortState();
37888
37889         this.beforeInitialResize();
37890         this.layout(true);
37891
37892         // two part rendering gives faster view to the user
37893         this.renderPhase2.defer(1, this);
37894     },
37895
37896     renderPhase2 : function(){
37897         // render the rows now
37898         this.refresh();
37899         if(this.grid.autoSizeColumns){
37900             this.autoSizeColumns();
37901         }
37902     },
37903
37904     beforeInitialResize : function(){
37905
37906     },
37907
37908     onColumnSplitterMoved : function(i, w){
37909         this.userResized = true;
37910         var cm = this.grid.colModel;
37911         cm.setColumnWidth(i, w, true);
37912         var cid = cm.getColumnId(i);
37913         this.css.updateRule(this.colSelector + this.idToCssName(cid), "width", (w-this.borderWidth) + "px");
37914         this.css.updateRule(this.hdSelector + this.idToCssName(cid), "width", (w-this.borderWidth) + "px");
37915         this.updateSplitters();
37916         this.layout();
37917         this.grid.fireEvent("columnresize", i, w);
37918     },
37919
37920     syncRowHeights : function(startIndex, endIndex){
37921         if(this.grid.enableRowHeightSync === true && this.cm.getLockedCount() > 0){
37922             startIndex = startIndex || 0;
37923             var mrows = this.getBodyTable().rows;
37924             var lrows = this.getLockedTable().rows;
37925             var len = mrows.length-1;
37926             endIndex = Math.min(endIndex || len, len);
37927             for(var i = startIndex; i <= endIndex; i++){
37928                 var m = mrows[i], l = lrows[i];
37929                 var h = Math.max(m.offsetHeight, l.offsetHeight);
37930                 m.style.height = l.style.height = h + "px";
37931             }
37932         }
37933     },
37934
37935     layout : function(initialRender, is2ndPass){
37936         var g = this.grid;
37937         var auto = g.autoHeight;
37938         var scrollOffset = 16;
37939         var c = g.getGridEl(), cm = this.cm,
37940                 expandCol = g.autoExpandColumn,
37941                 gv = this;
37942         //c.beginMeasure();
37943
37944         if(!c.dom.offsetWidth){ // display:none?
37945             if(initialRender){
37946                 this.lockedWrap.show();
37947                 this.mainWrap.show();
37948             }
37949             return;
37950         }
37951
37952         var hasLock = this.cm.isLocked(0);
37953
37954         var tbh = this.headerPanel.getHeight();
37955         var bbh = this.footerPanel.getHeight();
37956
37957         if(auto){
37958             var ch = this.getBodyTable().offsetHeight + tbh + bbh + this.mainHd.getHeight();
37959             var newHeight = ch + c.getBorderWidth("tb");
37960             if(g.maxHeight){
37961                 newHeight = Math.min(g.maxHeight, newHeight);
37962             }
37963             c.setHeight(newHeight);
37964         }
37965
37966         if(g.autoWidth){
37967             c.setWidth(cm.getTotalWidth()+c.getBorderWidth('lr'));
37968         }
37969
37970         var s = this.scroller;
37971
37972         var csize = c.getSize(true);
37973
37974         this.el.setSize(csize.width, csize.height);
37975
37976         this.headerPanel.setWidth(csize.width);
37977         this.footerPanel.setWidth(csize.width);
37978
37979         var hdHeight = this.mainHd.getHeight();
37980         var vw = csize.width;
37981         var vh = csize.height - (tbh + bbh);
37982
37983         s.setSize(vw, vh);
37984
37985         var bt = this.getBodyTable();
37986         var ltWidth = hasLock ?
37987                       Math.max(this.getLockedTable().offsetWidth, this.lockedHd.dom.firstChild.offsetWidth) : 0;
37988
37989         var scrollHeight = bt.offsetHeight;
37990         var scrollWidth = ltWidth + bt.offsetWidth;
37991         var vscroll = false, hscroll = false;
37992
37993         this.scrollSizer.setSize(scrollWidth, scrollHeight+hdHeight);
37994
37995         var lw = this.lockedWrap, mw = this.mainWrap;
37996         var lb = this.lockedBody, mb = this.mainBody;
37997
37998         setTimeout(function(){
37999             var t = s.dom.offsetTop;
38000             var w = s.dom.clientWidth,
38001                 h = s.dom.clientHeight;
38002
38003             lw.setTop(t);
38004             lw.setSize(ltWidth, h);
38005
38006             mw.setLeftTop(ltWidth, t);
38007             mw.setSize(w-ltWidth, h);
38008
38009             lb.setHeight(h-hdHeight);
38010             mb.setHeight(h-hdHeight);
38011
38012             if(is2ndPass !== true && !gv.userResized && expandCol){
38013                 // high speed resize without full column calculation
38014                 
38015                 var ci = cm.getIndexById(expandCol);
38016                 if (ci < 0) {
38017                     ci = cm.findColumnIndex(expandCol);
38018                 }
38019                 ci = Math.max(0, ci); // make sure it's got at least the first col.
38020                 var expandId = cm.getColumnId(ci);
38021                 var  tw = cm.getTotalWidth(false);
38022                 var currentWidth = cm.getColumnWidth(ci);
38023                 var cw = Math.min(Math.max(((w-tw)+currentWidth-2)-/*scrollbar*/(w <= s.dom.offsetWidth ? 0 : 18), g.autoExpandMin), g.autoExpandMax);
38024                 if(currentWidth != cw){
38025                     cm.setColumnWidth(ci, cw, true);
38026                     gv.css.updateRule(gv.colSelector+gv.idToCssName(expandId), "width", (cw - gv.borderWidth) + "px");
38027                     gv.css.updateRule(gv.hdSelector+gv.idToCssName(expandId), "width", (cw - gv.borderWidth) + "px");
38028                     gv.updateSplitters();
38029                     gv.layout(false, true);
38030                 }
38031             }
38032
38033             if(initialRender){
38034                 lw.show();
38035                 mw.show();
38036             }
38037             //c.endMeasure();
38038         }, 10);
38039     },
38040
38041     onWindowResize : function(){
38042         if(!this.grid.monitorWindowResize || this.grid.autoHeight){
38043             return;
38044         }
38045         this.layout();
38046     },
38047
38048     appendFooter : function(parentEl){
38049         return null;
38050     },
38051
38052     sortAscText : "Sort Ascending",
38053     sortDescText : "Sort Descending",
38054     lockText : "Lock Column",
38055     unlockText : "Unlock Column",
38056     columnsText : "Columns"
38057 });
38058
38059
38060 Roo.grid.GridView.ColumnDragZone = function(grid, hd){
38061     Roo.grid.GridView.ColumnDragZone.superclass.constructor.call(this, grid, hd, null);
38062     this.proxy.el.addClass('x-grid3-col-dd');
38063 };
38064
38065 Roo.extend(Roo.grid.GridView.ColumnDragZone, Roo.grid.HeaderDragZone, {
38066     handleMouseDown : function(e){
38067
38068     },
38069
38070     callHandleMouseDown : function(e){
38071         Roo.grid.GridView.ColumnDragZone.superclass.handleMouseDown.call(this, e);
38072     }
38073 });
38074 /*
38075  * Based on:
38076  * Ext JS Library 1.1.1
38077  * Copyright(c) 2006-2007, Ext JS, LLC.
38078  *
38079  * Originally Released Under LGPL - original licence link has changed is not relivant.
38080  *
38081  * Fork - LGPL
38082  * <script type="text/javascript">
38083  */
38084  
38085 // private
38086 // This is a support class used internally by the Grid components
38087 Roo.grid.SplitDragZone = function(grid, hd, hd2){
38088     this.grid = grid;
38089     this.view = grid.getView();
38090     this.proxy = this.view.resizeProxy;
38091     Roo.grid.SplitDragZone.superclass.constructor.call(this, hd,
38092         "gridSplitters" + this.grid.getGridEl().id, {
38093         dragElId : Roo.id(this.proxy.dom), resizeFrame:false
38094     });
38095     this.setHandleElId(Roo.id(hd));
38096     this.setOuterHandleElId(Roo.id(hd2));
38097     this.scroll = false;
38098 };
38099 Roo.extend(Roo.grid.SplitDragZone, Roo.dd.DDProxy, {
38100     fly: Roo.Element.fly,
38101
38102     b4StartDrag : function(x, y){
38103         this.view.headersDisabled = true;
38104         this.proxy.setHeight(this.view.mainWrap.getHeight());
38105         var w = this.cm.getColumnWidth(this.cellIndex);
38106         var minw = Math.max(w-this.grid.minColumnWidth, 0);
38107         this.resetConstraints();
38108         this.setXConstraint(minw, 1000);
38109         this.setYConstraint(0, 0);
38110         this.minX = x - minw;
38111         this.maxX = x + 1000;
38112         this.startPos = x;
38113         Roo.dd.DDProxy.prototype.b4StartDrag.call(this, x, y);
38114     },
38115
38116
38117     handleMouseDown : function(e){
38118         ev = Roo.EventObject.setEvent(e);
38119         var t = this.fly(ev.getTarget());
38120         if(t.hasClass("x-grid-split")){
38121             this.cellIndex = this.view.getCellIndex(t.dom);
38122             this.split = t.dom;
38123             this.cm = this.grid.colModel;
38124             if(this.cm.isResizable(this.cellIndex) && !this.cm.isFixed(this.cellIndex)){
38125                 Roo.grid.SplitDragZone.superclass.handleMouseDown.apply(this, arguments);
38126             }
38127         }
38128     },
38129
38130     endDrag : function(e){
38131         this.view.headersDisabled = false;
38132         var endX = Math.max(this.minX, Roo.lib.Event.getPageX(e));
38133         var diff = endX - this.startPos;
38134         this.view.onColumnSplitterMoved(this.cellIndex, this.cm.getColumnWidth(this.cellIndex)+diff);
38135     },
38136
38137     autoOffset : function(){
38138         this.setDelta(0,0);
38139     }
38140 });/*
38141  * Based on:
38142  * Ext JS Library 1.1.1
38143  * Copyright(c) 2006-2007, Ext JS, LLC.
38144  *
38145  * Originally Released Under LGPL - original licence link has changed is not relivant.
38146  *
38147  * Fork - LGPL
38148  * <script type="text/javascript">
38149  */
38150  
38151 // private
38152 // This is a support class used internally by the Grid components
38153 Roo.grid.GridDragZone = function(grid, config){
38154     this.view = grid.getView();
38155     Roo.grid.GridDragZone.superclass.constructor.call(this, this.view.mainBody.dom, config);
38156     if(this.view.lockedBody){
38157         this.setHandleElId(Roo.id(this.view.mainBody.dom));
38158         this.setOuterHandleElId(Roo.id(this.view.lockedBody.dom));
38159     }
38160     this.scroll = false;
38161     this.grid = grid;
38162     this.ddel = document.createElement('div');
38163     this.ddel.className = 'x-grid-dd-wrap';
38164 };
38165
38166 Roo.extend(Roo.grid.GridDragZone, Roo.dd.DragZone, {
38167     ddGroup : "GridDD",
38168
38169     getDragData : function(e){
38170         var t = Roo.lib.Event.getTarget(e);
38171         var rowIndex = this.view.findRowIndex(t);
38172         var sm = this.grid.selModel;
38173             
38174         //Roo.log(rowIndex);
38175         
38176         if (sm.getSelectedCell) {
38177             // cell selection..
38178             if (!sm.getSelectedCell()) {
38179                 return false;
38180             }
38181             if (rowIndex != sm.getSelectedCell()[0]) {
38182                 return false;
38183             }
38184         
38185         }
38186         
38187         if(rowIndex !== false){
38188             
38189             // if editorgrid.. 
38190             
38191             
38192             //Roo.log([ sm.getSelectedCell() ? sm.getSelectedCell()[0] : 'NO' , rowIndex ]);
38193                
38194             //if(!sm.isSelected(rowIndex) || e.hasModifier()){
38195               //  
38196             //}
38197             if (e.hasModifier()){
38198                 sm.handleMouseDown(e, t); // non modifier buttons are handled by row select.
38199             }
38200             
38201             Roo.log("getDragData");
38202             
38203             return {
38204                 grid: this.grid,
38205                 ddel: this.ddel,
38206                 rowIndex: rowIndex,
38207                 selections:sm.getSelections ? sm.getSelections() : (
38208                     sm.getSelectedCell() ? [ this.grid.ds.getAt(sm.getSelectedCell()[0]) ] : []
38209                 )
38210             };
38211         }
38212         return false;
38213     },
38214
38215     onInitDrag : function(e){
38216         var data = this.dragData;
38217         this.ddel.innerHTML = this.grid.getDragDropText();
38218         this.proxy.update(this.ddel);
38219         // fire start drag?
38220     },
38221
38222     afterRepair : function(){
38223         this.dragging = false;
38224     },
38225
38226     getRepairXY : function(e, data){
38227         return false;
38228     },
38229
38230     onEndDrag : function(data, e){
38231         // fire end drag?
38232     },
38233
38234     onValidDrop : function(dd, e, id){
38235         // fire drag drop?
38236         this.hideProxy();
38237     },
38238
38239     beforeInvalidDrop : function(e, id){
38240
38241     }
38242 });/*
38243  * Based on:
38244  * Ext JS Library 1.1.1
38245  * Copyright(c) 2006-2007, Ext JS, LLC.
38246  *
38247  * Originally Released Under LGPL - original licence link has changed is not relivant.
38248  *
38249  * Fork - LGPL
38250  * <script type="text/javascript">
38251  */
38252  
38253
38254 /**
38255  * @class Roo.grid.ColumnModel
38256  * @extends Roo.util.Observable
38257  * This is the default implementation of a ColumnModel used by the Grid. It defines
38258  * the columns in the grid.
38259  * <br>Usage:<br>
38260  <pre><code>
38261  var colModel = new Roo.grid.ColumnModel([
38262         {header: "Ticker", width: 60, sortable: true, locked: true},
38263         {header: "Company Name", width: 150, sortable: true},
38264         {header: "Market Cap.", width: 100, sortable: true},
38265         {header: "$ Sales", width: 100, sortable: true, renderer: money},
38266         {header: "Employees", width: 100, sortable: true, resizable: false}
38267  ]);
38268  </code></pre>
38269  * <p>
38270  
38271  * The config options listed for this class are options which may appear in each
38272  * individual column definition.
38273  * <br/>RooJS Fix - column id's are not sequential but use Roo.id() - fixes bugs with layouts.
38274  * @constructor
38275  * @param {Object} config An Array of column config objects. See this class's
38276  * config objects for details.
38277 */
38278 Roo.grid.ColumnModel = function(config){
38279         /**
38280      * The config passed into the constructor
38281      */
38282     this.config = config;
38283     this.lookup = {};
38284
38285     // if no id, create one
38286     // if the column does not have a dataIndex mapping,
38287     // map it to the order it is in the config
38288     for(var i = 0, len = config.length; i < len; i++){
38289         var c = config[i];
38290         if(typeof c.dataIndex == "undefined"){
38291             c.dataIndex = i;
38292         }
38293         if(typeof c.renderer == "string"){
38294             c.renderer = Roo.util.Format[c.renderer];
38295         }
38296         if(typeof c.id == "undefined"){
38297             c.id = Roo.id();
38298         }
38299         if(c.editor && c.editor.xtype){
38300             c.editor  = Roo.factory(c.editor, Roo.grid);
38301         }
38302         if(c.editor && c.editor.isFormField){
38303             c.editor = new Roo.grid.GridEditor(c.editor);
38304         }
38305         this.lookup[c.id] = c;
38306     }
38307
38308     /**
38309      * The width of columns which have no width specified (defaults to 100)
38310      * @type Number
38311      */
38312     this.defaultWidth = 100;
38313
38314     /**
38315      * Default sortable of columns which have no sortable specified (defaults to false)
38316      * @type Boolean
38317      */
38318     this.defaultSortable = false;
38319
38320     this.addEvents({
38321         /**
38322              * @event widthchange
38323              * Fires when the width of a column changes.
38324              * @param {ColumnModel} this
38325              * @param {Number} columnIndex The column index
38326              * @param {Number} newWidth The new width
38327              */
38328             "widthchange": true,
38329         /**
38330              * @event headerchange
38331              * Fires when the text of a header changes.
38332              * @param {ColumnModel} this
38333              * @param {Number} columnIndex The column index
38334              * @param {Number} newText The new header text
38335              */
38336             "headerchange": true,
38337         /**
38338              * @event hiddenchange
38339              * Fires when a column is hidden or "unhidden".
38340              * @param {ColumnModel} this
38341              * @param {Number} columnIndex The column index
38342              * @param {Boolean} hidden true if hidden, false otherwise
38343              */
38344             "hiddenchange": true,
38345             /**
38346          * @event columnmoved
38347          * Fires when a column is moved.
38348          * @param {ColumnModel} this
38349          * @param {Number} oldIndex
38350          * @param {Number} newIndex
38351          */
38352         "columnmoved" : true,
38353         /**
38354          * @event columlockchange
38355          * Fires when a column's locked state is changed
38356          * @param {ColumnModel} this
38357          * @param {Number} colIndex
38358          * @param {Boolean} locked true if locked
38359          */
38360         "columnlockchange" : true
38361     });
38362     Roo.grid.ColumnModel.superclass.constructor.call(this);
38363 };
38364 Roo.extend(Roo.grid.ColumnModel, Roo.util.Observable, {
38365     /**
38366      * @cfg {String} header The header text to display in the Grid view.
38367      */
38368     /**
38369      * @cfg {String} dataIndex (Optional) The name of the field in the grid's {@link Roo.data.Store}'s
38370      * {@link Roo.data.Record} definition from which to draw the column's value. If not
38371      * specified, the column's index is used as an index into the Record's data Array.
38372      */
38373     /**
38374      * @cfg {Number} width (Optional) The initial width in pixels of the column. Using this
38375      * instead of {@link Roo.grid.Grid#autoSizeColumns} is more efficient.
38376      */
38377     /**
38378      * @cfg {Boolean} sortable (Optional) True if sorting is to be allowed on this column.
38379      * Defaults to the value of the {@link #defaultSortable} property.
38380      * Whether local/remote sorting is used is specified in {@link Roo.data.Store#remoteSort}.
38381      */
38382     /**
38383      * @cfg {Boolean} locked (Optional) True to lock the column in place while scrolling the Grid.  Defaults to false.
38384      */
38385     /**
38386      * @cfg {Boolean} fixed (Optional) True if the column width cannot be changed.  Defaults to false.
38387      */
38388     /**
38389      * @cfg {Boolean} resizable (Optional) False to disable column resizing. Defaults to true.
38390      */
38391     /**
38392      * @cfg {Boolean} hidden (Optional) True to hide the column. Defaults to false.
38393      */
38394     /**
38395      * @cfg {Function} renderer (Optional) A function used to generate HTML markup for a cell
38396      * given the cell's data value. See {@link #setRenderer}. If not specified, the
38397      * default renderer uses the raw data value.
38398      */
38399        /**
38400      * @cfg {Roo.grid.GridEditor} editor (Optional) For grid editors - returns the grid editor 
38401      */
38402     /**
38403      * @cfg {String} align (Optional) Set the CSS text-align property of the column.  Defaults to undefined.
38404      */
38405
38406     /**
38407      * Returns the id of the column at the specified index.
38408      * @param {Number} index The column index
38409      * @return {String} the id
38410      */
38411     getColumnId : function(index){
38412         return this.config[index].id;
38413     },
38414
38415     /**
38416      * Returns the column for a specified id.
38417      * @param {String} id The column id
38418      * @return {Object} the column
38419      */
38420     getColumnById : function(id){
38421         return this.lookup[id];
38422     },
38423
38424     
38425     /**
38426      * Returns the column for a specified dataIndex.
38427      * @param {String} dataIndex The column dataIndex
38428      * @return {Object|Boolean} the column or false if not found
38429      */
38430     getColumnByDataIndex: function(dataIndex){
38431         var index = this.findColumnIndex(dataIndex);
38432         return index > -1 ? this.config[index] : false;
38433     },
38434     
38435     /**
38436      * Returns the index for a specified column id.
38437      * @param {String} id The column id
38438      * @return {Number} the index, or -1 if not found
38439      */
38440     getIndexById : function(id){
38441         for(var i = 0, len = this.config.length; i < len; i++){
38442             if(this.config[i].id == id){
38443                 return i;
38444             }
38445         }
38446         return -1;
38447     },
38448     
38449     /**
38450      * Returns the index for a specified column dataIndex.
38451      * @param {String} dataIndex The column dataIndex
38452      * @return {Number} the index, or -1 if not found
38453      */
38454     
38455     findColumnIndex : function(dataIndex){
38456         for(var i = 0, len = this.config.length; i < len; i++){
38457             if(this.config[i].dataIndex == dataIndex){
38458                 return i;
38459             }
38460         }
38461         return -1;
38462     },
38463     
38464     
38465     moveColumn : function(oldIndex, newIndex){
38466         var c = this.config[oldIndex];
38467         this.config.splice(oldIndex, 1);
38468         this.config.splice(newIndex, 0, c);
38469         this.dataMap = null;
38470         this.fireEvent("columnmoved", this, oldIndex, newIndex);
38471     },
38472
38473     isLocked : function(colIndex){
38474         return this.config[colIndex].locked === true;
38475     },
38476
38477     setLocked : function(colIndex, value, suppressEvent){
38478         if(this.isLocked(colIndex) == value){
38479             return;
38480         }
38481         this.config[colIndex].locked = value;
38482         if(!suppressEvent){
38483             this.fireEvent("columnlockchange", this, colIndex, value);
38484         }
38485     },
38486
38487     getTotalLockedWidth : function(){
38488         var totalWidth = 0;
38489         for(var i = 0; i < this.config.length; i++){
38490             if(this.isLocked(i) && !this.isHidden(i)){
38491                 this.totalWidth += this.getColumnWidth(i);
38492             }
38493         }
38494         return totalWidth;
38495     },
38496
38497     getLockedCount : function(){
38498         for(var i = 0, len = this.config.length; i < len; i++){
38499             if(!this.isLocked(i)){
38500                 return i;
38501             }
38502         }
38503     },
38504
38505     /**
38506      * Returns the number of columns.
38507      * @return {Number}
38508      */
38509     getColumnCount : function(visibleOnly){
38510         if(visibleOnly === true){
38511             var c = 0;
38512             for(var i = 0, len = this.config.length; i < len; i++){
38513                 if(!this.isHidden(i)){
38514                     c++;
38515                 }
38516             }
38517             return c;
38518         }
38519         return this.config.length;
38520     },
38521
38522     /**
38523      * Returns the column configs that return true by the passed function that is called with (columnConfig, index)
38524      * @param {Function} fn
38525      * @param {Object} scope (optional)
38526      * @return {Array} result
38527      */
38528     getColumnsBy : function(fn, scope){
38529         var r = [];
38530         for(var i = 0, len = this.config.length; i < len; i++){
38531             var c = this.config[i];
38532             if(fn.call(scope||this, c, i) === true){
38533                 r[r.length] = c;
38534             }
38535         }
38536         return r;
38537     },
38538
38539     /**
38540      * Returns true if the specified column is sortable.
38541      * @param {Number} col The column index
38542      * @return {Boolean}
38543      */
38544     isSortable : function(col){
38545         if(typeof this.config[col].sortable == "undefined"){
38546             return this.defaultSortable;
38547         }
38548         return this.config[col].sortable;
38549     },
38550
38551     /**
38552      * Returns the rendering (formatting) function defined for the column.
38553      * @param {Number} col The column index.
38554      * @return {Function} The function used to render the cell. See {@link #setRenderer}.
38555      */
38556     getRenderer : function(col){
38557         if(!this.config[col].renderer){
38558             return Roo.grid.ColumnModel.defaultRenderer;
38559         }
38560         return this.config[col].renderer;
38561     },
38562
38563     /**
38564      * Sets the rendering (formatting) function for a column.
38565      * @param {Number} col The column index
38566      * @param {Function} fn The function to use to process the cell's raw data
38567      * to return HTML markup for the grid view. The render function is called with
38568      * the following parameters:<ul>
38569      * <li>Data value.</li>
38570      * <li>Cell metadata. An object in which you may set the following attributes:<ul>
38571      * <li>css A CSS style string to apply to the table cell.</li>
38572      * <li>attr An HTML attribute definition string to apply to the data container element <i>within</i> the table cell.</li></ul>
38573      * <li>The {@link Roo.data.Record} from which the data was extracted.</li>
38574      * <li>Row index</li>
38575      * <li>Column index</li>
38576      * <li>The {@link Roo.data.Store} object from which the Record was extracted</li></ul>
38577      */
38578     setRenderer : function(col, fn){
38579         this.config[col].renderer = fn;
38580     },
38581
38582     /**
38583      * Returns the width for the specified column.
38584      * @param {Number} col The column index
38585      * @return {Number}
38586      */
38587     getColumnWidth : function(col){
38588         return this.config[col].width * 1 || this.defaultWidth;
38589     },
38590
38591     /**
38592      * Sets the width for a column.
38593      * @param {Number} col The column index
38594      * @param {Number} width The new width
38595      */
38596     setColumnWidth : function(col, width, suppressEvent){
38597         this.config[col].width = width;
38598         this.totalWidth = null;
38599         if(!suppressEvent){
38600              this.fireEvent("widthchange", this, col, width);
38601         }
38602     },
38603
38604     /**
38605      * Returns the total width of all columns.
38606      * @param {Boolean} includeHidden True to include hidden column widths
38607      * @return {Number}
38608      */
38609     getTotalWidth : function(includeHidden){
38610         if(!this.totalWidth){
38611             this.totalWidth = 0;
38612             for(var i = 0, len = this.config.length; i < len; i++){
38613                 if(includeHidden || !this.isHidden(i)){
38614                     this.totalWidth += this.getColumnWidth(i);
38615                 }
38616             }
38617         }
38618         return this.totalWidth;
38619     },
38620
38621     /**
38622      * Returns the header for the specified column.
38623      * @param {Number} col The column index
38624      * @return {String}
38625      */
38626     getColumnHeader : function(col){
38627         return this.config[col].header;
38628     },
38629
38630     /**
38631      * Sets the header for a column.
38632      * @param {Number} col The column index
38633      * @param {String} header The new header
38634      */
38635     setColumnHeader : function(col, header){
38636         this.config[col].header = header;
38637         this.fireEvent("headerchange", this, col, header);
38638     },
38639
38640     /**
38641      * Returns the tooltip for the specified column.
38642      * @param {Number} col The column index
38643      * @return {String}
38644      */
38645     getColumnTooltip : function(col){
38646             return this.config[col].tooltip;
38647     },
38648     /**
38649      * Sets the tooltip for a column.
38650      * @param {Number} col The column index
38651      * @param {String} tooltip The new tooltip
38652      */
38653     setColumnTooltip : function(col, tooltip){
38654             this.config[col].tooltip = tooltip;
38655     },
38656
38657     /**
38658      * Returns the dataIndex for the specified column.
38659      * @param {Number} col The column index
38660      * @return {Number}
38661      */
38662     getDataIndex : function(col){
38663         return this.config[col].dataIndex;
38664     },
38665
38666     /**
38667      * Sets the dataIndex for a column.
38668      * @param {Number} col The column index
38669      * @param {Number} dataIndex The new dataIndex
38670      */
38671     setDataIndex : function(col, dataIndex){
38672         this.config[col].dataIndex = dataIndex;
38673     },
38674
38675     
38676     
38677     /**
38678      * Returns true if the cell is editable.
38679      * @param {Number} colIndex The column index
38680      * @param {Number} rowIndex The row index
38681      * @return {Boolean}
38682      */
38683     isCellEditable : function(colIndex, rowIndex){
38684         return (this.config[colIndex].editable || (typeof this.config[colIndex].editable == "undefined" && this.config[colIndex].editor)) ? true : false;
38685     },
38686
38687     /**
38688      * Returns the editor defined for the cell/column.
38689      * return false or null to disable editing.
38690      * @param {Number} colIndex The column index
38691      * @param {Number} rowIndex The row index
38692      * @return {Object}
38693      */
38694     getCellEditor : function(colIndex, rowIndex){
38695         return this.config[colIndex].editor;
38696     },
38697
38698     /**
38699      * Sets if a column is editable.
38700      * @param {Number} col The column index
38701      * @param {Boolean} editable True if the column is editable
38702      */
38703     setEditable : function(col, editable){
38704         this.config[col].editable = editable;
38705     },
38706
38707
38708     /**
38709      * Returns true if the column is hidden.
38710      * @param {Number} colIndex The column index
38711      * @return {Boolean}
38712      */
38713     isHidden : function(colIndex){
38714         return this.config[colIndex].hidden;
38715     },
38716
38717
38718     /**
38719      * Returns true if the column width cannot be changed
38720      */
38721     isFixed : function(colIndex){
38722         return this.config[colIndex].fixed;
38723     },
38724
38725     /**
38726      * Returns true if the column can be resized
38727      * @return {Boolean}
38728      */
38729     isResizable : function(colIndex){
38730         return colIndex >= 0 && this.config[colIndex].resizable !== false && this.config[colIndex].fixed !== true;
38731     },
38732     /**
38733      * Sets if a column is hidden.
38734      * @param {Number} colIndex The column index
38735      * @param {Boolean} hidden True if the column is hidden
38736      */
38737     setHidden : function(colIndex, hidden){
38738         this.config[colIndex].hidden = hidden;
38739         this.totalWidth = null;
38740         this.fireEvent("hiddenchange", this, colIndex, hidden);
38741     },
38742
38743     /**
38744      * Sets the editor for a column.
38745      * @param {Number} col The column index
38746      * @param {Object} editor The editor object
38747      */
38748     setEditor : function(col, editor){
38749         this.config[col].editor = editor;
38750     }
38751 });
38752
38753 Roo.grid.ColumnModel.defaultRenderer = function(value){
38754         if(typeof value == "string" && value.length < 1){
38755             return "&#160;";
38756         }
38757         return value;
38758 };
38759
38760 // Alias for backwards compatibility
38761 Roo.grid.DefaultColumnModel = Roo.grid.ColumnModel;
38762 /*
38763  * Based on:
38764  * Ext JS Library 1.1.1
38765  * Copyright(c) 2006-2007, Ext JS, LLC.
38766  *
38767  * Originally Released Under LGPL - original licence link has changed is not relivant.
38768  *
38769  * Fork - LGPL
38770  * <script type="text/javascript">
38771  */
38772
38773 /**
38774  * @class Roo.grid.AbstractSelectionModel
38775  * @extends Roo.util.Observable
38776  * Abstract base class for grid SelectionModels.  It provides the interface that should be
38777  * implemented by descendant classes.  This class should not be directly instantiated.
38778  * @constructor
38779  */
38780 Roo.grid.AbstractSelectionModel = function(){
38781     this.locked = false;
38782     Roo.grid.AbstractSelectionModel.superclass.constructor.call(this);
38783 };
38784
38785 Roo.extend(Roo.grid.AbstractSelectionModel, Roo.util.Observable,  {
38786     /** @ignore Called by the grid automatically. Do not call directly. */
38787     init : function(grid){
38788         this.grid = grid;
38789         this.initEvents();
38790     },
38791
38792     /**
38793      * Locks the selections.
38794      */
38795     lock : function(){
38796         this.locked = true;
38797     },
38798
38799     /**
38800      * Unlocks the selections.
38801      */
38802     unlock : function(){
38803         this.locked = false;
38804     },
38805
38806     /**
38807      * Returns true if the selections are locked.
38808      * @return {Boolean}
38809      */
38810     isLocked : function(){
38811         return this.locked;
38812     }
38813 });/*
38814  * Based on:
38815  * Ext JS Library 1.1.1
38816  * Copyright(c) 2006-2007, Ext JS, LLC.
38817  *
38818  * Originally Released Under LGPL - original licence link has changed is not relivant.
38819  *
38820  * Fork - LGPL
38821  * <script type="text/javascript">
38822  */
38823 /**
38824  * @extends Roo.grid.AbstractSelectionModel
38825  * @class Roo.grid.RowSelectionModel
38826  * The default SelectionModel used by {@link Roo.grid.Grid}.
38827  * It supports multiple selections and keyboard selection/navigation. 
38828  * @constructor
38829  * @param {Object} config
38830  */
38831 Roo.grid.RowSelectionModel = function(config){
38832     Roo.apply(this, config);
38833     this.selections = new Roo.util.MixedCollection(false, function(o){
38834         return o.id;
38835     });
38836
38837     this.last = false;
38838     this.lastActive = false;
38839
38840     this.addEvents({
38841         /**
38842              * @event selectionchange
38843              * Fires when the selection changes
38844              * @param {SelectionModel} this
38845              */
38846             "selectionchange" : true,
38847         /**
38848              * @event afterselectionchange
38849              * Fires after the selection changes (eg. by key press or clicking)
38850              * @param {SelectionModel} this
38851              */
38852             "afterselectionchange" : true,
38853         /**
38854              * @event beforerowselect
38855              * Fires when a row is selected being selected, return false to cancel.
38856              * @param {SelectionModel} this
38857              * @param {Number} rowIndex The selected index
38858              * @param {Boolean} keepExisting False if other selections will be cleared
38859              */
38860             "beforerowselect" : true,
38861         /**
38862              * @event rowselect
38863              * Fires when a row is selected.
38864              * @param {SelectionModel} this
38865              * @param {Number} rowIndex The selected index
38866              * @param {Roo.data.Record} r The record
38867              */
38868             "rowselect" : true,
38869         /**
38870              * @event rowdeselect
38871              * Fires when a row is deselected.
38872              * @param {SelectionModel} this
38873              * @param {Number} rowIndex The selected index
38874              */
38875         "rowdeselect" : true
38876     });
38877     Roo.grid.RowSelectionModel.superclass.constructor.call(this);
38878     this.locked = false;
38879 };
38880
38881 Roo.extend(Roo.grid.RowSelectionModel, Roo.grid.AbstractSelectionModel,  {
38882     /**
38883      * @cfg {Boolean} singleSelect
38884      * True to allow selection of only one row at a time (defaults to false)
38885      */
38886     singleSelect : false,
38887
38888     // private
38889     initEvents : function(){
38890
38891         if(!this.grid.enableDragDrop && !this.grid.enableDrag){
38892             this.grid.on("mousedown", this.handleMouseDown, this);
38893         }else{ // allow click to work like normal
38894             this.grid.on("rowclick", this.handleDragableRowClick, this);
38895         }
38896
38897         this.rowNav = new Roo.KeyNav(this.grid.getGridEl(), {
38898             "up" : function(e){
38899                 if(!e.shiftKey){
38900                     this.selectPrevious(e.shiftKey);
38901                 }else if(this.last !== false && this.lastActive !== false){
38902                     var last = this.last;
38903                     this.selectRange(this.last,  this.lastActive-1);
38904                     this.grid.getView().focusRow(this.lastActive);
38905                     if(last !== false){
38906                         this.last = last;
38907                     }
38908                 }else{
38909                     this.selectFirstRow();
38910                 }
38911                 this.fireEvent("afterselectionchange", this);
38912             },
38913             "down" : function(e){
38914                 if(!e.shiftKey){
38915                     this.selectNext(e.shiftKey);
38916                 }else if(this.last !== false && this.lastActive !== false){
38917                     var last = this.last;
38918                     this.selectRange(this.last,  this.lastActive+1);
38919                     this.grid.getView().focusRow(this.lastActive);
38920                     if(last !== false){
38921                         this.last = last;
38922                     }
38923                 }else{
38924                     this.selectFirstRow();
38925                 }
38926                 this.fireEvent("afterselectionchange", this);
38927             },
38928             scope: this
38929         });
38930
38931         var view = this.grid.view;
38932         view.on("refresh", this.onRefresh, this);
38933         view.on("rowupdated", this.onRowUpdated, this);
38934         view.on("rowremoved", this.onRemove, this);
38935     },
38936
38937     // private
38938     onRefresh : function(){
38939         var ds = this.grid.dataSource, i, v = this.grid.view;
38940         var s = this.selections;
38941         s.each(function(r){
38942             if((i = ds.indexOfId(r.id)) != -1){
38943                 v.onRowSelect(i);
38944             }else{
38945                 s.remove(r);
38946             }
38947         });
38948     },
38949
38950     // private
38951     onRemove : function(v, index, r){
38952         this.selections.remove(r);
38953     },
38954
38955     // private
38956     onRowUpdated : function(v, index, r){
38957         if(this.isSelected(r)){
38958             v.onRowSelect(index);
38959         }
38960     },
38961
38962     /**
38963      * Select records.
38964      * @param {Array} records The records to select
38965      * @param {Boolean} keepExisting (optional) True to keep existing selections
38966      */
38967     selectRecords : function(records, keepExisting){
38968         if(!keepExisting){
38969             this.clearSelections();
38970         }
38971         var ds = this.grid.dataSource;
38972         for(var i = 0, len = records.length; i < len; i++){
38973             this.selectRow(ds.indexOf(records[i]), true);
38974         }
38975     },
38976
38977     /**
38978      * Gets the number of selected rows.
38979      * @return {Number}
38980      */
38981     getCount : function(){
38982         return this.selections.length;
38983     },
38984
38985     /**
38986      * Selects the first row in the grid.
38987      */
38988     selectFirstRow : function(){
38989         this.selectRow(0);
38990     },
38991
38992     /**
38993      * Select the last row.
38994      * @param {Boolean} keepExisting (optional) True to keep existing selections
38995      */
38996     selectLastRow : function(keepExisting){
38997         this.selectRow(this.grid.dataSource.getCount() - 1, keepExisting);
38998     },
38999
39000     /**
39001      * Selects the row immediately following the last selected row.
39002      * @param {Boolean} keepExisting (optional) True to keep existing selections
39003      */
39004     selectNext : function(keepExisting){
39005         if(this.last !== false && (this.last+1) < this.grid.dataSource.getCount()){
39006             this.selectRow(this.last+1, keepExisting);
39007             this.grid.getView().focusRow(this.last);
39008         }
39009     },
39010
39011     /**
39012      * Selects the row that precedes the last selected row.
39013      * @param {Boolean} keepExisting (optional) True to keep existing selections
39014      */
39015     selectPrevious : function(keepExisting){
39016         if(this.last){
39017             this.selectRow(this.last-1, keepExisting);
39018             this.grid.getView().focusRow(this.last);
39019         }
39020     },
39021
39022     /**
39023      * Returns the selected records
39024      * @return {Array} Array of selected records
39025      */
39026     getSelections : function(){
39027         return [].concat(this.selections.items);
39028     },
39029
39030     /**
39031      * Returns the first selected record.
39032      * @return {Record}
39033      */
39034     getSelected : function(){
39035         return this.selections.itemAt(0);
39036     },
39037
39038
39039     /**
39040      * Clears all selections.
39041      */
39042     clearSelections : function(fast){
39043         if(this.locked) return;
39044         if(fast !== true){
39045             var ds = this.grid.dataSource;
39046             var s = this.selections;
39047             s.each(function(r){
39048                 this.deselectRow(ds.indexOfId(r.id));
39049             }, this);
39050             s.clear();
39051         }else{
39052             this.selections.clear();
39053         }
39054         this.last = false;
39055     },
39056
39057
39058     /**
39059      * Selects all rows.
39060      */
39061     selectAll : function(){
39062         if(this.locked) return;
39063         this.selections.clear();
39064         for(var i = 0, len = this.grid.dataSource.getCount(); i < len; i++){
39065             this.selectRow(i, true);
39066         }
39067     },
39068
39069     /**
39070      * Returns True if there is a selection.
39071      * @return {Boolean}
39072      */
39073     hasSelection : function(){
39074         return this.selections.length > 0;
39075     },
39076
39077     /**
39078      * Returns True if the specified row is selected.
39079      * @param {Number/Record} record The record or index of the record to check
39080      * @return {Boolean}
39081      */
39082     isSelected : function(index){
39083         var r = typeof index == "number" ? this.grid.dataSource.getAt(index) : index;
39084         return (r && this.selections.key(r.id) ? true : false);
39085     },
39086
39087     /**
39088      * Returns True if the specified record id is selected.
39089      * @param {String} id The id of record to check
39090      * @return {Boolean}
39091      */
39092     isIdSelected : function(id){
39093         return (this.selections.key(id) ? true : false);
39094     },
39095
39096     // private
39097     handleMouseDown : function(e, t){
39098         var view = this.grid.getView(), rowIndex;
39099         if(this.isLocked() || (rowIndex = view.findRowIndex(t)) === false){
39100             return;
39101         };
39102         if(e.shiftKey && this.last !== false){
39103             var last = this.last;
39104             this.selectRange(last, rowIndex, e.ctrlKey);
39105             this.last = last; // reset the last
39106             view.focusRow(rowIndex);
39107         }else{
39108             var isSelected = this.isSelected(rowIndex);
39109             if(e.button !== 0 && isSelected){
39110                 view.focusRow(rowIndex);
39111             }else if(e.ctrlKey && isSelected){
39112                 this.deselectRow(rowIndex);
39113             }else if(!isSelected){
39114                 this.selectRow(rowIndex, e.button === 0 && (e.ctrlKey || e.shiftKey));
39115                 view.focusRow(rowIndex);
39116             }
39117         }
39118         this.fireEvent("afterselectionchange", this);
39119     },
39120     // private
39121     handleDragableRowClick :  function(grid, rowIndex, e) 
39122     {
39123         if(e.button === 0 && !e.shiftKey && !e.ctrlKey) {
39124             this.selectRow(rowIndex, false);
39125             grid.view.focusRow(rowIndex);
39126              this.fireEvent("afterselectionchange", this);
39127         }
39128     },
39129     
39130     /**
39131      * Selects multiple rows.
39132      * @param {Array} rows Array of the indexes of the row to select
39133      * @param {Boolean} keepExisting (optional) True to keep existing selections
39134      */
39135     selectRows : function(rows, keepExisting){
39136         if(!keepExisting){
39137             this.clearSelections();
39138         }
39139         for(var i = 0, len = rows.length; i < len; i++){
39140             this.selectRow(rows[i], true);
39141         }
39142     },
39143
39144     /**
39145      * Selects a range of rows. All rows in between startRow and endRow are also selected.
39146      * @param {Number} startRow The index of the first row in the range
39147      * @param {Number} endRow The index of the last row in the range
39148      * @param {Boolean} keepExisting (optional) True to retain existing selections
39149      */
39150     selectRange : function(startRow, endRow, keepExisting){
39151         if(this.locked) return;
39152         if(!keepExisting){
39153             this.clearSelections();
39154         }
39155         if(startRow <= endRow){
39156             for(var i = startRow; i <= endRow; i++){
39157                 this.selectRow(i, true);
39158             }
39159         }else{
39160             for(var i = startRow; i >= endRow; i--){
39161                 this.selectRow(i, true);
39162             }
39163         }
39164     },
39165
39166     /**
39167      * Deselects a range of rows. All rows in between startRow and endRow are also deselected.
39168      * @param {Number} startRow The index of the first row in the range
39169      * @param {Number} endRow The index of the last row in the range
39170      */
39171     deselectRange : function(startRow, endRow, preventViewNotify){
39172         if(this.locked) return;
39173         for(var i = startRow; i <= endRow; i++){
39174             this.deselectRow(i, preventViewNotify);
39175         }
39176     },
39177
39178     /**
39179      * Selects a row.
39180      * @param {Number} row The index of the row to select
39181      * @param {Boolean} keepExisting (optional) True to keep existing selections
39182      */
39183     selectRow : function(index, keepExisting, preventViewNotify){
39184         if(this.locked || (index < 0 || index >= this.grid.dataSource.getCount())) return;
39185         if(this.fireEvent("beforerowselect", this, index, keepExisting) !== false){
39186             if(!keepExisting || this.singleSelect){
39187                 this.clearSelections();
39188             }
39189             var r = this.grid.dataSource.getAt(index);
39190             this.selections.add(r);
39191             this.last = this.lastActive = index;
39192             if(!preventViewNotify){
39193                 this.grid.getView().onRowSelect(index);
39194             }
39195             this.fireEvent("rowselect", this, index, r);
39196             this.fireEvent("selectionchange", this);
39197         }
39198     },
39199
39200     /**
39201      * Deselects a row.
39202      * @param {Number} row The index of the row to deselect
39203      */
39204     deselectRow : function(index, preventViewNotify){
39205         if(this.locked) return;
39206         if(this.last == index){
39207             this.last = false;
39208         }
39209         if(this.lastActive == index){
39210             this.lastActive = false;
39211         }
39212         var r = this.grid.dataSource.getAt(index);
39213         this.selections.remove(r);
39214         if(!preventViewNotify){
39215             this.grid.getView().onRowDeselect(index);
39216         }
39217         this.fireEvent("rowdeselect", this, index);
39218         this.fireEvent("selectionchange", this);
39219     },
39220
39221     // private
39222     restoreLast : function(){
39223         if(this._last){
39224             this.last = this._last;
39225         }
39226     },
39227
39228     // private
39229     acceptsNav : function(row, col, cm){
39230         return !cm.isHidden(col) && cm.isCellEditable(col, row);
39231     },
39232
39233     // private
39234     onEditorKey : function(field, e){
39235         var k = e.getKey(), newCell, g = this.grid, ed = g.activeEditor;
39236         if(k == e.TAB){
39237             e.stopEvent();
39238             ed.completeEdit();
39239             if(e.shiftKey){
39240                 newCell = g.walkCells(ed.row, ed.col-1, -1, this.acceptsNav, this);
39241             }else{
39242                 newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
39243             }
39244         }else if(k == e.ENTER && !e.ctrlKey){
39245             e.stopEvent();
39246             ed.completeEdit();
39247             if(e.shiftKey){
39248                 newCell = g.walkCells(ed.row-1, ed.col, -1, this.acceptsNav, this);
39249             }else{
39250                 newCell = g.walkCells(ed.row+1, ed.col, 1, this.acceptsNav, this);
39251             }
39252         }else if(k == e.ESC){
39253             ed.cancelEdit();
39254         }
39255         if(newCell){
39256             g.startEditing(newCell[0], newCell[1]);
39257         }
39258     }
39259 });/*
39260  * Based on:
39261  * Ext JS Library 1.1.1
39262  * Copyright(c) 2006-2007, Ext JS, LLC.
39263  *
39264  * Originally Released Under LGPL - original licence link has changed is not relivant.
39265  *
39266  * Fork - LGPL
39267  * <script type="text/javascript">
39268  */
39269 /**
39270  * @class Roo.grid.CellSelectionModel
39271  * @extends Roo.grid.AbstractSelectionModel
39272  * This class provides the basic implementation for cell selection in a grid.
39273  * @constructor
39274  * @param {Object} config The object containing the configuration of this model.
39275  * @cfg {Boolean} enter_is_tab Enter behaves the same as tab. (eg. goes to next cell) default: false
39276  */
39277 Roo.grid.CellSelectionModel = function(config){
39278     Roo.apply(this, config);
39279
39280     this.selection = null;
39281
39282     this.addEvents({
39283         /**
39284              * @event beforerowselect
39285              * Fires before a cell is selected.
39286              * @param {SelectionModel} this
39287              * @param {Number} rowIndex The selected row index
39288              * @param {Number} colIndex The selected cell index
39289              */
39290             "beforecellselect" : true,
39291         /**
39292              * @event cellselect
39293              * Fires when a cell is selected.
39294              * @param {SelectionModel} this
39295              * @param {Number} rowIndex The selected row index
39296              * @param {Number} colIndex The selected cell index
39297              */
39298             "cellselect" : true,
39299         /**
39300              * @event selectionchange
39301              * Fires when the active selection changes.
39302              * @param {SelectionModel} this
39303              * @param {Object} selection null for no selection or an object (o) with two properties
39304                 <ul>
39305                 <li>o.record: the record object for the row the selection is in</li>
39306                 <li>o.cell: An array of [rowIndex, columnIndex]</li>
39307                 </ul>
39308              */
39309             "selectionchange" : true,
39310         /**
39311              * @event tabend
39312              * Fires when the tab (or enter) was pressed on the last editable cell
39313              * You can use this to trigger add new row.
39314              * @param {SelectionModel} this
39315              */
39316             "tabend" : true,
39317          /**
39318              * @event beforeeditnext
39319              * Fires before the next editable sell is made active
39320              * You can use this to skip to another cell or fire the tabend
39321              *    if you set cell to false
39322              * @param {Object} eventdata object : { cell : [ row, col ] } 
39323              */
39324             "beforeeditnext" : true
39325     });
39326     Roo.grid.CellSelectionModel.superclass.constructor.call(this);
39327 };
39328
39329 Roo.extend(Roo.grid.CellSelectionModel, Roo.grid.AbstractSelectionModel,  {
39330     
39331     enter_is_tab: false,
39332
39333     /** @ignore */
39334     initEvents : function(){
39335         this.grid.on("mousedown", this.handleMouseDown, this);
39336         this.grid.getGridEl().on(Roo.isIE ? "keydown" : "keypress", this.handleKeyDown, this);
39337         var view = this.grid.view;
39338         view.on("refresh", this.onViewChange, this);
39339         view.on("rowupdated", this.onRowUpdated, this);
39340         view.on("beforerowremoved", this.clearSelections, this);
39341         view.on("beforerowsinserted", this.clearSelections, this);
39342         if(this.grid.isEditor){
39343             this.grid.on("beforeedit", this.beforeEdit,  this);
39344         }
39345     },
39346
39347         //private
39348     beforeEdit : function(e){
39349         this.select(e.row, e.column, false, true, e.record);
39350     },
39351
39352         //private
39353     onRowUpdated : function(v, index, r){
39354         if(this.selection && this.selection.record == r){
39355             v.onCellSelect(index, this.selection.cell[1]);
39356         }
39357     },
39358
39359         //private
39360     onViewChange : function(){
39361         this.clearSelections(true);
39362     },
39363
39364         /**
39365          * Returns the currently selected cell,.
39366          * @return {Array} The selected cell (row, column) or null if none selected.
39367          */
39368     getSelectedCell : function(){
39369         return this.selection ? this.selection.cell : null;
39370     },
39371
39372     /**
39373      * Clears all selections.
39374      * @param {Boolean} true to prevent the gridview from being notified about the change.
39375      */
39376     clearSelections : function(preventNotify){
39377         var s = this.selection;
39378         if(s){
39379             if(preventNotify !== true){
39380                 this.grid.view.onCellDeselect(s.cell[0], s.cell[1]);
39381             }
39382             this.selection = null;
39383             this.fireEvent("selectionchange", this, null);
39384         }
39385     },
39386
39387     /**
39388      * Returns true if there is a selection.
39389      * @return {Boolean}
39390      */
39391     hasSelection : function(){
39392         return this.selection ? true : false;
39393     },
39394
39395     /** @ignore */
39396     handleMouseDown : function(e, t){
39397         var v = this.grid.getView();
39398         if(this.isLocked()){
39399             return;
39400         };
39401         var row = v.findRowIndex(t);
39402         var cell = v.findCellIndex(t);
39403         if(row !== false && cell !== false){
39404             this.select(row, cell);
39405         }
39406     },
39407
39408     /**
39409      * Selects a cell.
39410      * @param {Number} rowIndex
39411      * @param {Number} collIndex
39412      */
39413     select : function(rowIndex, colIndex, preventViewNotify, preventFocus, /*internal*/ r){
39414         if(this.fireEvent("beforecellselect", this, rowIndex, colIndex) !== false){
39415             this.clearSelections();
39416             r = r || this.grid.dataSource.getAt(rowIndex);
39417             this.selection = {
39418                 record : r,
39419                 cell : [rowIndex, colIndex]
39420             };
39421             if(!preventViewNotify){
39422                 var v = this.grid.getView();
39423                 v.onCellSelect(rowIndex, colIndex);
39424                 if(preventFocus !== true){
39425                     v.focusCell(rowIndex, colIndex);
39426                 }
39427             }
39428             this.fireEvent("cellselect", this, rowIndex, colIndex);
39429             this.fireEvent("selectionchange", this, this.selection);
39430         }
39431     },
39432
39433         //private
39434     isSelectable : function(rowIndex, colIndex, cm){
39435         return !cm.isHidden(colIndex);
39436     },
39437
39438     /** @ignore */
39439     handleKeyDown : function(e){
39440         //Roo.log('Cell Sel Model handleKeyDown');
39441         if(!e.isNavKeyPress()){
39442             return;
39443         }
39444         var g = this.grid, s = this.selection;
39445         if(!s){
39446             e.stopEvent();
39447             var cell = g.walkCells(0, 0, 1, this.isSelectable,  this);
39448             if(cell){
39449                 this.select(cell[0], cell[1]);
39450             }
39451             return;
39452         }
39453         var sm = this;
39454         var walk = function(row, col, step){
39455             return g.walkCells(row, col, step, sm.isSelectable,  sm);
39456         };
39457         var k = e.getKey(), r = s.cell[0], c = s.cell[1];
39458         var newCell;
39459
39460       
39461
39462         switch(k){
39463             case e.TAB:
39464                 // handled by onEditorKey
39465                 if (g.isEditor && g.editing) {
39466                     return;
39467                 }
39468                 if(e.shiftKey) {
39469                     newCell = walk(r, c-1, -1);
39470                 } else {
39471                     newCell = walk(r, c+1, 1);
39472                 }
39473                 break;
39474             
39475             case e.DOWN:
39476                newCell = walk(r+1, c, 1);
39477                 break;
39478             
39479             case e.UP:
39480                 newCell = walk(r-1, c, -1);
39481                 break;
39482             
39483             case e.RIGHT:
39484                 newCell = walk(r, c+1, 1);
39485                 break;
39486             
39487             case e.LEFT:
39488                 newCell = walk(r, c-1, -1);
39489                 break;
39490             
39491             case e.ENTER:
39492                 
39493                 if(g.isEditor && !g.editing){
39494                    g.startEditing(r, c);
39495                    e.stopEvent();
39496                    return;
39497                 }
39498                 
39499                 
39500              break;
39501         };
39502         if(newCell){
39503             this.select(newCell[0], newCell[1]);
39504             e.stopEvent();
39505             
39506         }
39507     },
39508
39509     acceptsNav : function(row, col, cm){
39510         return !cm.isHidden(col) && cm.isCellEditable(col, row);
39511     },
39512     /**
39513      * Selects a cell.
39514      * @param {Number} field (not used) - as it's normally used as a listener
39515      * @param {Number} e - event - fake it by using
39516      *
39517      * var e = Roo.EventObjectImpl.prototype;
39518      * e.keyCode = e.TAB
39519      *
39520      * 
39521      */
39522     onEditorKey : function(field, e){
39523         
39524         var k = e.getKey(),
39525             newCell,
39526             g = this.grid,
39527             ed = g.activeEditor,
39528             forward = false;
39529         ///Roo.log('onEditorKey' + k);
39530         
39531         
39532         if (this.enter_is_tab && k == e.ENTER) {
39533             k = e.TAB;
39534         }
39535         
39536         if(k == e.TAB){
39537             if(e.shiftKey){
39538                 newCell = g.walkCells(ed.row, ed.col-1, -1, this.acceptsNav, this);
39539             }else{
39540                 newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
39541                 forward = true;
39542             }
39543             
39544             e.stopEvent();
39545             
39546         } else if(k == e.ENTER &&  !e.ctrlKey){
39547             ed.completeEdit();
39548             e.stopEvent();
39549             newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
39550         
39551                 } else if(k == e.ESC){
39552             ed.cancelEdit();
39553         }
39554                 
39555         if (newCell) {
39556             var ecall = { cell : newCell, forward : forward };
39557             this.fireEvent('beforeeditnext', ecall );
39558             newCell = ecall.cell;
39559                         forward = ecall.forward;
39560         }
39561                 
39562         if(newCell){
39563             //Roo.log('next cell after edit');
39564             g.startEditing.defer(100, g, [newCell[0], newCell[1]]);
39565         } else if (forward) {
39566             // tabbed past last
39567             this.fireEvent.defer(100, this, ['tabend',this]);
39568         }
39569     }
39570 });/*
39571  * Based on:
39572  * Ext JS Library 1.1.1
39573  * Copyright(c) 2006-2007, Ext JS, LLC.
39574  *
39575  * Originally Released Under LGPL - original licence link has changed is not relivant.
39576  *
39577  * Fork - LGPL
39578  * <script type="text/javascript">
39579  */
39580  
39581 /**
39582  * @class Roo.grid.EditorGrid
39583  * @extends Roo.grid.Grid
39584  * Class for creating and editable grid.
39585  * @param {String/HTMLElement/Roo.Element} container The element into which this grid will be rendered - 
39586  * The container MUST have some type of size defined for the grid to fill. The container will be 
39587  * automatically set to position relative if it isn't already.
39588  * @param {Object} dataSource The data model to bind to
39589  * @param {Object} colModel The column model with info about this grid's columns
39590  */
39591 Roo.grid.EditorGrid = function(container, config){
39592     Roo.grid.EditorGrid.superclass.constructor.call(this, container, config);
39593     this.getGridEl().addClass("xedit-grid");
39594
39595     if(!this.selModel){
39596         this.selModel = new Roo.grid.CellSelectionModel();
39597     }
39598
39599     this.activeEditor = null;
39600
39601         this.addEvents({
39602             /**
39603              * @event beforeedit
39604              * Fires before cell editing is triggered. The edit event object has the following properties <br />
39605              * <ul style="padding:5px;padding-left:16px;">
39606              * <li>grid - This grid</li>
39607              * <li>record - The record being edited</li>
39608              * <li>field - The field name being edited</li>
39609              * <li>value - The value for the field being edited.</li>
39610              * <li>row - The grid row index</li>
39611              * <li>column - The grid column index</li>
39612              * <li>cancel - Set this to true to cancel the edit or return false from your handler.</li>
39613              * </ul>
39614              * @param {Object} e An edit event (see above for description)
39615              */
39616             "beforeedit" : true,
39617             /**
39618              * @event afteredit
39619              * Fires after a cell is edited. <br />
39620              * <ul style="padding:5px;padding-left:16px;">
39621              * <li>grid - This grid</li>
39622              * <li>record - The record being edited</li>
39623              * <li>field - The field name being edited</li>
39624              * <li>value - The value being set</li>
39625              * <li>originalValue - The original value for the field, before the edit.</li>
39626              * <li>row - The grid row index</li>
39627              * <li>column - The grid column index</li>
39628              * </ul>
39629              * @param {Object} e An edit event (see above for description)
39630              */
39631             "afteredit" : true,
39632             /**
39633              * @event validateedit
39634              * Fires after a cell is edited, but before the value is set in the record. 
39635          * You can use this to modify the value being set in the field, Return false
39636              * to cancel the change. The edit event object has the following properties <br />
39637              * <ul style="padding:5px;padding-left:16px;">
39638          * <li>editor - This editor</li>
39639              * <li>grid - This grid</li>
39640              * <li>record - The record being edited</li>
39641              * <li>field - The field name being edited</li>
39642              * <li>value - The value being set</li>
39643              * <li>originalValue - The original value for the field, before the edit.</li>
39644              * <li>row - The grid row index</li>
39645              * <li>column - The grid column index</li>
39646              * <li>cancel - Set this to true to cancel the edit or return false from your handler.</li>
39647              * </ul>
39648              * @param {Object} e An edit event (see above for description)
39649              */
39650             "validateedit" : true
39651         });
39652     this.on("bodyscroll", this.stopEditing,  this);
39653     this.on(this.clicksToEdit == 1 ? "cellclick" : "celldblclick", this.onCellDblClick,  this);
39654 };
39655
39656 Roo.extend(Roo.grid.EditorGrid, Roo.grid.Grid, {
39657     /**
39658      * @cfg {Number} clicksToEdit
39659      * The number of clicks on a cell required to display the cell's editor (defaults to 2)
39660      */
39661     clicksToEdit: 2,
39662
39663     // private
39664     isEditor : true,
39665     // private
39666     trackMouseOver: false, // causes very odd FF errors
39667
39668     onCellDblClick : function(g, row, col){
39669         this.startEditing(row, col);
39670     },
39671
39672     onEditComplete : function(ed, value, startValue){
39673         this.editing = false;
39674         this.activeEditor = null;
39675         ed.un("specialkey", this.selModel.onEditorKey, this.selModel);
39676         var r = ed.record;
39677         var field = this.colModel.getDataIndex(ed.col);
39678         var e = {
39679             grid: this,
39680             record: r,
39681             field: field,
39682             originalValue: startValue,
39683             value: value,
39684             row: ed.row,
39685             column: ed.col,
39686             cancel:false,
39687             editor: ed
39688         };
39689         var cell = Roo.get(this.view.getCell(ed.row,ed.col))
39690         cell.show();
39691           
39692         if(String(value) !== String(startValue)){
39693             
39694             if(this.fireEvent("validateedit", e) !== false && !e.cancel){
39695                 r.set(field, e.value);
39696                 // if we are dealing with a combo box..
39697                 // then we also set the 'name' colum to be the displayField
39698                 if (ed.field.displayField && ed.field.name) {
39699                     r.set(ed.field.name, ed.field.el.dom.value);
39700                 }
39701                 
39702                 delete e.cancel; //?? why!!!
39703                 this.fireEvent("afteredit", e);
39704             }
39705         } else {
39706             this.fireEvent("afteredit", e); // always fire it!
39707         }
39708         this.view.focusCell(ed.row, ed.col);
39709     },
39710
39711     /**
39712      * Starts editing the specified for the specified row/column
39713      * @param {Number} rowIndex
39714      * @param {Number} colIndex
39715      */
39716     startEditing : function(row, col){
39717         this.stopEditing();
39718         if(this.colModel.isCellEditable(col, row)){
39719             this.view.ensureVisible(row, col, true);
39720           
39721             var r = this.dataSource.getAt(row);
39722             var field = this.colModel.getDataIndex(col);
39723             var cell = Roo.get(this.view.getCell(row,col));
39724             var e = {
39725                 grid: this,
39726                 record: r,
39727                 field: field,
39728                 value: r.data[field],
39729                 row: row,
39730                 column: col,
39731                 cancel:false 
39732             };
39733             if(this.fireEvent("beforeedit", e) !== false && !e.cancel){
39734                 this.editing = true;
39735                 var ed = this.colModel.getCellEditor(col, row);
39736                 
39737                 if (!ed) {
39738                     return;
39739                 }
39740                 if(!ed.rendered){
39741                     ed.render(ed.parentEl || document.body);
39742                 }
39743                 ed.field.reset();
39744                
39745                 cell.hide();
39746                 
39747                 (function(){ // complex but required for focus issues in safari, ie and opera
39748                     ed.row = row;
39749                     ed.col = col;
39750                     ed.record = r;
39751                     ed.on("complete",   this.onEditComplete,        this,       {single: true});
39752                     ed.on("specialkey", this.selModel.onEditorKey,  this.selModel);
39753                     this.activeEditor = ed;
39754                     var v = r.data[field];
39755                     ed.startEdit(this.view.getCell(row, col), v);
39756                     // combo's with 'displayField and name set
39757                     if (ed.field.displayField && ed.field.name) {
39758                         ed.field.el.dom.value = r.data[ed.field.name];
39759                     }
39760                     
39761                     
39762                 }).defer(50, this);
39763             }
39764         }
39765     },
39766         
39767     /**
39768      * Stops any active editing
39769      */
39770     stopEditing : function(){
39771         if(this.activeEditor){
39772             this.activeEditor.completeEdit();
39773         }
39774         this.activeEditor = null;
39775     },
39776         
39777          /**
39778      * Called to get grid's drag proxy text, by default returns this.ddText.
39779      * @return {String}
39780      */
39781     getDragDropText : function(){
39782         var count = this.selModel.getSelectedCell() ? 1 : 0;
39783         return String.format(this.ddText, count, count == 1 ? '' : 's');
39784     }
39785         
39786 });/*
39787  * Based on:
39788  * Ext JS Library 1.1.1
39789  * Copyright(c) 2006-2007, Ext JS, LLC.
39790  *
39791  * Originally Released Under LGPL - original licence link has changed is not relivant.
39792  *
39793  * Fork - LGPL
39794  * <script type="text/javascript">
39795  */
39796
39797 // private - not really -- you end up using it !
39798 // This is a support class used internally by the Grid components
39799
39800 /**
39801  * @class Roo.grid.GridEditor
39802  * @extends Roo.Editor
39803  * Class for creating and editable grid elements.
39804  * @param {Object} config any settings (must include field)
39805  */
39806 Roo.grid.GridEditor = function(field, config){
39807     if (!config && field.field) {
39808         config = field;
39809         field = Roo.factory(config.field, Roo.form);
39810     }
39811     Roo.grid.GridEditor.superclass.constructor.call(this, field, config);
39812     field.monitorTab = false;
39813 };
39814
39815 Roo.extend(Roo.grid.GridEditor, Roo.Editor, {
39816     
39817     /**
39818      * @cfg {Roo.form.Field} field Field to wrap (or xtyped)
39819      */
39820     
39821     alignment: "tl-tl",
39822     autoSize: "width",
39823     hideEl : false,
39824     cls: "x-small-editor x-grid-editor",
39825     shim:false,
39826     shadow:"frame"
39827 });/*
39828  * Based on:
39829  * Ext JS Library 1.1.1
39830  * Copyright(c) 2006-2007, Ext JS, LLC.
39831  *
39832  * Originally Released Under LGPL - original licence link has changed is not relivant.
39833  *
39834  * Fork - LGPL
39835  * <script type="text/javascript">
39836  */
39837   
39838
39839   
39840 Roo.grid.PropertyRecord = Roo.data.Record.create([
39841     {name:'name',type:'string'},  'value'
39842 ]);
39843
39844
39845 Roo.grid.PropertyStore = function(grid, source){
39846     this.grid = grid;
39847     this.store = new Roo.data.Store({
39848         recordType : Roo.grid.PropertyRecord
39849     });
39850     this.store.on('update', this.onUpdate,  this);
39851     if(source){
39852         this.setSource(source);
39853     }
39854     Roo.grid.PropertyStore.superclass.constructor.call(this);
39855 };
39856
39857
39858
39859 Roo.extend(Roo.grid.PropertyStore, Roo.util.Observable, {
39860     setSource : function(o){
39861         this.source = o;
39862         this.store.removeAll();
39863         var data = [];
39864         for(var k in o){
39865             if(this.isEditableValue(o[k])){
39866                 data.push(new Roo.grid.PropertyRecord({name: k, value: o[k]}, k));
39867             }
39868         }
39869         this.store.loadRecords({records: data}, {}, true);
39870     },
39871
39872     onUpdate : function(ds, record, type){
39873         if(type == Roo.data.Record.EDIT){
39874             var v = record.data['value'];
39875             var oldValue = record.modified['value'];
39876             if(this.grid.fireEvent('beforepropertychange', this.source, record.id, v, oldValue) !== false){
39877                 this.source[record.id] = v;
39878                 record.commit();
39879                 this.grid.fireEvent('propertychange', this.source, record.id, v, oldValue);
39880             }else{
39881                 record.reject();
39882             }
39883         }
39884     },
39885
39886     getProperty : function(row){
39887        return this.store.getAt(row);
39888     },
39889
39890     isEditableValue: function(val){
39891         if(val && val instanceof Date){
39892             return true;
39893         }else if(typeof val == 'object' || typeof val == 'function'){
39894             return false;
39895         }
39896         return true;
39897     },
39898
39899     setValue : function(prop, value){
39900         this.source[prop] = value;
39901         this.store.getById(prop).set('value', value);
39902     },
39903
39904     getSource : function(){
39905         return this.source;
39906     }
39907 });
39908
39909 Roo.grid.PropertyColumnModel = function(grid, store){
39910     this.grid = grid;
39911     var g = Roo.grid;
39912     g.PropertyColumnModel.superclass.constructor.call(this, [
39913         {header: this.nameText, sortable: true, dataIndex:'name', id: 'name'},
39914         {header: this.valueText, resizable:false, dataIndex: 'value', id: 'value'}
39915     ]);
39916     this.store = store;
39917     this.bselect = Roo.DomHelper.append(document.body, {
39918         tag: 'select', style:'display:none', cls: 'x-grid-editor', children: [
39919             {tag: 'option', value: 'true', html: 'true'},
39920             {tag: 'option', value: 'false', html: 'false'}
39921         ]
39922     });
39923     Roo.id(this.bselect);
39924     var f = Roo.form;
39925     this.editors = {
39926         'date' : new g.GridEditor(new f.DateField({selectOnFocus:true})),
39927         'string' : new g.GridEditor(new f.TextField({selectOnFocus:true})),
39928         'number' : new g.GridEditor(new f.NumberField({selectOnFocus:true, style:'text-align:left;'})),
39929         'int' : new g.GridEditor(new f.NumberField({selectOnFocus:true, allowDecimals:false, style:'text-align:left;'})),
39930         'boolean' : new g.GridEditor(new f.Field({el:this.bselect,selectOnFocus:true}))
39931     };
39932     this.renderCellDelegate = this.renderCell.createDelegate(this);
39933     this.renderPropDelegate = this.renderProp.createDelegate(this);
39934 };
39935
39936 Roo.extend(Roo.grid.PropertyColumnModel, Roo.grid.ColumnModel, {
39937     
39938     
39939     nameText : 'Name',
39940     valueText : 'Value',
39941     
39942     dateFormat : 'm/j/Y',
39943     
39944     
39945     renderDate : function(dateVal){
39946         return dateVal.dateFormat(this.dateFormat);
39947     },
39948
39949     renderBool : function(bVal){
39950         return bVal ? 'true' : 'false';
39951     },
39952
39953     isCellEditable : function(colIndex, rowIndex){
39954         return colIndex == 1;
39955     },
39956
39957     getRenderer : function(col){
39958         return col == 1 ?
39959             this.renderCellDelegate : this.renderPropDelegate;
39960     },
39961
39962     renderProp : function(v){
39963         return this.getPropertyName(v);
39964     },
39965
39966     renderCell : function(val){
39967         var rv = val;
39968         if(val instanceof Date){
39969             rv = this.renderDate(val);
39970         }else if(typeof val == 'boolean'){
39971             rv = this.renderBool(val);
39972         }
39973         return Roo.util.Format.htmlEncode(rv);
39974     },
39975
39976     getPropertyName : function(name){
39977         var pn = this.grid.propertyNames;
39978         return pn && pn[name] ? pn[name] : name;
39979     },
39980
39981     getCellEditor : function(colIndex, rowIndex){
39982         var p = this.store.getProperty(rowIndex);
39983         var n = p.data['name'], val = p.data['value'];
39984         
39985         if(typeof(this.grid.customEditors[n]) == 'string'){
39986             return this.editors[this.grid.customEditors[n]];
39987         }
39988         if(typeof(this.grid.customEditors[n]) != 'undefined'){
39989             return this.grid.customEditors[n];
39990         }
39991         if(val instanceof Date){
39992             return this.editors['date'];
39993         }else if(typeof val == 'number'){
39994             return this.editors['number'];
39995         }else if(typeof val == 'boolean'){
39996             return this.editors['boolean'];
39997         }else{
39998             return this.editors['string'];
39999         }
40000     }
40001 });
40002
40003 /**
40004  * @class Roo.grid.PropertyGrid
40005  * @extends Roo.grid.EditorGrid
40006  * This class represents the  interface of a component based property grid control.
40007  * <br><br>Usage:<pre><code>
40008  var grid = new Roo.grid.PropertyGrid("my-container-id", {
40009       
40010  });
40011  // set any options
40012  grid.render();
40013  * </code></pre>
40014   
40015  * @constructor
40016  * @param {String/HTMLElement/Roo.Element} container The element into which this grid will be rendered -
40017  * The container MUST have some type of size defined for the grid to fill. The container will be
40018  * automatically set to position relative if it isn't already.
40019  * @param {Object} config A config object that sets properties on this grid.
40020  */
40021 Roo.grid.PropertyGrid = function(container, config){
40022     config = config || {};
40023     var store = new Roo.grid.PropertyStore(this);
40024     this.store = store;
40025     var cm = new Roo.grid.PropertyColumnModel(this, store);
40026     store.store.sort('name', 'ASC');
40027     Roo.grid.PropertyGrid.superclass.constructor.call(this, container, Roo.apply({
40028         ds: store.store,
40029         cm: cm,
40030         enableColLock:false,
40031         enableColumnMove:false,
40032         stripeRows:false,
40033         trackMouseOver: false,
40034         clicksToEdit:1
40035     }, config));
40036     this.getGridEl().addClass('x-props-grid');
40037     this.lastEditRow = null;
40038     this.on('columnresize', this.onColumnResize, this);
40039     this.addEvents({
40040          /**
40041              * @event beforepropertychange
40042              * Fires before a property changes (return false to stop?)
40043              * @param {Roo.grid.PropertyGrid} grid property grid? (check could be store)
40044              * @param {String} id Record Id
40045              * @param {String} newval New Value
40046          * @param {String} oldval Old Value
40047              */
40048         "beforepropertychange": true,
40049         /**
40050              * @event propertychange
40051              * Fires after a property changes
40052              * @param {Roo.grid.PropertyGrid} grid property grid? (check could be store)
40053              * @param {String} id Record Id
40054              * @param {String} newval New Value
40055          * @param {String} oldval Old Value
40056              */
40057         "propertychange": true
40058     });
40059     this.customEditors = this.customEditors || {};
40060 };
40061 Roo.extend(Roo.grid.PropertyGrid, Roo.grid.EditorGrid, {
40062     
40063      /**
40064      * @cfg {Object} customEditors map of colnames=> custom editors.
40065      * the custom editor can be one of the standard ones (date|string|number|int|boolean), or a
40066      * grid editor eg. Roo.grid.GridEditor(new Roo.form.TextArea({selectOnFocus:true})),
40067      * false disables editing of the field.
40068          */
40069     
40070       /**
40071      * @cfg {Object} propertyNames map of property Names to their displayed value
40072          */
40073     
40074     render : function(){
40075         Roo.grid.PropertyGrid.superclass.render.call(this);
40076         this.autoSize.defer(100, this);
40077     },
40078
40079     autoSize : function(){
40080         Roo.grid.PropertyGrid.superclass.autoSize.call(this);
40081         if(this.view){
40082             this.view.fitColumns();
40083         }
40084     },
40085
40086     onColumnResize : function(){
40087         this.colModel.setColumnWidth(1, this.container.getWidth(true)-this.colModel.getColumnWidth(0));
40088         this.autoSize();
40089     },
40090     /**
40091      * Sets the data for the Grid
40092      * accepts a Key => Value object of all the elements avaiable.
40093      * @param {Object} data  to appear in grid.
40094      */
40095     setSource : function(source){
40096         this.store.setSource(source);
40097         //this.autoSize();
40098     },
40099     /**
40100      * Gets all the data from the grid.
40101      * @return {Object} data  data stored in grid
40102      */
40103     getSource : function(){
40104         return this.store.getSource();
40105     }
40106 });/*
40107  * Based on:
40108  * Ext JS Library 1.1.1
40109  * Copyright(c) 2006-2007, Ext JS, LLC.
40110  *
40111  * Originally Released Under LGPL - original licence link has changed is not relivant.
40112  *
40113  * Fork - LGPL
40114  * <script type="text/javascript">
40115  */
40116  
40117 /**
40118  * @class Roo.LoadMask
40119  * A simple utility class for generically masking elements while loading data.  If the element being masked has
40120  * an underlying {@link Roo.data.Store}, the masking will be automatically synchronized with the store's loading
40121  * process and the mask element will be cached for reuse.  For all other elements, this mask will replace the
40122  * element's UpdateManager load indicator and will be destroyed after the initial load.
40123  * @constructor
40124  * Create a new LoadMask
40125  * @param {String/HTMLElement/Roo.Element} el The element or DOM node, or its id
40126  * @param {Object} config The config object
40127  */
40128 Roo.LoadMask = function(el, config){
40129     this.el = Roo.get(el);
40130     Roo.apply(this, config);
40131     if(this.store){
40132         this.store.on('beforeload', this.onBeforeLoad, this);
40133         this.store.on('load', this.onLoad, this);
40134         this.store.on('loadexception', this.onLoadException, this);
40135         this.removeMask = false;
40136     }else{
40137         var um = this.el.getUpdateManager();
40138         um.showLoadIndicator = false; // disable the default indicator
40139         um.on('beforeupdate', this.onBeforeLoad, this);
40140         um.on('update', this.onLoad, this);
40141         um.on('failure', this.onLoad, this);
40142         this.removeMask = true;
40143     }
40144 };
40145
40146 Roo.LoadMask.prototype = {
40147     /**
40148      * @cfg {Boolean} removeMask
40149      * True to create a single-use mask that is automatically destroyed after loading (useful for page loads),
40150      * False to persist the mask element reference for multiple uses (e.g., for paged data widgets).  Defaults to false.
40151      */
40152     /**
40153      * @cfg {String} msg
40154      * The text to display in a centered loading message box (defaults to 'Loading...')
40155      */
40156     msg : 'Loading...',
40157     /**
40158      * @cfg {String} msgCls
40159      * The CSS class to apply to the loading message element (defaults to "x-mask-loading")
40160      */
40161     msgCls : 'x-mask-loading',
40162
40163     /**
40164      * Read-only. True if the mask is currently disabled so that it will not be displayed (defaults to false)
40165      * @type Boolean
40166      */
40167     disabled: false,
40168
40169     /**
40170      * Disables the mask to prevent it from being displayed
40171      */
40172     disable : function(){
40173        this.disabled = true;
40174     },
40175
40176     /**
40177      * Enables the mask so that it can be displayed
40178      */
40179     enable : function(){
40180         this.disabled = false;
40181     },
40182     
40183     onLoadException : function()
40184     {
40185         Roo.log(arguments);
40186         
40187         if (typeof(arguments[3]) != 'undefined') {
40188             Roo.MessageBox.alert("Error loading",arguments[3]);
40189         } 
40190         /*
40191         try {
40192             if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
40193                 Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
40194             }   
40195         } catch(e) {
40196             
40197         }
40198         */
40199     
40200         
40201         
40202         this.el.unmask(this.removeMask);
40203     },
40204     // private
40205     onLoad : function()
40206     {
40207         this.el.unmask(this.removeMask);
40208     },
40209
40210     // private
40211     onBeforeLoad : function(){
40212         if(!this.disabled){
40213             this.el.mask(this.msg, this.msgCls);
40214         }
40215     },
40216
40217     // private
40218     destroy : function(){
40219         if(this.store){
40220             this.store.un('beforeload', this.onBeforeLoad, this);
40221             this.store.un('load', this.onLoad, this);
40222             this.store.un('loadexception', this.onLoadException, this);
40223         }else{
40224             var um = this.el.getUpdateManager();
40225             um.un('beforeupdate', this.onBeforeLoad, this);
40226             um.un('update', this.onLoad, this);
40227             um.un('failure', this.onLoad, this);
40228         }
40229     }
40230 };/*
40231  * Based on:
40232  * Ext JS Library 1.1.1
40233  * Copyright(c) 2006-2007, Ext JS, LLC.
40234  *
40235  * Originally Released Under LGPL - original licence link has changed is not relivant.
40236  *
40237  * Fork - LGPL
40238  * <script type="text/javascript">
40239  */
40240
40241
40242 /**
40243  * @class Roo.XTemplate
40244  * @extends Roo.Template
40245  * Provides a template that can have nested templates for loops or conditionals. The syntax is:
40246 <pre><code>
40247 var t = new Roo.XTemplate(
40248         '&lt;select name="{name}"&gt;',
40249                 '&lt;tpl for="options"&gt;&lt;option value="{value:trim}"&gt;{text:ellipsis(10)}&lt;/option&gt;&lt;/tpl&gt;',
40250         '&lt;/select&gt;'
40251 );
40252  
40253 // then append, applying the master template values
40254  </code></pre>
40255  *
40256  * Supported features:
40257  *
40258  *  Tags:
40259
40260 <pre><code>
40261       {a_variable} - output encoded.
40262       {a_variable.format:("Y-m-d")} - call a method on the variable
40263       {a_variable:raw} - unencoded output
40264       {a_variable:toFixed(1,2)} - Roo.util.Format."toFixed"
40265       {a_variable:this.method_on_template(...)} - call a method on the template object.
40266  
40267 </code></pre>
40268  *  The tpl tag:
40269 <pre><code>
40270         &lt;tpl for="a_variable or condition.."&gt;&lt;/tpl&gt;
40271         &lt;tpl if="a_variable or condition"&gt;&lt;/tpl&gt;
40272         &lt;tpl exec="some javascript"&gt;&lt;/tpl&gt;
40273         &lt;tpl name="named_template"&gt;&lt;/tpl&gt; (experimental)
40274   
40275         &lt;tpl for="."&gt;&lt;/tpl&gt; - just iterate the property..
40276         &lt;tpl for=".."&gt;&lt;/tpl&gt; - iterates with the parent (probably the template) 
40277 </code></pre>
40278  *      
40279  */
40280 Roo.XTemplate = function()
40281 {
40282     Roo.XTemplate.superclass.constructor.apply(this, arguments);
40283     if (this.html) {
40284         this.compile();
40285     }
40286 };
40287
40288
40289 Roo.extend(Roo.XTemplate, Roo.Template, {
40290
40291     /**
40292      * The various sub templates
40293      */
40294     tpls : false,
40295     /**
40296      *
40297      * basic tag replacing syntax
40298      * WORD:WORD()
40299      *
40300      * // you can fake an object call by doing this
40301      *  x.t:(test,tesT) 
40302      * 
40303      */
40304     re : /\{([\w-\.]+)(?:\:([\w\.]*)(?:\((.*?)?\))?)?\}/g,
40305
40306     /**
40307      * compile the template
40308      *
40309      * This is not recursive, so I'm not sure how nested templates are really going to be handled..
40310      *
40311      */
40312     compile: function()
40313     {
40314         var s = this.html;
40315      
40316         s = ['<tpl>', s, '</tpl>'].join('');
40317     
40318         var re     = /<tpl\b[^>]*>((?:(?=([^<]+))\2|<(?!tpl\b[^>]*>))*?)<\/tpl>/,
40319             nameRe = /^<tpl\b[^>]*?for="(.*?)"/,
40320             ifRe   = /^<tpl\b[^>]*?if="(.*?)"/,
40321             execRe = /^<tpl\b[^>]*?exec="(.*?)"/,
40322             namedRe = /^<tpl\b[^>]*?name="(\w+)"/,  // named templates..
40323             m,
40324             id     = 0,
40325             tpls   = [];
40326     
40327         while(true == !!(m = s.match(re))){
40328             var forMatch   = m[0].match(nameRe),
40329                 ifMatch   = m[0].match(ifRe),
40330                 execMatch   = m[0].match(execRe),
40331                 namedMatch   = m[0].match(namedRe),
40332                 
40333                 exp  = null, 
40334                 fn   = null,
40335                 exec = null,
40336                 name = forMatch && forMatch[1] ? forMatch[1] : '';
40337                 
40338             if (ifMatch) {
40339                 // if - puts fn into test..
40340                 exp = ifMatch && ifMatch[1] ? ifMatch[1] : null;
40341                 if(exp){
40342                    fn = new Function('values', 'parent', 'with(values){ return '+(Roo.util.Format.htmlDecode(exp))+'; }');
40343                 }
40344             }
40345             
40346             if (execMatch) {
40347                 // exec - calls a function... returns empty if true is  returned.
40348                 exp = execMatch && execMatch[1] ? execMatch[1] : null;
40349                 if(exp){
40350                    exec = new Function('values', 'parent', 'with(values){ '+(Roo.util.Format.htmlDecode(exp))+'; }');
40351                 }
40352             }
40353             
40354             
40355             if (name) {
40356                 // for = 
40357                 switch(name){
40358                     case '.':  name = new Function('values', 'parent', 'with(values){ return values; }'); break;
40359                     case '..': name = new Function('values', 'parent', 'with(values){ return parent; }'); break;
40360                     default:   name = new Function('values', 'parent', 'with(values){ return '+name+'; }');
40361                 }
40362             }
40363             var uid = namedMatch ? namedMatch[1] : id;
40364             
40365             
40366             tpls.push({
40367                 id:     namedMatch ? namedMatch[1] : id,
40368                 target: name,
40369                 exec:   exec,
40370                 test:   fn,
40371                 body:   m[1] || ''
40372             });
40373             if (namedMatch) {
40374                 s = s.replace(m[0], '');
40375             } else { 
40376                 s = s.replace(m[0], '{xtpl'+ id + '}');
40377             }
40378             ++id;
40379         }
40380         this.tpls = [];
40381         for(var i = tpls.length-1; i >= 0; --i){
40382             this.compileTpl(tpls[i]);
40383             this.tpls[tpls[i].id] = tpls[i];
40384         }
40385         this.master = tpls[tpls.length-1];
40386         return this;
40387     },
40388     /**
40389      * same as applyTemplate, except it's done to one of the subTemplates
40390      * when using named templates, you can do:
40391      *
40392      * var str = pl.applySubTemplate('your-name', values);
40393      *
40394      * 
40395      * @param {Number} id of the template
40396      * @param {Object} values to apply to template
40397      * @param {Object} parent (normaly the instance of this object)
40398      */
40399     applySubTemplate : function(id, values, parent)
40400     {
40401         
40402         
40403         var t = this.tpls[id];
40404         
40405         
40406         try { 
40407             if(t.test && !t.test.call(this, values, parent)){
40408                 return '';
40409             }
40410         } catch(e) {
40411             Roo.log("Xtemplate.applySubTemplate 'test': Exception thrown");
40412             Roo.log(e.toString());
40413             Roo.log(t.test);
40414             return ''
40415         }
40416         try { 
40417             
40418             if(t.exec && t.exec.call(this, values, parent)){
40419                 return '';
40420             }
40421         } catch(e) {
40422             Roo.log("Xtemplate.applySubTemplate 'exec': Exception thrown");
40423             Roo.log(e.toString());
40424             Roo.log(t.exec);
40425             return ''
40426         }
40427         try {
40428             var vs = t.target ? t.target.call(this, values, parent) : values;
40429             parent = t.target ? values : parent;
40430             if(t.target && vs instanceof Array){
40431                 var buf = [];
40432                 for(var i = 0, len = vs.length; i < len; i++){
40433                     buf[buf.length] = t.compiled.call(this, vs[i], parent);
40434                 }
40435                 return buf.join('');
40436             }
40437             return t.compiled.call(this, vs, parent);
40438         } catch (e) {
40439             Roo.log("Xtemplate.applySubTemplate : Exception thrown");
40440             Roo.log(e.toString());
40441             Roo.log(t.compiled);
40442             return '';
40443         }
40444     },
40445
40446     compileTpl : function(tpl)
40447     {
40448         var fm = Roo.util.Format;
40449         var useF = this.disableFormats !== true;
40450         var sep = Roo.isGecko ? "+" : ",";
40451         var undef = function(str) {
40452             Roo.log("Property not found :"  + str);
40453             return '';
40454         };
40455         
40456         var fn = function(m, name, format, args)
40457         {
40458             //Roo.log(arguments);
40459             args = args ? args.replace(/\\'/g,"'") : args;
40460             //["{TEST:(a,b,c)}", "TEST", "", "a,b,c", 0, "{TEST:(a,b,c)}"]
40461             if (typeof(format) == 'undefined') {
40462                 format= 'htmlEncode';
40463             }
40464             if (format == 'raw' ) {
40465                 format = false;
40466             }
40467             
40468             if(name.substr(0, 4) == 'xtpl'){
40469                 return "'"+ sep +'this.applySubTemplate('+name.substr(4)+', values, parent)'+sep+"'";
40470             }
40471             
40472             // build an array of options to determine if value is undefined..
40473             
40474             // basically get 'xxxx.yyyy' then do
40475             // (typeof(xxxx) == 'undefined' || typeof(xxx.yyyy) == 'undefined') ?
40476             //    (function () { Roo.log("Property not found"); return ''; })() :
40477             //    ......
40478             
40479             var udef_ar = [];
40480             var lookfor = '';
40481             Roo.each(name.split('.'), function(st) {
40482                 lookfor += (lookfor.length ? '.': '') + st;
40483                 udef_ar.push(  "(typeof(" + lookfor + ") == 'undefined')"  );
40484             });
40485             
40486             var udef_st = '((' + udef_ar.join(" || ") +") ? undef('" + name + "') : "; // .. needs )
40487             
40488             
40489             if(format && useF){
40490                 
40491                 args = args ? ',' + args : "";
40492                  
40493                 if(format.substr(0, 5) != "this."){
40494                     format = "fm." + format + '(';
40495                 }else{
40496                     format = 'this.call("'+ format.substr(5) + '", ';
40497                     args = ", values";
40498                 }
40499                 
40500                 return "'"+ sep +   udef_st   +    format + name + args + "))"+sep+"'";
40501             }
40502              
40503             if (args.length) {
40504                 // called with xxyx.yuu:(test,test)
40505                 // change to ()
40506                 return "'"+ sep + udef_st  + name + '(' +  args + "))"+sep+"'";
40507             }
40508             // raw.. - :raw modifier..
40509             return "'"+ sep + udef_st  + name + ")"+sep+"'";
40510             
40511         };
40512         var body;
40513         // branched to use + in gecko and [].join() in others
40514         if(Roo.isGecko){
40515             body = "tpl.compiled = function(values, parent){  with(values) { return '" +
40516                    tpl.body.replace(/(\r\n|\n)/g, '\\n').replace(/'/g, "\\'").replace(this.re, fn) +
40517                     "';};};";
40518         }else{
40519             body = ["tpl.compiled = function(values, parent){  with (values) { return ['"];
40520             body.push(tpl.body.replace(/(\r\n|\n)/g,
40521                             '\\n').replace(/'/g, "\\'").replace(this.re, fn));
40522             body.push("'].join('');};};");
40523             body = body.join('');
40524         }
40525         
40526         Roo.debug && Roo.log(body.replace(/\\n/,'\n'));
40527        
40528         /** eval:var:tpl eval:var:fm eval:var:useF eval:var:undef  */
40529         eval(body);
40530         
40531         return this;
40532     },
40533
40534     applyTemplate : function(values){
40535         return this.master.compiled.call(this, values, {});
40536         //var s = this.subs;
40537     },
40538
40539     apply : function(){
40540         return this.applyTemplate.apply(this, arguments);
40541     }
40542
40543  });
40544
40545 Roo.XTemplate.from = function(el){
40546     el = Roo.getDom(el);
40547     return new Roo.XTemplate(el.value || el.innerHTML);
40548 };/*
40549  * Original code for Roojs - LGPL
40550  * <script type="text/javascript">
40551  */
40552  
40553 /**
40554  * @class Roo.XComponent
40555  * A delayed Element creator...
40556  * Or a way to group chunks of interface together.
40557  * 
40558  * Mypart.xyx = new Roo.XComponent({
40559
40560     parent : 'Mypart.xyz', // empty == document.element.!!
40561     order : '001',
40562     name : 'xxxx'
40563     region : 'xxxx'
40564     disabled : function() {} 
40565      
40566     tree : function() { // return an tree of xtype declared components
40567         var MODULE = this;
40568         return 
40569         {
40570             xtype : 'NestedLayoutPanel',
40571             // technicall
40572         }
40573      ]
40574  *})
40575  *
40576  *
40577  * It can be used to build a big heiracy, with parent etc.
40578  * or you can just use this to render a single compoent to a dom element
40579  * MYPART.render(Roo.Element | String(id) | dom_element )
40580  * 
40581  * @extends Roo.util.Observable
40582  * @constructor
40583  * @param cfg {Object} configuration of component
40584  * 
40585  */
40586 Roo.XComponent = function(cfg) {
40587     Roo.apply(this, cfg);
40588     this.addEvents({ 
40589         /**
40590              * @event built
40591              * Fires when this the componnt is built
40592              * @param {Roo.XComponent} c the component
40593              */
40594         'built' : true
40595         
40596     });
40597     this.region = this.region || 'center'; // default..
40598     Roo.XComponent.register(this);
40599     this.modules = false;
40600     this.el = false; // where the layout goes..
40601     
40602     
40603 }
40604 Roo.extend(Roo.XComponent, Roo.util.Observable, {
40605     /**
40606      * @property el
40607      * The created element (with Roo.factory())
40608      * @type {Roo.Layout}
40609      */
40610     el  : false,
40611     
40612     /**
40613      * @property el
40614      * for BC  - use el in new code
40615      * @type {Roo.Layout}
40616      */
40617     panel : false,
40618     
40619     /**
40620      * @property layout
40621      * for BC  - use el in new code
40622      * @type {Roo.Layout}
40623      */
40624     layout : false,
40625     
40626      /**
40627      * @cfg {Function|boolean} disabled
40628      * If this module is disabled by some rule, return true from the funtion
40629      */
40630     disabled : false,
40631     
40632     /**
40633      * @cfg {String} parent 
40634      * Name of parent element which it get xtype added to..
40635      */
40636     parent: false,
40637     
40638     /**
40639      * @cfg {String} order
40640      * Used to set the order in which elements are created (usefull for multiple tabs)
40641      */
40642     
40643     order : false,
40644     /**
40645      * @cfg {String} name
40646      * String to display while loading.
40647      */
40648     name : false,
40649     /**
40650      * @cfg {String} region
40651      * Region to render component to (defaults to center)
40652      */
40653     region : 'center',
40654     
40655     /**
40656      * @cfg {Array} items
40657      * A single item array - the first element is the root of the tree..
40658      * It's done this way to stay compatible with the Xtype system...
40659      */
40660     items : false,
40661     
40662     /**
40663      * @property _tree
40664      * The method that retuns the tree of parts that make up this compoennt 
40665      * @type {function}
40666      */
40667     _tree  : false,
40668     
40669      /**
40670      * render
40671      * render element to dom or tree
40672      * @param {Roo.Element|String|DomElement} optional render to if parent is not set.
40673      */
40674     
40675     render : function(el)
40676     {
40677         
40678         el = el || false;
40679         var hp = this.parent ? 1 : 0;
40680         
40681         if (!el && typeof(this.parent) == 'string' && this.parent.substring(0,1) == '#') {
40682             // if parent is a '#.....' string, then let's use that..
40683             var ename = this.parent.substr(1)
40684             this.parent = false;
40685             el = Roo.get(ename);
40686             if (!el) {
40687                 Roo.log("Warning - element can not be found :#" + ename );
40688                 return;
40689             }
40690         }
40691         
40692         
40693         if (!this.parent) {
40694             
40695             el = el ? Roo.get(el) : false;      
40696             
40697             // it's a top level one..
40698             this.parent =  {
40699                 el : new Roo.BorderLayout(el || document.body, {
40700                 
40701                      center: {
40702                          titlebar: false,
40703                          autoScroll:false,
40704                          closeOnTab: true,
40705                          tabPosition: 'top',
40706                           //resizeTabs: true,
40707                          alwaysShowTabs: el && hp? false :  true,
40708                          hideTabs: el || !hp ? true :  false,
40709                          minTabWidth: 140
40710                      }
40711                  })
40712             }
40713         }
40714         
40715                 if (!this.parent.el) {
40716                         // probably an old style ctor, which has been disabled.
40717                         return;
40718                         
40719                 }
40720                 // The 'tree' method is  '_tree now' 
40721             
40722         var tree = this._tree ? this._tree() : this.tree();
40723         tree.region = tree.region || this.region;
40724         this.el = this.parent.el.addxtype(tree);
40725         this.fireEvent('built', this);
40726         
40727         this.panel = this.el;
40728         this.layout = this.panel.layout;
40729                 this.parentLayout = this.parent.layout  || false;  
40730          
40731     }
40732     
40733 });
40734
40735 Roo.apply(Roo.XComponent, {
40736     /**
40737      * @property  hideProgress
40738      * true to disable the building progress bar.. usefull on single page renders.
40739      * @type Boolean
40740      */
40741     hideProgress : false,
40742     /**
40743      * @property  buildCompleted
40744      * True when the builder has completed building the interface.
40745      * @type Boolean
40746      */
40747     buildCompleted : false,
40748      
40749     /**
40750      * @property  topModule
40751      * the upper most module - uses document.element as it's constructor.
40752      * @type Object
40753      */
40754      
40755     topModule  : false,
40756       
40757     /**
40758      * @property  modules
40759      * array of modules to be created by registration system.
40760      * @type {Array} of Roo.XComponent
40761      */
40762     
40763     modules : [],
40764     /**
40765      * @property  elmodules
40766      * array of modules to be created by which use #ID 
40767      * @type {Array} of Roo.XComponent
40768      */
40769      
40770     elmodules : [],
40771
40772     
40773     /**
40774      * Register components to be built later.
40775      *
40776      * This solves the following issues
40777      * - Building is not done on page load, but after an authentication process has occured.
40778      * - Interface elements are registered on page load
40779      * - Parent Interface elements may not be loaded before child, so this handles that..
40780      * 
40781      *
40782      * example:
40783      * 
40784      * MyApp.register({
40785           order : '000001',
40786           module : 'Pman.Tab.projectMgr',
40787           region : 'center',
40788           parent : 'Pman.layout',
40789           disabled : false,  // or use a function..
40790         })
40791      
40792      * * @param {Object} details about module
40793      */
40794     register : function(obj) {
40795                 
40796         Roo.XComponent.event.fireEvent('register', obj);
40797         switch(typeof(obj.disabled) ) {
40798                 
40799             case 'undefined':
40800                 break;
40801             
40802             case 'function':
40803                 if ( obj.disabled() ) {
40804                         return;
40805                 }
40806                 break;
40807             
40808             default:
40809                 if (obj.disabled) {
40810                         return;
40811                 }
40812                 break;
40813         }
40814                 
40815         this.modules.push(obj);
40816          
40817     },
40818     /**
40819      * convert a string to an object..
40820      * eg. 'AAA.BBB' -> finds AAA.BBB
40821
40822      */
40823     
40824     toObject : function(str)
40825     {
40826         if (!str || typeof(str) == 'object') {
40827             return str;
40828         }
40829         if (str.substring(0,1) == '#') {
40830             return str;
40831         }
40832
40833         var ar = str.split('.');
40834         var rt, o;
40835         rt = ar.shift();
40836             /** eval:var:o */
40837         try {
40838             eval('if (typeof ' + rt + ' == "undefined"){ o = false;} o = ' + rt + ';');
40839         } catch (e) {
40840             throw "Module not found : " + str;
40841         }
40842         
40843         if (o === false) {
40844             throw "Module not found : " + str;
40845         }
40846         Roo.each(ar, function(e) {
40847             if (typeof(o[e]) == 'undefined') {
40848                 throw "Module not found : " + str;
40849             }
40850             o = o[e];
40851         });
40852         
40853         return o;
40854         
40855     },
40856     
40857     
40858     /**
40859      * move modules into their correct place in the tree..
40860      * 
40861      */
40862     preBuild : function ()
40863     {
40864         var _t = this;
40865         Roo.each(this.modules , function (obj)
40866         {
40867             Roo.XComponent.event.fireEvent('beforebuild', obj);
40868             
40869             var opar = obj.parent;
40870             try { 
40871                 obj.parent = this.toObject(opar);
40872             } catch(e) {
40873                 Roo.log("parent:toObject failed: " + e.toString());
40874                 return;
40875             }
40876             
40877             if (!obj.parent) {
40878                 Roo.debug && Roo.log("GOT top level module");
40879                 Roo.debug && Roo.log(obj);
40880                 obj.modules = new Roo.util.MixedCollection(false, 
40881                     function(o) { return o.order + '' }
40882                 );
40883                 this.topModule = obj;
40884                 return;
40885             }
40886                         // parent is a string (usually a dom element name..)
40887             if (typeof(obj.parent) == 'string') {
40888                 this.elmodules.push(obj);
40889                 return;
40890             }
40891             if (obj.parent.constructor != Roo.XComponent) {
40892                 Roo.log("Warning : Object Parent is not instance of XComponent:" + obj.name)
40893             }
40894             if (!obj.parent.modules) {
40895                 obj.parent.modules = new Roo.util.MixedCollection(false, 
40896                     function(o) { return o.order + '' }
40897                 );
40898             }
40899             if (obj.parent.disabled) {
40900                 obj.disabled = true;
40901             }
40902             obj.parent.modules.add(obj);
40903         }, this);
40904     },
40905     
40906      /**
40907      * make a list of modules to build.
40908      * @return {Array} list of modules. 
40909      */ 
40910     
40911     buildOrder : function()
40912     {
40913         var _this = this;
40914         var cmp = function(a,b) {   
40915             return String(a).toUpperCase() > String(b).toUpperCase() ? 1 : -1;
40916         };
40917         if ((!this.topModule || !this.topModule.modules) && !this.elmodules.length) {
40918             throw "No top level modules to build";
40919         }
40920         
40921         // make a flat list in order of modules to build.
40922         var mods = this.topModule ? [ this.topModule ] : [];
40923                 
40924         
40925         // elmodules (is a list of DOM based modules )
40926         Roo.each(this.elmodules, function(e) {
40927             mods.push(e);
40928             if (!this.topModule &&
40929                 typeof(e.parent) == 'string' &&
40930                 e.parent.substring(0,1) == '#' &&
40931                 Roo.get(e.parent.substr(1))
40932                ) {
40933                 
40934                 _this.topModule = e;
40935             }
40936             
40937         });
40938
40939         
40940         // add modules to their parents..
40941         var addMod = function(m) {
40942             Roo.debug && Roo.log("build Order: add: " + m.name);
40943                 
40944             mods.push(m);
40945             if (m.modules && !m.disabled) {
40946                 Roo.debug && Roo.log("build Order: " + m.modules.length + " child modules");
40947                 m.modules.keySort('ASC',  cmp );
40948                 Roo.debug && Roo.log("build Order: " + m.modules.length + " child modules (after sort)");
40949     
40950                 m.modules.each(addMod);
40951             } else {
40952                 Roo.debug && Roo.log("build Order: no child modules");
40953             }
40954             // not sure if this is used any more..
40955             if (m.finalize) {
40956                 m.finalize.name = m.name + " (clean up) ";
40957                 mods.push(m.finalize);
40958             }
40959             
40960         }
40961         if (this.topModule && this.topModule.modules) { 
40962             this.topModule.modules.keySort('ASC',  cmp );
40963             this.topModule.modules.each(addMod);
40964         } 
40965         return mods;
40966     },
40967     
40968      /**
40969      * Build the registered modules.
40970      * @param {Object} parent element.
40971      * @param {Function} optional method to call after module has been added.
40972      * 
40973      */ 
40974    
40975     build : function() 
40976     {
40977         
40978         this.preBuild();
40979         var mods = this.buildOrder();
40980       
40981         //this.allmods = mods;
40982         //Roo.debug && Roo.log(mods);
40983         //return;
40984         if (!mods.length) { // should not happen
40985             throw "NO modules!!!";
40986         }
40987         
40988         
40989         var msg = "Building Interface...";
40990         // flash it up as modal - so we store the mask!?
40991         if (!this.hideProgress) {
40992             Roo.MessageBox.show({ title: 'loading' });
40993             Roo.MessageBox.show({
40994                title: "Please wait...",
40995                msg: msg,
40996                width:450,
40997                progress:true,
40998                closable:false,
40999                modal: false
41000               
41001             });
41002         }
41003         var total = mods.length;
41004         
41005         var _this = this;
41006         var progressRun = function() {
41007             if (!mods.length) {
41008                 Roo.debug && Roo.log('hide?');
41009                 if (!this.hideProgress) {
41010                     Roo.MessageBox.hide();
41011                 }
41012                 Roo.XComponent.event.fireEvent('buildcomplete', _this.topModule);
41013                 
41014                 // THE END...
41015                 return false;   
41016             }
41017             
41018             var m = mods.shift();
41019             
41020             
41021             Roo.debug && Roo.log(m);
41022             // not sure if this is supported any more.. - modules that are are just function
41023             if (typeof(m) == 'function') { 
41024                 m.call(this);
41025                 return progressRun.defer(10, _this);
41026             } 
41027             
41028             
41029             msg = "Building Interface " + (total  - mods.length) + 
41030                     " of " + total + 
41031                     (m.name ? (' - ' + m.name) : '');
41032                         Roo.debug && Roo.log(msg);
41033             if (!this.hideProgress) { 
41034                 Roo.MessageBox.updateProgress(  (total  - mods.length)/total, msg  );
41035             }
41036             
41037          
41038             // is the module disabled?
41039             var disabled = (typeof(m.disabled) == 'function') ?
41040                 m.disabled.call(m.module.disabled) : m.disabled;    
41041             
41042             
41043             if (disabled) {
41044                 return progressRun(); // we do not update the display!
41045             }
41046             
41047             // now build 
41048             
41049                         
41050                         
41051             m.render();
41052             // it's 10 on top level, and 1 on others??? why...
41053             return progressRun.defer(10, _this);
41054              
41055         }
41056         progressRun.defer(1, _this);
41057      
41058         
41059         
41060     },
41061         
41062         
41063         /**
41064          * Event Object.
41065          *
41066          *
41067          */
41068         event: false, 
41069     /**
41070          * wrapper for event.on - aliased later..  
41071          * Typically use to register a event handler for register:
41072          *
41073          * eg. Roo.XComponent.on('register', function(comp) { comp.disable = true } );
41074          *
41075          */
41076     on : false
41077    
41078     
41079     
41080 });
41081
41082 Roo.XComponent.event = new Roo.util.Observable({
41083                 events : { 
41084                         /**
41085                          * @event register
41086                          * Fires when an Component is registered,
41087                          * set the disable property on the Component to stop registration.
41088                          * @param {Roo.XComponent} c the component being registerd.
41089                          * 
41090                          */
41091                         'register' : true,
41092             /**
41093                          * @event beforebuild
41094                          * Fires before each Component is built
41095                          * can be used to apply permissions.
41096                          * @param {Roo.XComponent} c the component being registerd.
41097                          * 
41098                          */
41099                         'beforebuild' : true,
41100                         /**
41101                          * @event buildcomplete
41102                          * Fires on the top level element when all elements have been built
41103                          * @param {Roo.XComponent} the top level component.
41104                          */
41105                         'buildcomplete' : true
41106                         
41107                 }
41108 });
41109
41110 Roo.XComponent.on = Roo.XComponent.event.on.createDelegate(Roo.XComponent.event); 
41111