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 (!Roo.isTouch && this.primaryButtonOnly && e.button != 0) {
828             Roo.log('not touch/ button !=0');
829             return;
830         }
831
832         if (this.isLocked()) {
833             Roo.log('locked');
834             return;
835         }
836
837         this.DDM.refreshCache(this.groups);
838
839         var pt = new Roo.lib.Point(Roo.lib.Event.getPageX(e), Roo.lib.Event.getPageY(e));
840         if (!this.hasOuterHandles && !this.DDM.isOverTarget(pt, this) )  {
841                 // do nothing.
842         } else {
843             Roo.log('check validator');
844             if (this.clickValidator(e)) {
845                 Roo.log('validate success');
846                 // set the initial element position
847                 this.setStartPosition();
848
849
850                 this.b4MouseDown(e);
851                 this.onMouseDown(e);
852
853                 this.DDM.handleMouseDown(e, this);
854
855                 this.DDM.stopEvent(e);
856             } else {
857
858
859             }
860         }
861     },
862
863     clickValidator: function(e) {
864         var target = e.getTarget();
865         return ( this.isValidHandleChild(target) &&
866                     (this.id == this.handleElId ||
867                         this.DDM.handleWasClicked(target, this.id)) );
868     },
869
870     /**
871      * Allows you to specify a tag name that should not start a drag operation
872      * when clicked.  This is designed to facilitate embedding links within a
873      * drag handle that do something other than start the drag.
874      * @method addInvalidHandleType
875      * @param {string} tagName the type of element to exclude
876      */
877     addInvalidHandleType: function(tagName) {
878         var type = tagName.toUpperCase();
879         this.invalidHandleTypes[type] = type;
880     },
881
882     /**
883      * Lets you to specify an element id for a child of a drag handle
884      * that should not initiate a drag
885      * @method addInvalidHandleId
886      * @param {string} id the element id of the element you wish to ignore
887      */
888     addInvalidHandleId: function(id) {
889         if (typeof id !== "string") {
890             id = Roo.id(id);
891         }
892         this.invalidHandleIds[id] = id;
893     },
894
895     /**
896      * Lets you specify a css class of elements that will not initiate a drag
897      * @method addInvalidHandleClass
898      * @param {string} cssClass the class of the elements you wish to ignore
899      */
900     addInvalidHandleClass: function(cssClass) {
901         this.invalidHandleClasses.push(cssClass);
902     },
903
904     /**
905      * Unsets an excluded tag name set by addInvalidHandleType
906      * @method removeInvalidHandleType
907      * @param {string} tagName the type of element to unexclude
908      */
909     removeInvalidHandleType: function(tagName) {
910         var type = tagName.toUpperCase();
911         // this.invalidHandleTypes[type] = null;
912         delete this.invalidHandleTypes[type];
913     },
914
915     /**
916      * Unsets an invalid handle id
917      * @method removeInvalidHandleId
918      * @param {string} id the id of the element to re-enable
919      */
920     removeInvalidHandleId: function(id) {
921         if (typeof id !== "string") {
922             id = Roo.id(id);
923         }
924         delete this.invalidHandleIds[id];
925     },
926
927     /**
928      * Unsets an invalid css class
929      * @method removeInvalidHandleClass
930      * @param {string} cssClass the class of the element(s) you wish to
931      * re-enable
932      */
933     removeInvalidHandleClass: function(cssClass) {
934         for (var i=0, len=this.invalidHandleClasses.length; i<len; ++i) {
935             if (this.invalidHandleClasses[i] == cssClass) {
936                 delete this.invalidHandleClasses[i];
937             }
938         }
939     },
940
941     /**
942      * Checks the tag exclusion list to see if this click should be ignored
943      * @method isValidHandleChild
944      * @param {HTMLElement} node the HTMLElement to evaluate
945      * @return {boolean} true if this is a valid tag type, false if not
946      */
947     isValidHandleChild: function(node) {
948
949         var valid = true;
950         // var n = (node.nodeName == "#text") ? node.parentNode : node;
951         var nodeName;
952         try {
953             nodeName = node.nodeName.toUpperCase();
954         } catch(e) {
955             nodeName = node.nodeName;
956         }
957         valid = valid && !this.invalidHandleTypes[nodeName];
958         valid = valid && !this.invalidHandleIds[node.id];
959
960         for (var i=0, len=this.invalidHandleClasses.length; valid && i<len; ++i) {
961             valid = !Dom.hasClass(node, this.invalidHandleClasses[i]);
962         }
963
964
965         return valid;
966
967     },
968
969     /**
970      * Create the array of horizontal tick marks if an interval was specified
971      * in setXConstraint().
972      * @method setXTicks
973      * @private
974      */
975     setXTicks: function(iStartX, iTickSize) {
976         this.xTicks = [];
977         this.xTickSize = iTickSize;
978
979         var tickMap = {};
980
981         for (var i = this.initPageX; i >= this.minX; i = i - iTickSize) {
982             if (!tickMap[i]) {
983                 this.xTicks[this.xTicks.length] = i;
984                 tickMap[i] = true;
985             }
986         }
987
988         for (i = this.initPageX; i <= this.maxX; i = i + iTickSize) {
989             if (!tickMap[i]) {
990                 this.xTicks[this.xTicks.length] = i;
991                 tickMap[i] = true;
992             }
993         }
994
995         this.xTicks.sort(this.DDM.numericSort) ;
996     },
997
998     /**
999      * Create the array of vertical tick marks if an interval was specified in
1000      * setYConstraint().
1001      * @method setYTicks
1002      * @private
1003      */
1004     setYTicks: function(iStartY, iTickSize) {
1005         this.yTicks = [];
1006         this.yTickSize = iTickSize;
1007
1008         var tickMap = {};
1009
1010         for (var i = this.initPageY; i >= this.minY; i = i - iTickSize) {
1011             if (!tickMap[i]) {
1012                 this.yTicks[this.yTicks.length] = i;
1013                 tickMap[i] = true;
1014             }
1015         }
1016
1017         for (i = this.initPageY; i <= this.maxY; i = i + iTickSize) {
1018             if (!tickMap[i]) {
1019                 this.yTicks[this.yTicks.length] = i;
1020                 tickMap[i] = true;
1021             }
1022         }
1023
1024         this.yTicks.sort(this.DDM.numericSort) ;
1025     },
1026
1027     /**
1028      * By default, the element can be dragged any place on the screen.  Use
1029      * this method to limit the horizontal travel of the element.  Pass in
1030      * 0,0 for the parameters if you want to lock the drag to the y axis.
1031      * @method setXConstraint
1032      * @param {int} iLeft the number of pixels the element can move to the left
1033      * @param {int} iRight the number of pixels the element can move to the
1034      * right
1035      * @param {int} iTickSize optional parameter for specifying that the
1036      * element
1037      * should move iTickSize pixels at a time.
1038      */
1039     setXConstraint: function(iLeft, iRight, iTickSize) {
1040         this.leftConstraint = iLeft;
1041         this.rightConstraint = iRight;
1042
1043         this.minX = this.initPageX - iLeft;
1044         this.maxX = this.initPageX + iRight;
1045         if (iTickSize) { this.setXTicks(this.initPageX, iTickSize); }
1046
1047         this.constrainX = true;
1048     },
1049
1050     /**
1051      * Clears any constraints applied to this instance.  Also clears ticks
1052      * since they can't exist independent of a constraint at this time.
1053      * @method clearConstraints
1054      */
1055     clearConstraints: function() {
1056         this.constrainX = false;
1057         this.constrainY = false;
1058         this.clearTicks();
1059     },
1060
1061     /**
1062      * Clears any tick interval defined for this instance
1063      * @method clearTicks
1064      */
1065     clearTicks: function() {
1066         this.xTicks = null;
1067         this.yTicks = null;
1068         this.xTickSize = 0;
1069         this.yTickSize = 0;
1070     },
1071
1072     /**
1073      * By default, the element can be dragged any place on the screen.  Set
1074      * this to limit the vertical travel of the element.  Pass in 0,0 for the
1075      * parameters if you want to lock the drag to the x axis.
1076      * @method setYConstraint
1077      * @param {int} iUp the number of pixels the element can move up
1078      * @param {int} iDown the number of pixels the element can move down
1079      * @param {int} iTickSize optional parameter for specifying that the
1080      * element should move iTickSize pixels at a time.
1081      */
1082     setYConstraint: function(iUp, iDown, iTickSize) {
1083         this.topConstraint = iUp;
1084         this.bottomConstraint = iDown;
1085
1086         this.minY = this.initPageY - iUp;
1087         this.maxY = this.initPageY + iDown;
1088         if (iTickSize) { this.setYTicks(this.initPageY, iTickSize); }
1089
1090         this.constrainY = true;
1091
1092     },
1093
1094     /**
1095      * resetConstraints must be called if you manually reposition a dd element.
1096      * @method resetConstraints
1097      * @param {boolean} maintainOffset
1098      */
1099     resetConstraints: function() {
1100
1101
1102         // Maintain offsets if necessary
1103         if (this.initPageX || this.initPageX === 0) {
1104             // figure out how much this thing has moved
1105             var dx = (this.maintainOffset) ? this.lastPageX - this.initPageX : 0;
1106             var dy = (this.maintainOffset) ? this.lastPageY - this.initPageY : 0;
1107
1108             this.setInitPosition(dx, dy);
1109
1110         // This is the first time we have detected the element's position
1111         } else {
1112             this.setInitPosition();
1113         }
1114
1115         if (this.constrainX) {
1116             this.setXConstraint( this.leftConstraint,
1117                                  this.rightConstraint,
1118                                  this.xTickSize        );
1119         }
1120
1121         if (this.constrainY) {
1122             this.setYConstraint( this.topConstraint,
1123                                  this.bottomConstraint,
1124                                  this.yTickSize         );
1125         }
1126     },
1127
1128     /**
1129      * Normally the drag element is moved pixel by pixel, but we can specify
1130      * that it move a number of pixels at a time.  This method resolves the
1131      * location when we have it set up like this.
1132      * @method getTick
1133      * @param {int} val where we want to place the object
1134      * @param {int[]} tickArray sorted array of valid points
1135      * @return {int} the closest tick
1136      * @private
1137      */
1138     getTick: function(val, tickArray) {
1139
1140         if (!tickArray) {
1141             // If tick interval is not defined, it is effectively 1 pixel,
1142             // so we return the value passed to us.
1143             return val;
1144         } else if (tickArray[0] >= val) {
1145             // The value is lower than the first tick, so we return the first
1146             // tick.
1147             return tickArray[0];
1148         } else {
1149             for (var i=0, len=tickArray.length; i<len; ++i) {
1150                 var next = i + 1;
1151                 if (tickArray[next] && tickArray[next] >= val) {
1152                     var diff1 = val - tickArray[i];
1153                     var diff2 = tickArray[next] - val;
1154                     return (diff2 > diff1) ? tickArray[i] : tickArray[next];
1155                 }
1156             }
1157
1158             // The value is larger than the last tick, so we return the last
1159             // tick.
1160             return tickArray[tickArray.length - 1];
1161         }
1162     },
1163
1164     /**
1165      * toString method
1166      * @method toString
1167      * @return {string} string representation of the dd obj
1168      */
1169     toString: function() {
1170         return ("DragDrop " + this.id);
1171     }
1172
1173 });
1174
1175 })();
1176 /*
1177  * Based on:
1178  * Ext JS Library 1.1.1
1179  * Copyright(c) 2006-2007, Ext JS, LLC.
1180  *
1181  * Originally Released Under LGPL - original licence link has changed is not relivant.
1182  *
1183  * Fork - LGPL
1184  * <script type="text/javascript">
1185  */
1186
1187
1188 /**
1189  * The drag and drop utility provides a framework for building drag and drop
1190  * applications.  In addition to enabling drag and drop for specific elements,
1191  * the drag and drop elements are tracked by the manager class, and the
1192  * interactions between the various elements are tracked during the drag and
1193  * the implementing code is notified about these important moments.
1194  */
1195
1196 // Only load the library once.  Rewriting the manager class would orphan
1197 // existing drag and drop instances.
1198 if (!Roo.dd.DragDropMgr) {
1199
1200 /**
1201  * @class Roo.dd.DragDropMgr
1202  * DragDropMgr is a singleton that tracks the element interaction for
1203  * all DragDrop items in the window.  Generally, you will not call
1204  * this class directly, but it does have helper methods that could
1205  * be useful in your DragDrop implementations.
1206  * @singleton
1207  */
1208 Roo.dd.DragDropMgr = function() {
1209
1210     var Event = Roo.EventManager;
1211
1212     return {
1213
1214         /**
1215          * Two dimensional Array of registered DragDrop objects.  The first
1216          * dimension is the DragDrop item group, the second the DragDrop
1217          * object.
1218          * @property ids
1219          * @type {string: string}
1220          * @private
1221          * @static
1222          */
1223         ids: {},
1224
1225         /**
1226          * Array of element ids defined as drag handles.  Used to determine
1227          * if the element that generated the mousedown event is actually the
1228          * handle and not the html element itself.
1229          * @property handleIds
1230          * @type {string: string}
1231          * @private
1232          * @static
1233          */
1234         handleIds: {},
1235
1236         /**
1237          * the DragDrop object that is currently being dragged
1238          * @property dragCurrent
1239          * @type DragDrop
1240          * @private
1241          * @static
1242          **/
1243         dragCurrent: null,
1244
1245         /**
1246          * the DragDrop object(s) that are being hovered over
1247          * @property dragOvers
1248          * @type Array
1249          * @private
1250          * @static
1251          */
1252         dragOvers: {},
1253
1254         /**
1255          * the X distance between the cursor and the object being dragged
1256          * @property deltaX
1257          * @type int
1258          * @private
1259          * @static
1260          */
1261         deltaX: 0,
1262
1263         /**
1264          * the Y distance between the cursor and the object being dragged
1265          * @property deltaY
1266          * @type int
1267          * @private
1268          * @static
1269          */
1270         deltaY: 0,
1271
1272         /**
1273          * Flag to determine if we should prevent the default behavior of the
1274          * events we define. By default this is true, but this can be set to
1275          * false if you need the default behavior (not recommended)
1276          * @property preventDefault
1277          * @type boolean
1278          * @static
1279          */
1280         preventDefault: true,
1281
1282         /**
1283          * Flag to determine if we should stop the propagation of the events
1284          * we generate. This is true by default but you may want to set it to
1285          * false if the html element contains other features that require the
1286          * mouse click.
1287          * @property stopPropagation
1288          * @type boolean
1289          * @static
1290          */
1291         stopPropagation: true,
1292
1293         /**
1294          * Internal flag that is set to true when drag and drop has been
1295          * intialized
1296          * @property initialized
1297          * @private
1298          * @static
1299          */
1300         initalized: false,
1301
1302         /**
1303          * All drag and drop can be disabled.
1304          * @property locked
1305          * @private
1306          * @static
1307          */
1308         locked: false,
1309
1310         /**
1311          * Called the first time an element is registered.
1312          * @method init
1313          * @private
1314          * @static
1315          */
1316         init: function() {
1317             this.initialized = true;
1318         },
1319
1320         /**
1321          * In point mode, drag and drop interaction is defined by the
1322          * location of the cursor during the drag/drop
1323          * @property POINT
1324          * @type int
1325          * @static
1326          */
1327         POINT: 0,
1328
1329         /**
1330          * In intersect mode, drag and drop interactio nis defined by the
1331          * overlap of two or more drag and drop objects.
1332          * @property INTERSECT
1333          * @type int
1334          * @static
1335          */
1336         INTERSECT: 1,
1337
1338         /**
1339          * The current drag and drop mode.  Default: POINT
1340          * @property mode
1341          * @type int
1342          * @static
1343          */
1344         mode: 0,
1345
1346         /**
1347          * Runs method on all drag and drop objects
1348          * @method _execOnAll
1349          * @private
1350          * @static
1351          */
1352         _execOnAll: function(sMethod, args) {
1353             for (var i in this.ids) {
1354                 for (var j in this.ids[i]) {
1355                     var oDD = this.ids[i][j];
1356                     if (! this.isTypeOfDD(oDD)) {
1357                         continue;
1358                     }
1359                     oDD[sMethod].apply(oDD, args);
1360                 }
1361             }
1362         },
1363
1364         /**
1365          * Drag and drop initialization.  Sets up the global event handlers
1366          * @method _onLoad
1367          * @private
1368          * @static
1369          */
1370         _onLoad: function() {
1371
1372             this.init();
1373
1374             if (!Roo.isTouch) {
1375                 Event.on(document, "mouseup",   this.handleMouseUp, this, true);
1376                 Event.on(document, "mousemove", this.handleMouseMove, this, true);
1377             }
1378             Event.on(document, "touchend",   this.handleMouseUp, this, true);
1379             Event.on(document, "touchmove", this.handleMouseMove, this, true);
1380             
1381             Event.on(window,   "unload",    this._onUnload, this, true);
1382             Event.on(window,   "resize",    this._onResize, this, true);
1383             // Event.on(window,   "mouseout",    this._test);
1384
1385         },
1386
1387         /**
1388          * Reset constraints on all drag and drop objs
1389          * @method _onResize
1390          * @private
1391          * @static
1392          */
1393         _onResize: function(e) {
1394             this._execOnAll("resetConstraints", []);
1395         },
1396
1397         /**
1398          * Lock all drag and drop functionality
1399          * @method lock
1400          * @static
1401          */
1402         lock: function() { this.locked = true; },
1403
1404         /**
1405          * Unlock all drag and drop functionality
1406          * @method unlock
1407          * @static
1408          */
1409         unlock: function() { this.locked = false; },
1410
1411         /**
1412          * Is drag and drop locked?
1413          * @method isLocked
1414          * @return {boolean} True if drag and drop is locked, false otherwise.
1415          * @static
1416          */
1417         isLocked: function() { return this.locked; },
1418
1419         /**
1420          * Location cache that is set for all drag drop objects when a drag is
1421          * initiated, cleared when the drag is finished.
1422          * @property locationCache
1423          * @private
1424          * @static
1425          */
1426         locationCache: {},
1427
1428         /**
1429          * Set useCache to false if you want to force object the lookup of each
1430          * drag and drop linked element constantly during a drag.
1431          * @property useCache
1432          * @type boolean
1433          * @static
1434          */
1435         useCache: true,
1436
1437         /**
1438          * The number of pixels that the mouse needs to move after the
1439          * mousedown before the drag is initiated.  Default=3;
1440          * @property clickPixelThresh
1441          * @type int
1442          * @static
1443          */
1444         clickPixelThresh: 3,
1445
1446         /**
1447          * The number of milliseconds after the mousedown event to initiate the
1448          * drag if we don't get a mouseup event. Default=1000
1449          * @property clickTimeThresh
1450          * @type int
1451          * @static
1452          */
1453         clickTimeThresh: 350,
1454
1455         /**
1456          * Flag that indicates that either the drag pixel threshold or the
1457          * mousdown time threshold has been met
1458          * @property dragThreshMet
1459          * @type boolean
1460          * @private
1461          * @static
1462          */
1463         dragThreshMet: false,
1464
1465         /**
1466          * Timeout used for the click time threshold
1467          * @property clickTimeout
1468          * @type Object
1469          * @private
1470          * @static
1471          */
1472         clickTimeout: null,
1473
1474         /**
1475          * The X position of the mousedown event stored for later use when a
1476          * drag threshold is met.
1477          * @property startX
1478          * @type int
1479          * @private
1480          * @static
1481          */
1482         startX: 0,
1483
1484         /**
1485          * The Y position of the mousedown event stored for later use when a
1486          * drag threshold is met.
1487          * @property startY
1488          * @type int
1489          * @private
1490          * @static
1491          */
1492         startY: 0,
1493
1494         /**
1495          * Each DragDrop instance must be registered with the DragDropMgr.
1496          * This is executed in DragDrop.init()
1497          * @method regDragDrop
1498          * @param {DragDrop} oDD the DragDrop object to register
1499          * @param {String} sGroup the name of the group this element belongs to
1500          * @static
1501          */
1502         regDragDrop: function(oDD, sGroup) {
1503             if (!this.initialized) { this.init(); }
1504
1505             if (!this.ids[sGroup]) {
1506                 this.ids[sGroup] = {};
1507             }
1508             this.ids[sGroup][oDD.id] = oDD;
1509         },
1510
1511         /**
1512          * Removes the supplied dd instance from the supplied group. Executed
1513          * by DragDrop.removeFromGroup, so don't call this function directly.
1514          * @method removeDDFromGroup
1515          * @private
1516          * @static
1517          */
1518         removeDDFromGroup: function(oDD, sGroup) {
1519             if (!this.ids[sGroup]) {
1520                 this.ids[sGroup] = {};
1521             }
1522
1523             var obj = this.ids[sGroup];
1524             if (obj && obj[oDD.id]) {
1525                 delete obj[oDD.id];
1526             }
1527         },
1528
1529         /**
1530          * Unregisters a drag and drop item.  This is executed in
1531          * DragDrop.unreg, use that method instead of calling this directly.
1532          * @method _remove
1533          * @private
1534          * @static
1535          */
1536         _remove: function(oDD) {
1537             for (var g in oDD.groups) {
1538                 if (g && this.ids[g][oDD.id]) {
1539                     delete this.ids[g][oDD.id];
1540                 }
1541             }
1542             delete this.handleIds[oDD.id];
1543         },
1544
1545         /**
1546          * Each DragDrop handle element must be registered.  This is done
1547          * automatically when executing DragDrop.setHandleElId()
1548          * @method regHandle
1549          * @param {String} sDDId the DragDrop id this element is a handle for
1550          * @param {String} sHandleId the id of the element that is the drag
1551          * handle
1552          * @static
1553          */
1554         regHandle: function(sDDId, sHandleId) {
1555             if (!this.handleIds[sDDId]) {
1556                 this.handleIds[sDDId] = {};
1557             }
1558             this.handleIds[sDDId][sHandleId] = sHandleId;
1559         },
1560
1561         /**
1562          * Utility function to determine if a given element has been
1563          * registered as a drag drop item.
1564          * @method isDragDrop
1565          * @param {String} id the element id to check
1566          * @return {boolean} true if this element is a DragDrop item,
1567          * false otherwise
1568          * @static
1569          */
1570         isDragDrop: function(id) {
1571             return ( this.getDDById(id) ) ? true : false;
1572         },
1573
1574         /**
1575          * Returns the drag and drop instances that are in all groups the
1576          * passed in instance belongs to.
1577          * @method getRelated
1578          * @param {DragDrop} p_oDD the obj to get related data for
1579          * @param {boolean} bTargetsOnly if true, only return targetable objs
1580          * @return {DragDrop[]} the related instances
1581          * @static
1582          */
1583         getRelated: function(p_oDD, bTargetsOnly) {
1584             var oDDs = [];
1585             for (var i in p_oDD.groups) {
1586                 for (j in this.ids[i]) {
1587                     var dd = this.ids[i][j];
1588                     if (! this.isTypeOfDD(dd)) {
1589                         continue;
1590                     }
1591                     if (!bTargetsOnly || dd.isTarget) {
1592                         oDDs[oDDs.length] = dd;
1593                     }
1594                 }
1595             }
1596
1597             return oDDs;
1598         },
1599
1600         /**
1601          * Returns true if the specified dd target is a legal target for
1602          * the specifice drag obj
1603          * @method isLegalTarget
1604          * @param {DragDrop} the drag obj
1605          * @param {DragDrop} the target
1606          * @return {boolean} true if the target is a legal target for the
1607          * dd obj
1608          * @static
1609          */
1610         isLegalTarget: function (oDD, oTargetDD) {
1611             var targets = this.getRelated(oDD, true);
1612             for (var i=0, len=targets.length;i<len;++i) {
1613                 if (targets[i].id == oTargetDD.id) {
1614                     return true;
1615                 }
1616             }
1617
1618             return false;
1619         },
1620
1621         /**
1622          * My goal is to be able to transparently determine if an object is
1623          * typeof DragDrop, and the exact subclass of DragDrop.  typeof
1624          * returns "object", oDD.constructor.toString() always returns
1625          * "DragDrop" and not the name of the subclass.  So for now it just
1626          * evaluates a well-known variable in DragDrop.
1627          * @method isTypeOfDD
1628          * @param {Object} the object to evaluate
1629          * @return {boolean} true if typeof oDD = DragDrop
1630          * @static
1631          */
1632         isTypeOfDD: function (oDD) {
1633             return (oDD && oDD.__ygDragDrop);
1634         },
1635
1636         /**
1637          * Utility function to determine if a given element has been
1638          * registered as a drag drop handle for the given Drag Drop object.
1639          * @method isHandle
1640          * @param {String} id the element id to check
1641          * @return {boolean} true if this element is a DragDrop handle, false
1642          * otherwise
1643          * @static
1644          */
1645         isHandle: function(sDDId, sHandleId) {
1646             return ( this.handleIds[sDDId] &&
1647                             this.handleIds[sDDId][sHandleId] );
1648         },
1649
1650         /**
1651          * Returns the DragDrop instance for a given id
1652          * @method getDDById
1653          * @param {String} id the id of the DragDrop object
1654          * @return {DragDrop} the drag drop object, null if it is not found
1655          * @static
1656          */
1657         getDDById: function(id) {
1658             for (var i in this.ids) {
1659                 if (this.ids[i][id]) {
1660                     return this.ids[i][id];
1661                 }
1662             }
1663             return null;
1664         },
1665
1666         /**
1667          * Fired after a registered DragDrop object gets the mousedown event.
1668          * Sets up the events required to track the object being dragged
1669          * @method handleMouseDown
1670          * @param {Event} e the event
1671          * @param oDD the DragDrop object being dragged
1672          * @private
1673          * @static
1674          */
1675         handleMouseDown: function(e, oDD) {
1676             if(Roo.QuickTips){
1677                 Roo.QuickTips.disable();
1678             }
1679             this.currentTarget = e.getTarget();
1680
1681             this.dragCurrent = oDD;
1682
1683             var el = oDD.getEl();
1684
1685             // track start position
1686             this.startX = e.getPageX();
1687             this.startY = e.getPageY();
1688
1689             this.deltaX = this.startX - el.offsetLeft;
1690             this.deltaY = this.startY - el.offsetTop;
1691
1692             this.dragThreshMet = false;
1693
1694             this.clickTimeout = setTimeout(
1695                     function() {
1696                         var DDM = Roo.dd.DDM;
1697                         DDM.startDrag(DDM.startX, DDM.startY);
1698                     },
1699                     this.clickTimeThresh );
1700         },
1701
1702         /**
1703          * Fired when either the drag pixel threshol or the mousedown hold
1704          * time threshold has been met.
1705          * @method startDrag
1706          * @param x {int} the X position of the original mousedown
1707          * @param y {int} the Y position of the original mousedown
1708          * @static
1709          */
1710         startDrag: function(x, y) {
1711             clearTimeout(this.clickTimeout);
1712             if (this.dragCurrent) {
1713                 this.dragCurrent.b4StartDrag(x, y);
1714                 this.dragCurrent.startDrag(x, y);
1715             }
1716             this.dragThreshMet = true;
1717         },
1718
1719         /**
1720          * Internal function to handle the mouseup event.  Will be invoked
1721          * from the context of the document.
1722          * @method handleMouseUp
1723          * @param {Event} e the event
1724          * @private
1725          * @static
1726          */
1727         handleMouseUp: function(e) {
1728
1729             if(Roo.QuickTips){
1730                 Roo.QuickTips.enable();
1731             }
1732             if (! this.dragCurrent) {
1733                 return;
1734             }
1735
1736             clearTimeout(this.clickTimeout);
1737
1738             if (this.dragThreshMet) {
1739                 this.fireEvents(e, true);
1740             } else {
1741             }
1742
1743             this.stopDrag(e);
1744
1745             this.stopEvent(e);
1746         },
1747
1748         /**
1749          * Utility to stop event propagation and event default, if these
1750          * features are turned on.
1751          * @method stopEvent
1752          * @param {Event} e the event as returned by this.getEvent()
1753          * @static
1754          */
1755         stopEvent: function(e){
1756             if(this.stopPropagation) {
1757                 e.stopPropagation();
1758             }
1759
1760             if (this.preventDefault) {
1761                 e.preventDefault();
1762             }
1763         },
1764
1765         /**
1766          * Internal function to clean up event handlers after the drag
1767          * operation is complete
1768          * @method stopDrag
1769          * @param {Event} e the event
1770          * @private
1771          * @static
1772          */
1773         stopDrag: function(e) {
1774             // Fire the drag end event for the item that was dragged
1775             if (this.dragCurrent) {
1776                 if (this.dragThreshMet) {
1777                     this.dragCurrent.b4EndDrag(e);
1778                     this.dragCurrent.endDrag(e);
1779                 }
1780
1781                 this.dragCurrent.onMouseUp(e);
1782             }
1783
1784             this.dragCurrent = null;
1785             this.dragOvers = {};
1786         },
1787
1788         /**
1789          * Internal function to handle the mousemove event.  Will be invoked
1790          * from the context of the html element.
1791          *
1792          * @TODO figure out what we can do about mouse events lost when the
1793          * user drags objects beyond the window boundary.  Currently we can
1794          * detect this in internet explorer by verifying that the mouse is
1795          * down during the mousemove event.  Firefox doesn't give us the
1796          * button state on the mousemove event.
1797          * @method handleMouseMove
1798          * @param {Event} e the event
1799          * @private
1800          * @static
1801          */
1802         handleMouseMove: function(e) {
1803             if (! this.dragCurrent) {
1804                 return true;
1805             }
1806
1807             // var button = e.which || e.button;
1808
1809             // check for IE mouseup outside of page boundary
1810             if (Roo.isIE && (e.button !== 0 && e.button !== 1 && e.button !== 2)) {
1811                 this.stopEvent(e);
1812                 return this.handleMouseUp(e);
1813             }
1814
1815             if (!this.dragThreshMet) {
1816                 var diffX = Math.abs(this.startX - e.getPageX());
1817                 var diffY = Math.abs(this.startY - e.getPageY());
1818                 if (diffX > this.clickPixelThresh ||
1819                             diffY > this.clickPixelThresh) {
1820                     this.startDrag(this.startX, this.startY);
1821                 }
1822             }
1823
1824             if (this.dragThreshMet) {
1825                 this.dragCurrent.b4Drag(e);
1826                 this.dragCurrent.onDrag(e);
1827                 if(!this.dragCurrent.moveOnly){
1828                     this.fireEvents(e, false);
1829                 }
1830             }
1831
1832             this.stopEvent(e);
1833
1834             return true;
1835         },
1836
1837         /**
1838          * Iterates over all of the DragDrop elements to find ones we are
1839          * hovering over or dropping on
1840          * @method fireEvents
1841          * @param {Event} e the event
1842          * @param {boolean} isDrop is this a drop op or a mouseover op?
1843          * @private
1844          * @static
1845          */
1846         fireEvents: function(e, isDrop) {
1847             var dc = this.dragCurrent;
1848
1849             // If the user did the mouse up outside of the window, we could
1850             // get here even though we have ended the drag.
1851             if (!dc || dc.isLocked()) {
1852                 return;
1853             }
1854
1855             var pt = e.getPoint();
1856
1857             // cache the previous dragOver array
1858             var oldOvers = [];
1859
1860             var outEvts   = [];
1861             var overEvts  = [];
1862             var dropEvts  = [];
1863             var enterEvts = [];
1864
1865             // Check to see if the object(s) we were hovering over is no longer
1866             // being hovered over so we can fire the onDragOut event
1867             for (var i in this.dragOvers) {
1868
1869                 var ddo = this.dragOvers[i];
1870
1871                 if (! this.isTypeOfDD(ddo)) {
1872                     continue;
1873                 }
1874
1875                 if (! this.isOverTarget(pt, ddo, this.mode)) {
1876                     outEvts.push( ddo );
1877                 }
1878
1879                 oldOvers[i] = true;
1880                 delete this.dragOvers[i];
1881             }
1882
1883             for (var sGroup in dc.groups) {
1884
1885                 if ("string" != typeof sGroup) {
1886                     continue;
1887                 }
1888
1889                 for (i in this.ids[sGroup]) {
1890                     var oDD = this.ids[sGroup][i];
1891                     if (! this.isTypeOfDD(oDD)) {
1892                         continue;
1893                     }
1894
1895                     if (oDD.isTarget && !oDD.isLocked() && oDD != dc) {
1896                         if (this.isOverTarget(pt, oDD, this.mode)) {
1897                             // look for drop interactions
1898                             if (isDrop) {
1899                                 dropEvts.push( oDD );
1900                             // look for drag enter and drag over interactions
1901                             } else {
1902
1903                                 // initial drag over: dragEnter fires
1904                                 if (!oldOvers[oDD.id]) {
1905                                     enterEvts.push( oDD );
1906                                 // subsequent drag overs: dragOver fires
1907                                 } else {
1908                                     overEvts.push( oDD );
1909                                 }
1910
1911                                 this.dragOvers[oDD.id] = oDD;
1912                             }
1913                         }
1914                     }
1915                 }
1916             }
1917
1918             if (this.mode) {
1919                 if (outEvts.length) {
1920                     dc.b4DragOut(e, outEvts);
1921                     dc.onDragOut(e, outEvts);
1922                 }
1923
1924                 if (enterEvts.length) {
1925                     dc.onDragEnter(e, enterEvts);
1926                 }
1927
1928                 if (overEvts.length) {
1929                     dc.b4DragOver(e, overEvts);
1930                     dc.onDragOver(e, overEvts);
1931                 }
1932
1933                 if (dropEvts.length) {
1934                     dc.b4DragDrop(e, dropEvts);
1935                     dc.onDragDrop(e, dropEvts);
1936                 }
1937
1938             } else {
1939                 // fire dragout events
1940                 var len = 0;
1941                 for (i=0, len=outEvts.length; i<len; ++i) {
1942                     dc.b4DragOut(e, outEvts[i].id);
1943                     dc.onDragOut(e, outEvts[i].id);
1944                 }
1945
1946                 // fire enter events
1947                 for (i=0,len=enterEvts.length; i<len; ++i) {
1948                     // dc.b4DragEnter(e, oDD.id);
1949                     dc.onDragEnter(e, enterEvts[i].id);
1950                 }
1951
1952                 // fire over events
1953                 for (i=0,len=overEvts.length; i<len; ++i) {
1954                     dc.b4DragOver(e, overEvts[i].id);
1955                     dc.onDragOver(e, overEvts[i].id);
1956                 }
1957
1958                 // fire drop events
1959                 for (i=0, len=dropEvts.length; i<len; ++i) {
1960                     dc.b4DragDrop(e, dropEvts[i].id);
1961                     dc.onDragDrop(e, dropEvts[i].id);
1962                 }
1963
1964             }
1965
1966             // notify about a drop that did not find a target
1967             if (isDrop && !dropEvts.length) {
1968                 dc.onInvalidDrop(e);
1969             }
1970
1971         },
1972
1973         /**
1974          * Helper function for getting the best match from the list of drag
1975          * and drop objects returned by the drag and drop events when we are
1976          * in INTERSECT mode.  It returns either the first object that the
1977          * cursor is over, or the object that has the greatest overlap with
1978          * the dragged element.
1979          * @method getBestMatch
1980          * @param  {DragDrop[]} dds The array of drag and drop objects
1981          * targeted
1982          * @return {DragDrop}       The best single match
1983          * @static
1984          */
1985         getBestMatch: function(dds) {
1986             var winner = null;
1987             // Return null if the input is not what we expect
1988             //if (!dds || !dds.length || dds.length == 0) {
1989                // winner = null;
1990             // If there is only one item, it wins
1991             //} else if (dds.length == 1) {
1992
1993             var len = dds.length;
1994
1995             if (len == 1) {
1996                 winner = dds[0];
1997             } else {
1998                 // Loop through the targeted items
1999                 for (var i=0; i<len; ++i) {
2000                     var dd = dds[i];
2001                     // If the cursor is over the object, it wins.  If the
2002                     // cursor is over multiple matches, the first one we come
2003                     // to wins.
2004                     if (dd.cursorIsOver) {
2005                         winner = dd;
2006                         break;
2007                     // Otherwise the object with the most overlap wins
2008                     } else {
2009                         if (!winner ||
2010                             winner.overlap.getArea() < dd.overlap.getArea()) {
2011                             winner = dd;
2012                         }
2013                     }
2014                 }
2015             }
2016
2017             return winner;
2018         },
2019
2020         /**
2021          * Refreshes the cache of the top-left and bottom-right points of the
2022          * drag and drop objects in the specified group(s).  This is in the
2023          * format that is stored in the drag and drop instance, so typical
2024          * usage is:
2025          * <code>
2026          * Roo.dd.DragDropMgr.refreshCache(ddinstance.groups);
2027          * </code>
2028          * Alternatively:
2029          * <code>
2030          * Roo.dd.DragDropMgr.refreshCache({group1:true, group2:true});
2031          * </code>
2032          * @TODO this really should be an indexed array.  Alternatively this
2033          * method could accept both.
2034          * @method refreshCache
2035          * @param {Object} groups an associative array of groups to refresh
2036          * @static
2037          */
2038         refreshCache: function(groups) {
2039             for (var sGroup in groups) {
2040                 if ("string" != typeof sGroup) {
2041                     continue;
2042                 }
2043                 for (var i in this.ids[sGroup]) {
2044                     var oDD = this.ids[sGroup][i];
2045
2046                     if (this.isTypeOfDD(oDD)) {
2047                     // if (this.isTypeOfDD(oDD) && oDD.isTarget) {
2048                         var loc = this.getLocation(oDD);
2049                         if (loc) {
2050                             this.locationCache[oDD.id] = loc;
2051                         } else {
2052                             delete this.locationCache[oDD.id];
2053                             // this will unregister the drag and drop object if
2054                             // the element is not in a usable state
2055                             // oDD.unreg();
2056                         }
2057                     }
2058                 }
2059             }
2060         },
2061
2062         /**
2063          * This checks to make sure an element exists and is in the DOM.  The
2064          * main purpose is to handle cases where innerHTML is used to remove
2065          * drag and drop objects from the DOM.  IE provides an 'unspecified
2066          * error' when trying to access the offsetParent of such an element
2067          * @method verifyEl
2068          * @param {HTMLElement} el the element to check
2069          * @return {boolean} true if the element looks usable
2070          * @static
2071          */
2072         verifyEl: function(el) {
2073             if (el) {
2074                 var parent;
2075                 if(Roo.isIE){
2076                     try{
2077                         parent = el.offsetParent;
2078                     }catch(e){}
2079                 }else{
2080                     parent = el.offsetParent;
2081                 }
2082                 if (parent) {
2083                     return true;
2084                 }
2085             }
2086
2087             return false;
2088         },
2089
2090         /**
2091          * Returns a Region object containing the drag and drop element's position
2092          * and size, including the padding configured for it
2093          * @method getLocation
2094          * @param {DragDrop} oDD the drag and drop object to get the
2095          *                       location for
2096          * @return {Roo.lib.Region} a Region object representing the total area
2097          *                             the element occupies, including any padding
2098          *                             the instance is configured for.
2099          * @static
2100          */
2101         getLocation: function(oDD) {
2102             if (! this.isTypeOfDD(oDD)) {
2103                 return null;
2104             }
2105
2106             var el = oDD.getEl(), pos, x1, x2, y1, y2, t, r, b, l;
2107
2108             try {
2109                 pos= Roo.lib.Dom.getXY(el);
2110             } catch (e) { }
2111
2112             if (!pos) {
2113                 return null;
2114             }
2115
2116             x1 = pos[0];
2117             x2 = x1 + el.offsetWidth;
2118             y1 = pos[1];
2119             y2 = y1 + el.offsetHeight;
2120
2121             t = y1 - oDD.padding[0];
2122             r = x2 + oDD.padding[1];
2123             b = y2 + oDD.padding[2];
2124             l = x1 - oDD.padding[3];
2125
2126             return new Roo.lib.Region( t, r, b, l );
2127         },
2128
2129         /**
2130          * Checks the cursor location to see if it over the target
2131          * @method isOverTarget
2132          * @param {Roo.lib.Point} pt The point to evaluate
2133          * @param {DragDrop} oTarget the DragDrop object we are inspecting
2134          * @return {boolean} true if the mouse is over the target
2135          * @private
2136          * @static
2137          */
2138         isOverTarget: function(pt, oTarget, intersect) {
2139             // use cache if available
2140             var loc = this.locationCache[oTarget.id];
2141             if (!loc || !this.useCache) {
2142                 loc = this.getLocation(oTarget);
2143                 this.locationCache[oTarget.id] = loc;
2144
2145             }
2146
2147             if (!loc) {
2148                 return false;
2149             }
2150
2151             oTarget.cursorIsOver = loc.contains( pt );
2152
2153             // DragDrop is using this as a sanity check for the initial mousedown
2154             // in this case we are done.  In POINT mode, if the drag obj has no
2155             // contraints, we are also done. Otherwise we need to evaluate the
2156             // location of the target as related to the actual location of the
2157             // dragged element.
2158             var dc = this.dragCurrent;
2159             if (!dc || !dc.getTargetCoord ||
2160                     (!intersect && !dc.constrainX && !dc.constrainY)) {
2161                 return oTarget.cursorIsOver;
2162             }
2163
2164             oTarget.overlap = null;
2165
2166             // Get the current location of the drag element, this is the
2167             // location of the mouse event less the delta that represents
2168             // where the original mousedown happened on the element.  We
2169             // need to consider constraints and ticks as well.
2170             var pos = dc.getTargetCoord(pt.x, pt.y);
2171
2172             var el = dc.getDragEl();
2173             var curRegion = new Roo.lib.Region( pos.y,
2174                                                    pos.x + el.offsetWidth,
2175                                                    pos.y + el.offsetHeight,
2176                                                    pos.x );
2177
2178             var overlap = curRegion.intersect(loc);
2179
2180             if (overlap) {
2181                 oTarget.overlap = overlap;
2182                 return (intersect) ? true : oTarget.cursorIsOver;
2183             } else {
2184                 return false;
2185             }
2186         },
2187
2188         /**
2189          * unload event handler
2190          * @method _onUnload
2191          * @private
2192          * @static
2193          */
2194         _onUnload: function(e, me) {
2195             Roo.dd.DragDropMgr.unregAll();
2196         },
2197
2198         /**
2199          * Cleans up the drag and drop events and objects.
2200          * @method unregAll
2201          * @private
2202          * @static
2203          */
2204         unregAll: function() {
2205
2206             if (this.dragCurrent) {
2207                 this.stopDrag();
2208                 this.dragCurrent = null;
2209             }
2210
2211             this._execOnAll("unreg", []);
2212
2213             for (i in this.elementCache) {
2214                 delete this.elementCache[i];
2215             }
2216
2217             this.elementCache = {};
2218             this.ids = {};
2219         },
2220
2221         /**
2222          * A cache of DOM elements
2223          * @property elementCache
2224          * @private
2225          * @static
2226          */
2227         elementCache: {},
2228
2229         /**
2230          * Get the wrapper for the DOM element specified
2231          * @method getElWrapper
2232          * @param {String} id the id of the element to get
2233          * @return {Roo.dd.DDM.ElementWrapper} the wrapped element
2234          * @private
2235          * @deprecated This wrapper isn't that useful
2236          * @static
2237          */
2238         getElWrapper: function(id) {
2239             var oWrapper = this.elementCache[id];
2240             if (!oWrapper || !oWrapper.el) {
2241                 oWrapper = this.elementCache[id] =
2242                     new this.ElementWrapper(Roo.getDom(id));
2243             }
2244             return oWrapper;
2245         },
2246
2247         /**
2248          * Returns the actual DOM element
2249          * @method getElement
2250          * @param {String} id the id of the elment to get
2251          * @return {Object} The element
2252          * @deprecated use Roo.getDom instead
2253          * @static
2254          */
2255         getElement: function(id) {
2256             return Roo.getDom(id);
2257         },
2258
2259         /**
2260          * Returns the style property for the DOM element (i.e.,
2261          * document.getElById(id).style)
2262          * @method getCss
2263          * @param {String} id the id of the elment to get
2264          * @return {Object} The style property of the element
2265          * @deprecated use Roo.getDom instead
2266          * @static
2267          */
2268         getCss: function(id) {
2269             var el = Roo.getDom(id);
2270             return (el) ? el.style : null;
2271         },
2272
2273         /**
2274          * Inner class for cached elements
2275          * @class DragDropMgr.ElementWrapper
2276          * @for DragDropMgr
2277          * @private
2278          * @deprecated
2279          */
2280         ElementWrapper: function(el) {
2281                 /**
2282                  * The element
2283                  * @property el
2284                  */
2285                 this.el = el || null;
2286                 /**
2287                  * The element id
2288                  * @property id
2289                  */
2290                 this.id = this.el && el.id;
2291                 /**
2292                  * A reference to the style property
2293                  * @property css
2294                  */
2295                 this.css = this.el && el.style;
2296             },
2297
2298         /**
2299          * Returns the X position of an html element
2300          * @method getPosX
2301          * @param el the element for which to get the position
2302          * @return {int} the X coordinate
2303          * @for DragDropMgr
2304          * @deprecated use Roo.lib.Dom.getX instead
2305          * @static
2306          */
2307         getPosX: function(el) {
2308             return Roo.lib.Dom.getX(el);
2309         },
2310
2311         /**
2312          * Returns the Y position of an html element
2313          * @method getPosY
2314          * @param el the element for which to get the position
2315          * @return {int} the Y coordinate
2316          * @deprecated use Roo.lib.Dom.getY instead
2317          * @static
2318          */
2319         getPosY: function(el) {
2320             return Roo.lib.Dom.getY(el);
2321         },
2322
2323         /**
2324          * Swap two nodes.  In IE, we use the native method, for others we
2325          * emulate the IE behavior
2326          * @method swapNode
2327          * @param n1 the first node to swap
2328          * @param n2 the other node to swap
2329          * @static
2330          */
2331         swapNode: function(n1, n2) {
2332             if (n1.swapNode) {
2333                 n1.swapNode(n2);
2334             } else {
2335                 var p = n2.parentNode;
2336                 var s = n2.nextSibling;
2337
2338                 if (s == n1) {
2339                     p.insertBefore(n1, n2);
2340                 } else if (n2 == n1.nextSibling) {
2341                     p.insertBefore(n2, n1);
2342                 } else {
2343                     n1.parentNode.replaceChild(n2, n1);
2344                     p.insertBefore(n1, s);
2345                 }
2346             }
2347         },
2348
2349         /**
2350          * Returns the current scroll position
2351          * @method getScroll
2352          * @private
2353          * @static
2354          */
2355         getScroll: function () {
2356             var t, l, dde=document.documentElement, db=document.body;
2357             if (dde && (dde.scrollTop || dde.scrollLeft)) {
2358                 t = dde.scrollTop;
2359                 l = dde.scrollLeft;
2360             } else if (db) {
2361                 t = db.scrollTop;
2362                 l = db.scrollLeft;
2363             } else {
2364
2365             }
2366             return { top: t, left: l };
2367         },
2368
2369         /**
2370          * Returns the specified element style property
2371          * @method getStyle
2372          * @param {HTMLElement} el          the element
2373          * @param {string}      styleProp   the style property
2374          * @return {string} The value of the style property
2375          * @deprecated use Roo.lib.Dom.getStyle
2376          * @static
2377          */
2378         getStyle: function(el, styleProp) {
2379             return Roo.fly(el).getStyle(styleProp);
2380         },
2381
2382         /**
2383          * Gets the scrollTop
2384          * @method getScrollTop
2385          * @return {int} the document's scrollTop
2386          * @static
2387          */
2388         getScrollTop: function () { return this.getScroll().top; },
2389
2390         /**
2391          * Gets the scrollLeft
2392          * @method getScrollLeft
2393          * @return {int} the document's scrollTop
2394          * @static
2395          */
2396         getScrollLeft: function () { return this.getScroll().left; },
2397
2398         /**
2399          * Sets the x/y position of an element to the location of the
2400          * target element.
2401          * @method moveToEl
2402          * @param {HTMLElement} moveEl      The element to move
2403          * @param {HTMLElement} targetEl    The position reference element
2404          * @static
2405          */
2406         moveToEl: function (moveEl, targetEl) {
2407             var aCoord = Roo.lib.Dom.getXY(targetEl);
2408             Roo.lib.Dom.setXY(moveEl, aCoord);
2409         },
2410
2411         /**
2412          * Numeric array sort function
2413          * @method numericSort
2414          * @static
2415          */
2416         numericSort: function(a, b) { return (a - b); },
2417
2418         /**
2419          * Internal counter
2420          * @property _timeoutCount
2421          * @private
2422          * @static
2423          */
2424         _timeoutCount: 0,
2425
2426         /**
2427          * Trying to make the load order less important.  Without this we get
2428          * an error if this file is loaded before the Event Utility.
2429          * @method _addListeners
2430          * @private
2431          * @static
2432          */
2433         _addListeners: function() {
2434             var DDM = Roo.dd.DDM;
2435             if ( Roo.lib.Event && document ) {
2436                 DDM._onLoad();
2437             } else {
2438                 if (DDM._timeoutCount > 2000) {
2439                 } else {
2440                     setTimeout(DDM._addListeners, 10);
2441                     if (document && document.body) {
2442                         DDM._timeoutCount += 1;
2443                     }
2444                 }
2445             }
2446         },
2447
2448         /**
2449          * Recursively searches the immediate parent and all child nodes for
2450          * the handle element in order to determine wheter or not it was
2451          * clicked.
2452          * @method handleWasClicked
2453          * @param node the html element to inspect
2454          * @static
2455          */
2456         handleWasClicked: function(node, id) {
2457             if (this.isHandle(id, node.id)) {
2458                 return true;
2459             } else {
2460                 // check to see if this is a text node child of the one we want
2461                 var p = node.parentNode;
2462
2463                 while (p) {
2464                     if (this.isHandle(id, p.id)) {
2465                         return true;
2466                     } else {
2467                         p = p.parentNode;
2468                     }
2469                 }
2470             }
2471
2472             return false;
2473         }
2474
2475     };
2476
2477 }();
2478
2479 // shorter alias, save a few bytes
2480 Roo.dd.DDM = Roo.dd.DragDropMgr;
2481 Roo.dd.DDM._addListeners();
2482
2483 }/*
2484  * Based on:
2485  * Ext JS Library 1.1.1
2486  * Copyright(c) 2006-2007, Ext JS, LLC.
2487  *
2488  * Originally Released Under LGPL - original licence link has changed is not relivant.
2489  *
2490  * Fork - LGPL
2491  * <script type="text/javascript">
2492  */
2493
2494 /**
2495  * @class Roo.dd.DD
2496  * A DragDrop implementation where the linked element follows the
2497  * mouse cursor during a drag.
2498  * @extends Roo.dd.DragDrop
2499  * @constructor
2500  * @param {String} id the id of the linked element
2501  * @param {String} sGroup the group of related DragDrop items
2502  * @param {object} config an object containing configurable attributes
2503  *                Valid properties for DD:
2504  *                    scroll
2505  */
2506 Roo.dd.DD = function(id, sGroup, config) {
2507     if (id) {
2508         this.init(id, sGroup, config);
2509     }
2510 };
2511
2512 Roo.extend(Roo.dd.DD, Roo.dd.DragDrop, {
2513
2514     /**
2515      * When set to true, the utility automatically tries to scroll the browser
2516      * window wehn a drag and drop element is dragged near the viewport boundary.
2517      * Defaults to true.
2518      * @property scroll
2519      * @type boolean
2520      */
2521     scroll: true,
2522
2523     /**
2524      * Sets the pointer offset to the distance between the linked element's top
2525      * left corner and the location the element was clicked
2526      * @method autoOffset
2527      * @param {int} iPageX the X coordinate of the click
2528      * @param {int} iPageY the Y coordinate of the click
2529      */
2530     autoOffset: function(iPageX, iPageY) {
2531         var x = iPageX - this.startPageX;
2532         var y = iPageY - this.startPageY;
2533         this.setDelta(x, y);
2534     },
2535
2536     /**
2537      * Sets the pointer offset.  You can call this directly to force the
2538      * offset to be in a particular location (e.g., pass in 0,0 to set it
2539      * to the center of the object)
2540      * @method setDelta
2541      * @param {int} iDeltaX the distance from the left
2542      * @param {int} iDeltaY the distance from the top
2543      */
2544     setDelta: function(iDeltaX, iDeltaY) {
2545         this.deltaX = iDeltaX;
2546         this.deltaY = iDeltaY;
2547     },
2548
2549     /**
2550      * Sets the drag element to the location of the mousedown or click event,
2551      * maintaining the cursor location relative to the location on the element
2552      * that was clicked.  Override this if you want to place the element in a
2553      * location other than where the cursor is.
2554      * @method setDragElPos
2555      * @param {int} iPageX the X coordinate of the mousedown or drag event
2556      * @param {int} iPageY the Y coordinate of the mousedown or drag event
2557      */
2558     setDragElPos: function(iPageX, iPageY) {
2559         // the first time we do this, we are going to check to make sure
2560         // the element has css positioning
2561
2562         var el = this.getDragEl();
2563         this.alignElWithMouse(el, iPageX, iPageY);
2564     },
2565
2566     /**
2567      * Sets the element to the location of the mousedown or click event,
2568      * maintaining the cursor location relative to the location on the element
2569      * that was clicked.  Override this if you want to place the element in a
2570      * location other than where the cursor is.
2571      * @method alignElWithMouse
2572      * @param {HTMLElement} el the element to move
2573      * @param {int} iPageX the X coordinate of the mousedown or drag event
2574      * @param {int} iPageY the Y coordinate of the mousedown or drag event
2575      */
2576     alignElWithMouse: function(el, iPageX, iPageY) {
2577         var oCoord = this.getTargetCoord(iPageX, iPageY);
2578         var fly = el.dom ? el : Roo.fly(el);
2579         if (!this.deltaSetXY) {
2580             var aCoord = [oCoord.x, oCoord.y];
2581             fly.setXY(aCoord);
2582             var newLeft = fly.getLeft(true);
2583             var newTop  = fly.getTop(true);
2584             this.deltaSetXY = [ newLeft - oCoord.x, newTop - oCoord.y ];
2585         } else {
2586             fly.setLeftTop(oCoord.x + this.deltaSetXY[0], oCoord.y + this.deltaSetXY[1]);
2587         }
2588
2589         this.cachePosition(oCoord.x, oCoord.y);
2590         this.autoScroll(oCoord.x, oCoord.y, el.offsetHeight, el.offsetWidth);
2591         return oCoord;
2592     },
2593
2594     /**
2595      * Saves the most recent position so that we can reset the constraints and
2596      * tick marks on-demand.  We need to know this so that we can calculate the
2597      * number of pixels the element is offset from its original position.
2598      * @method cachePosition
2599      * @param iPageX the current x position (optional, this just makes it so we
2600      * don't have to look it up again)
2601      * @param iPageY the current y position (optional, this just makes it so we
2602      * don't have to look it up again)
2603      */
2604     cachePosition: function(iPageX, iPageY) {
2605         if (iPageX) {
2606             this.lastPageX = iPageX;
2607             this.lastPageY = iPageY;
2608         } else {
2609             var aCoord = Roo.lib.Dom.getXY(this.getEl());
2610             this.lastPageX = aCoord[0];
2611             this.lastPageY = aCoord[1];
2612         }
2613     },
2614
2615     /**
2616      * Auto-scroll the window if the dragged object has been moved beyond the
2617      * visible window boundary.
2618      * @method autoScroll
2619      * @param {int} x the drag element's x position
2620      * @param {int} y the drag element's y position
2621      * @param {int} h the height of the drag element
2622      * @param {int} w the width of the drag element
2623      * @private
2624      */
2625     autoScroll: function(x, y, h, w) {
2626
2627         if (this.scroll) {
2628             // The client height
2629             var clientH = Roo.lib.Dom.getViewWidth();
2630
2631             // The client width
2632             var clientW = Roo.lib.Dom.getViewHeight();
2633
2634             // The amt scrolled down
2635             var st = this.DDM.getScrollTop();
2636
2637             // The amt scrolled right
2638             var sl = this.DDM.getScrollLeft();
2639
2640             // Location of the bottom of the element
2641             var bot = h + y;
2642
2643             // Location of the right of the element
2644             var right = w + x;
2645
2646             // The distance from the cursor to the bottom of the visible area,
2647             // adjusted so that we don't scroll if the cursor is beyond the
2648             // element drag constraints
2649             var toBot = (clientH + st - y - this.deltaY);
2650
2651             // The distance from the cursor to the right of the visible area
2652             var toRight = (clientW + sl - x - this.deltaX);
2653
2654
2655             // How close to the edge the cursor must be before we scroll
2656             // var thresh = (document.all) ? 100 : 40;
2657             var thresh = 40;
2658
2659             // How many pixels to scroll per autoscroll op.  This helps to reduce
2660             // clunky scrolling. IE is more sensitive about this ... it needs this
2661             // value to be higher.
2662             var scrAmt = (document.all) ? 80 : 30;
2663
2664             // Scroll down if we are near the bottom of the visible page and the
2665             // obj extends below the crease
2666             if ( bot > clientH && toBot < thresh ) {
2667                 window.scrollTo(sl, st + scrAmt);
2668             }
2669
2670             // Scroll up if the window is scrolled down and the top of the object
2671             // goes above the top border
2672             if ( y < st && st > 0 && y - st < thresh ) {
2673                 window.scrollTo(sl, st - scrAmt);
2674             }
2675
2676             // Scroll right if the obj is beyond the right border and the cursor is
2677             // near the border.
2678             if ( right > clientW && toRight < thresh ) {
2679                 window.scrollTo(sl + scrAmt, st);
2680             }
2681
2682             // Scroll left if the window has been scrolled to the right and the obj
2683             // extends past the left border
2684             if ( x < sl && sl > 0 && x - sl < thresh ) {
2685                 window.scrollTo(sl - scrAmt, st);
2686             }
2687         }
2688     },
2689
2690     /**
2691      * Finds the location the element should be placed if we want to move
2692      * it to where the mouse location less the click offset would place us.
2693      * @method getTargetCoord
2694      * @param {int} iPageX the X coordinate of the click
2695      * @param {int} iPageY the Y coordinate of the click
2696      * @return an object that contains the coordinates (Object.x and Object.y)
2697      * @private
2698      */
2699     getTargetCoord: function(iPageX, iPageY) {
2700
2701
2702         var x = iPageX - this.deltaX;
2703         var y = iPageY - this.deltaY;
2704
2705         if (this.constrainX) {
2706             if (x < this.minX) { x = this.minX; }
2707             if (x > this.maxX) { x = this.maxX; }
2708         }
2709
2710         if (this.constrainY) {
2711             if (y < this.minY) { y = this.minY; }
2712             if (y > this.maxY) { y = this.maxY; }
2713         }
2714
2715         x = this.getTick(x, this.xTicks);
2716         y = this.getTick(y, this.yTicks);
2717
2718
2719         return {x:x, y:y};
2720     },
2721
2722     /*
2723      * Sets up config options specific to this class. Overrides
2724      * Roo.dd.DragDrop, but all versions of this method through the
2725      * inheritance chain are called
2726      */
2727     applyConfig: function() {
2728         Roo.dd.DD.superclass.applyConfig.call(this);
2729         this.scroll = (this.config.scroll !== false);
2730     },
2731
2732     /*
2733      * Event that fires prior to the onMouseDown event.  Overrides
2734      * Roo.dd.DragDrop.
2735      */
2736     b4MouseDown: function(e) {
2737         // this.resetConstraints();
2738         this.autoOffset(e.getPageX(),
2739                             e.getPageY());
2740     },
2741
2742     /*
2743      * Event that fires prior to the onDrag event.  Overrides
2744      * Roo.dd.DragDrop.
2745      */
2746     b4Drag: function(e) {
2747         this.setDragElPos(e.getPageX(),
2748                             e.getPageY());
2749     },
2750
2751     toString: function() {
2752         return ("DD " + this.id);
2753     }
2754
2755     //////////////////////////////////////////////////////////////////////////
2756     // Debugging ygDragDrop events that can be overridden
2757     //////////////////////////////////////////////////////////////////////////
2758     /*
2759     startDrag: function(x, y) {
2760     },
2761
2762     onDrag: function(e) {
2763     },
2764
2765     onDragEnter: function(e, id) {
2766     },
2767
2768     onDragOver: function(e, id) {
2769     },
2770
2771     onDragOut: function(e, id) {
2772     },
2773
2774     onDragDrop: function(e, id) {
2775     },
2776
2777     endDrag: function(e) {
2778     }
2779
2780     */
2781
2782 });/*
2783  * Based on:
2784  * Ext JS Library 1.1.1
2785  * Copyright(c) 2006-2007, Ext JS, LLC.
2786  *
2787  * Originally Released Under LGPL - original licence link has changed is not relivant.
2788  *
2789  * Fork - LGPL
2790  * <script type="text/javascript">
2791  */
2792
2793 /**
2794  * @class Roo.dd.DDProxy
2795  * A DragDrop implementation that inserts an empty, bordered div into
2796  * the document that follows the cursor during drag operations.  At the time of
2797  * the click, the frame div is resized to the dimensions of the linked html
2798  * element, and moved to the exact location of the linked element.
2799  *
2800  * References to the "frame" element refer to the single proxy element that
2801  * was created to be dragged in place of all DDProxy elements on the
2802  * page.
2803  *
2804  * @extends Roo.dd.DD
2805  * @constructor
2806  * @param {String} id the id of the linked html element
2807  * @param {String} sGroup the group of related DragDrop objects
2808  * @param {object} config an object containing configurable attributes
2809  *                Valid properties for DDProxy in addition to those in DragDrop:
2810  *                   resizeFrame, centerFrame, dragElId
2811  */
2812 Roo.dd.DDProxy = function(id, sGroup, config) {
2813     if (id) {
2814         this.init(id, sGroup, config);
2815         this.initFrame();
2816     }
2817 };
2818
2819 /**
2820  * The default drag frame div id
2821  * @property Roo.dd.DDProxy.dragElId
2822  * @type String
2823  * @static
2824  */
2825 Roo.dd.DDProxy.dragElId = "ygddfdiv";
2826
2827 Roo.extend(Roo.dd.DDProxy, Roo.dd.DD, {
2828
2829     /**
2830      * By default we resize the drag frame to be the same size as the element
2831      * we want to drag (this is to get the frame effect).  We can turn it off
2832      * if we want a different behavior.
2833      * @property resizeFrame
2834      * @type boolean
2835      */
2836     resizeFrame: true,
2837
2838     /**
2839      * By default the frame is positioned exactly where the drag element is, so
2840      * we use the cursor offset provided by Roo.dd.DD.  Another option that works only if
2841      * you do not have constraints on the obj is to have the drag frame centered
2842      * around the cursor.  Set centerFrame to true for this effect.
2843      * @property centerFrame
2844      * @type boolean
2845      */
2846     centerFrame: false,
2847
2848     /**
2849      * Creates the proxy element if it does not yet exist
2850      * @method createFrame
2851      */
2852     createFrame: function() {
2853         var self = this;
2854         var body = document.body;
2855
2856         if (!body || !body.firstChild) {
2857             setTimeout( function() { self.createFrame(); }, 50 );
2858             return;
2859         }
2860
2861         var div = this.getDragEl();
2862
2863         if (!div) {
2864             div    = document.createElement("div");
2865             div.id = this.dragElId;
2866             var s  = div.style;
2867
2868             s.position   = "absolute";
2869             s.visibility = "hidden";
2870             s.cursor     = "move";
2871             s.border     = "2px solid #aaa";
2872             s.zIndex     = 999;
2873
2874             // appendChild can blow up IE if invoked prior to the window load event
2875             // while rendering a table.  It is possible there are other scenarios
2876             // that would cause this to happen as well.
2877             body.insertBefore(div, body.firstChild);
2878         }
2879     },
2880
2881     /**
2882      * Initialization for the drag frame element.  Must be called in the
2883      * constructor of all subclasses
2884      * @method initFrame
2885      */
2886     initFrame: function() {
2887         this.createFrame();
2888     },
2889
2890     applyConfig: function() {
2891         Roo.dd.DDProxy.superclass.applyConfig.call(this);
2892
2893         this.resizeFrame = (this.config.resizeFrame !== false);
2894         this.centerFrame = (this.config.centerFrame);
2895         this.setDragElId(this.config.dragElId || Roo.dd.DDProxy.dragElId);
2896     },
2897
2898     /**
2899      * Resizes the drag frame to the dimensions of the clicked object, positions
2900      * it over the object, and finally displays it
2901      * @method showFrame
2902      * @param {int} iPageX X click position
2903      * @param {int} iPageY Y click position
2904      * @private
2905      */
2906     showFrame: function(iPageX, iPageY) {
2907         var el = this.getEl();
2908         var dragEl = this.getDragEl();
2909         var s = dragEl.style;
2910
2911         this._resizeProxy();
2912
2913         if (this.centerFrame) {
2914             this.setDelta( Math.round(parseInt(s.width,  10)/2),
2915                            Math.round(parseInt(s.height, 10)/2) );
2916         }
2917
2918         this.setDragElPos(iPageX, iPageY);
2919
2920         Roo.fly(dragEl).show();
2921     },
2922
2923     /**
2924      * The proxy is automatically resized to the dimensions of the linked
2925      * element when a drag is initiated, unless resizeFrame is set to false
2926      * @method _resizeProxy
2927      * @private
2928      */
2929     _resizeProxy: function() {
2930         if (this.resizeFrame) {
2931             var el = this.getEl();
2932             Roo.fly(this.getDragEl()).setSize(el.offsetWidth, el.offsetHeight);
2933         }
2934     },
2935
2936     // overrides Roo.dd.DragDrop
2937     b4MouseDown: function(e) {
2938         var x = e.getPageX();
2939         var y = e.getPageY();
2940         this.autoOffset(x, y);
2941         this.setDragElPos(x, y);
2942     },
2943
2944     // overrides Roo.dd.DragDrop
2945     b4StartDrag: function(x, y) {
2946         // show the drag frame
2947         this.showFrame(x, y);
2948     },
2949
2950     // overrides Roo.dd.DragDrop
2951     b4EndDrag: function(e) {
2952         Roo.fly(this.getDragEl()).hide();
2953     },
2954
2955     // overrides Roo.dd.DragDrop
2956     // By default we try to move the element to the last location of the frame.
2957     // This is so that the default behavior mirrors that of Roo.dd.DD.
2958     endDrag: function(e) {
2959
2960         var lel = this.getEl();
2961         var del = this.getDragEl();
2962
2963         // Show the drag frame briefly so we can get its position
2964         del.style.visibility = "";
2965
2966         this.beforeMove();
2967         // Hide the linked element before the move to get around a Safari
2968         // rendering bug.
2969         lel.style.visibility = "hidden";
2970         Roo.dd.DDM.moveToEl(lel, del);
2971         del.style.visibility = "hidden";
2972         lel.style.visibility = "";
2973
2974         this.afterDrag();
2975     },
2976
2977     beforeMove : function(){
2978
2979     },
2980
2981     afterDrag : function(){
2982
2983     },
2984
2985     toString: function() {
2986         return ("DDProxy " + this.id);
2987     }
2988
2989 });
2990 /*
2991  * Based on:
2992  * Ext JS Library 1.1.1
2993  * Copyright(c) 2006-2007, Ext JS, LLC.
2994  *
2995  * Originally Released Under LGPL - original licence link has changed is not relivant.
2996  *
2997  * Fork - LGPL
2998  * <script type="text/javascript">
2999  */
3000
3001  /**
3002  * @class Roo.dd.DDTarget
3003  * A DragDrop implementation that does not move, but can be a drop
3004  * target.  You would get the same result by simply omitting implementation
3005  * for the event callbacks, but this way we reduce the processing cost of the
3006  * event listener and the callbacks.
3007  * @extends Roo.dd.DragDrop
3008  * @constructor
3009  * @param {String} id the id of the element that is a drop target
3010  * @param {String} sGroup the group of related DragDrop objects
3011  * @param {object} config an object containing configurable attributes
3012  *                 Valid properties for DDTarget in addition to those in
3013  *                 DragDrop:
3014  *                    none
3015  */
3016 Roo.dd.DDTarget = function(id, sGroup, config) {
3017     if (id) {
3018         this.initTarget(id, sGroup, config);
3019     }
3020     if (config.listeners || config.events) { 
3021        Roo.dd.DragDrop.superclass.constructor.call(this,  { 
3022             listeners : config.listeners || {}, 
3023             events : config.events || {} 
3024         });    
3025     }
3026 };
3027
3028 // Roo.dd.DDTarget.prototype = new Roo.dd.DragDrop();
3029 Roo.extend(Roo.dd.DDTarget, Roo.dd.DragDrop, {
3030     toString: function() {
3031         return ("DDTarget " + this.id);
3032     }
3033 });
3034 /*
3035  * Based on:
3036  * Ext JS Library 1.1.1
3037  * Copyright(c) 2006-2007, Ext JS, LLC.
3038  *
3039  * Originally Released Under LGPL - original licence link has changed is not relivant.
3040  *
3041  * Fork - LGPL
3042  * <script type="text/javascript">
3043  */
3044  
3045
3046 /**
3047  * @class Roo.dd.ScrollManager
3048  * Provides automatic scrolling of overflow regions in the page during drag operations.<br><br>
3049  * <b>Note: This class uses "Point Mode" and is untested in "Intersect Mode".</b>
3050  * @singleton
3051  */
3052 Roo.dd.ScrollManager = function(){
3053     var ddm = Roo.dd.DragDropMgr;
3054     var els = {};
3055     var dragEl = null;
3056     var proc = {};
3057     
3058     
3059     
3060     var onStop = function(e){
3061         dragEl = null;
3062         clearProc();
3063     };
3064     
3065     var triggerRefresh = function(){
3066         if(ddm.dragCurrent){
3067              ddm.refreshCache(ddm.dragCurrent.groups);
3068         }
3069     };
3070     
3071     var doScroll = function(){
3072         if(ddm.dragCurrent){
3073             var dds = Roo.dd.ScrollManager;
3074             if(!dds.animate){
3075                 if(proc.el.scroll(proc.dir, dds.increment)){
3076                     triggerRefresh();
3077                 }
3078             }else{
3079                 proc.el.scroll(proc.dir, dds.increment, true, dds.animDuration, triggerRefresh);
3080             }
3081         }
3082     };
3083     
3084     var clearProc = function(){
3085         if(proc.id){
3086             clearInterval(proc.id);
3087         }
3088         proc.id = 0;
3089         proc.el = null;
3090         proc.dir = "";
3091     };
3092     
3093     var startProc = function(el, dir){
3094          Roo.log('scroll startproc');
3095         clearProc();
3096         proc.el = el;
3097         proc.dir = dir;
3098         proc.id = setInterval(doScroll, Roo.dd.ScrollManager.frequency);
3099     };
3100     
3101     var onFire = function(e, isDrop){
3102        
3103         if(isDrop || !ddm.dragCurrent){ return; }
3104         var dds = Roo.dd.ScrollManager;
3105         if(!dragEl || dragEl != ddm.dragCurrent){
3106             dragEl = ddm.dragCurrent;
3107             // refresh regions on drag start
3108             dds.refreshCache();
3109         }
3110         
3111         var xy = Roo.lib.Event.getXY(e);
3112         var pt = new Roo.lib.Point(xy[0], xy[1]);
3113         for(var id in els){
3114             var el = els[id], r = el._region;
3115             if(r && r.contains(pt) && el.isScrollable()){
3116                 if(r.bottom - pt.y <= dds.thresh){
3117                     if(proc.el != el){
3118                         startProc(el, "down");
3119                     }
3120                     return;
3121                 }else if(r.right - pt.x <= dds.thresh){
3122                     if(proc.el != el){
3123                         startProc(el, "left");
3124                     }
3125                     return;
3126                 }else if(pt.y - r.top <= dds.thresh){
3127                     if(proc.el != el){
3128                         startProc(el, "up");
3129                     }
3130                     return;
3131                 }else if(pt.x - r.left <= dds.thresh){
3132                     if(proc.el != el){
3133                         startProc(el, "right");
3134                     }
3135                     return;
3136                 }
3137             }
3138         }
3139         clearProc();
3140     };
3141     
3142     ddm.fireEvents = ddm.fireEvents.createSequence(onFire, ddm);
3143     ddm.stopDrag = ddm.stopDrag.createSequence(onStop, ddm);
3144     
3145     return {
3146         /**
3147          * Registers new overflow element(s) to auto scroll
3148          * @param {String/HTMLElement/Element/Array} el The id of or the element to be scrolled or an array of either
3149          */
3150         register : function(el){
3151             if(el instanceof Array){
3152                 for(var i = 0, len = el.length; i < len; i++) {
3153                         this.register(el[i]);
3154                 }
3155             }else{
3156                 el = Roo.get(el);
3157                 els[el.id] = el;
3158             }
3159             Roo.dd.ScrollManager.els = els;
3160         },
3161         
3162         /**
3163          * Unregisters overflow element(s) so they are no longer scrolled
3164          * @param {String/HTMLElement/Element/Array} el The id of or the element to be removed or an array of either
3165          */
3166         unregister : function(el){
3167             if(el instanceof Array){
3168                 for(var i = 0, len = el.length; i < len; i++) {
3169                         this.unregister(el[i]);
3170                 }
3171             }else{
3172                 el = Roo.get(el);
3173                 delete els[el.id];
3174             }
3175         },
3176         
3177         /**
3178          * The number of pixels from the edge of a container the pointer needs to be to 
3179          * trigger scrolling (defaults to 25)
3180          * @type Number
3181          */
3182         thresh : 25,
3183         
3184         /**
3185          * The number of pixels to scroll in each scroll increment (defaults to 50)
3186          * @type Number
3187          */
3188         increment : 100,
3189         
3190         /**
3191          * The frequency of scrolls in milliseconds (defaults to 500)
3192          * @type Number
3193          */
3194         frequency : 500,
3195         
3196         /**
3197          * True to animate the scroll (defaults to true)
3198          * @type Boolean
3199          */
3200         animate: true,
3201         
3202         /**
3203          * The animation duration in seconds - 
3204          * MUST BE less than Roo.dd.ScrollManager.frequency! (defaults to .4)
3205          * @type Number
3206          */
3207         animDuration: .4,
3208         
3209         /**
3210          * Manually trigger a cache refresh.
3211          */
3212         refreshCache : function(){
3213             for(var id in els){
3214                 if(typeof els[id] == 'object'){ // for people extending the object prototype
3215                     els[id]._region = els[id].getRegion();
3216                 }
3217             }
3218         }
3219     };
3220 }();/*
3221  * Based on:
3222  * Ext JS Library 1.1.1
3223  * Copyright(c) 2006-2007, Ext JS, LLC.
3224  *
3225  * Originally Released Under LGPL - original licence link has changed is not relivant.
3226  *
3227  * Fork - LGPL
3228  * <script type="text/javascript">
3229  */
3230  
3231
3232 /**
3233  * @class Roo.dd.Registry
3234  * Provides easy access to all drag drop components that are registered on a page.  Items can be retrieved either
3235  * directly by DOM node id, or by passing in the drag drop event that occurred and looking up the event target.
3236  * @singleton
3237  */
3238 Roo.dd.Registry = function(){
3239     var elements = {}; 
3240     var handles = {}; 
3241     var autoIdSeed = 0;
3242
3243     var getId = function(el, autogen){
3244         if(typeof el == "string"){
3245             return el;
3246         }
3247         var id = el.id;
3248         if(!id && autogen !== false){
3249             id = "roodd-" + (++autoIdSeed);
3250             el.id = id;
3251         }
3252         return id;
3253     };
3254     
3255     return {
3256     /**
3257      * Register a drag drop element
3258      * @param {String|HTMLElement} element The id or DOM node to register
3259      * @param {Object} data (optional) A custom data object that will be passed between the elements that are involved
3260      * in drag drop operations.  You can populate this object with any arbitrary properties that your own code
3261      * knows how to interpret, plus there are some specific properties known to the Registry that should be
3262      * populated in the data object (if applicable):
3263      * <pre>
3264 Value      Description<br />
3265 ---------  ------------------------------------------<br />
3266 handles    Array of DOM nodes that trigger dragging<br />
3267            for the element being registered<br />
3268 isHandle   True if the element passed in triggers<br />
3269            dragging itself, else false
3270 </pre>
3271      */
3272         register : function(el, data){
3273             data = data || {};
3274             if(typeof el == "string"){
3275                 el = document.getElementById(el);
3276             }
3277             data.ddel = el;
3278             elements[getId(el)] = data;
3279             if(data.isHandle !== false){
3280                 handles[data.ddel.id] = data;
3281             }
3282             if(data.handles){
3283                 var hs = data.handles;
3284                 for(var i = 0, len = hs.length; i < len; i++){
3285                         handles[getId(hs[i])] = data;
3286                 }
3287             }
3288         },
3289
3290     /**
3291      * Unregister a drag drop element
3292      * @param {String|HTMLElement}  element The id or DOM node to unregister
3293      */
3294         unregister : function(el){
3295             var id = getId(el, false);
3296             var data = elements[id];
3297             if(data){
3298                 delete elements[id];
3299                 if(data.handles){
3300                     var hs = data.handles;
3301                     for(var i = 0, len = hs.length; i < len; i++){
3302                         delete handles[getId(hs[i], false)];
3303                     }
3304                 }
3305             }
3306         },
3307
3308     /**
3309      * Returns the handle registered for a DOM Node by id
3310      * @param {String|HTMLElement} id The DOM node or id to look up
3311      * @return {Object} handle The custom handle data
3312      */
3313         getHandle : function(id){
3314             if(typeof id != "string"){ // must be element?
3315                 id = id.id;
3316             }
3317             return handles[id];
3318         },
3319
3320     /**
3321      * Returns the handle that is registered for the DOM node that is the target of the event
3322      * @param {Event} e The event
3323      * @return {Object} handle The custom handle data
3324      */
3325         getHandleFromEvent : function(e){
3326             var t = Roo.lib.Event.getTarget(e);
3327             return t ? handles[t.id] : null;
3328         },
3329
3330     /**
3331      * Returns a custom data object that is registered for a DOM node by id
3332      * @param {String|HTMLElement} id The DOM node or id to look up
3333      * @return {Object} data The custom data
3334      */
3335         getTarget : function(id){
3336             if(typeof id != "string"){ // must be element?
3337                 id = id.id;
3338             }
3339             return elements[id];
3340         },
3341
3342     /**
3343      * Returns a custom data object that is registered for the DOM node that is the target of the event
3344      * @param {Event} e The event
3345      * @return {Object} data The custom data
3346      */
3347         getTargetFromEvent : function(e){
3348             var t = Roo.lib.Event.getTarget(e);
3349             return t ? elements[t.id] || handles[t.id] : null;
3350         }
3351     };
3352 }();/*
3353  * Based on:
3354  * Ext JS Library 1.1.1
3355  * Copyright(c) 2006-2007, Ext JS, LLC.
3356  *
3357  * Originally Released Under LGPL - original licence link has changed is not relivant.
3358  *
3359  * Fork - LGPL
3360  * <script type="text/javascript">
3361  */
3362  
3363
3364 /**
3365  * @class Roo.dd.StatusProxy
3366  * A specialized drag proxy that supports a drop status icon, {@link Roo.Layer} styles and auto-repair.  This is the
3367  * default drag proxy used by all Roo.dd components.
3368  * @constructor
3369  * @param {Object} config
3370  */
3371 Roo.dd.StatusProxy = function(config){
3372     Roo.apply(this, config);
3373     this.id = this.id || Roo.id();
3374     this.el = new Roo.Layer({
3375         dh: {
3376             id: this.id, tag: "div", cls: "x-dd-drag-proxy "+this.dropNotAllowed, children: [
3377                 {tag: "div", cls: "x-dd-drop-icon"},
3378                 {tag: "div", cls: "x-dd-drag-ghost"}
3379             ]
3380         }, 
3381         shadow: !config || config.shadow !== false
3382     });
3383     this.ghost = Roo.get(this.el.dom.childNodes[1]);
3384     this.dropStatus = this.dropNotAllowed;
3385 };
3386
3387 Roo.dd.StatusProxy.prototype = {
3388     /**
3389      * @cfg {String} dropAllowed
3390      * The CSS class to apply to the status element when drop is allowed (defaults to "x-dd-drop-ok").
3391      */
3392     dropAllowed : "x-dd-drop-ok",
3393     /**
3394      * @cfg {String} dropNotAllowed
3395      * The CSS class to apply to the status element when drop is not allowed (defaults to "x-dd-drop-nodrop").
3396      */
3397     dropNotAllowed : "x-dd-drop-nodrop",
3398
3399     /**
3400      * Updates the proxy's visual element to indicate the status of whether or not drop is allowed
3401      * over the current target element.
3402      * @param {String} cssClass The css class for the new drop status indicator image
3403      */
3404     setStatus : function(cssClass){
3405         cssClass = cssClass || this.dropNotAllowed;
3406         if(this.dropStatus != cssClass){
3407             this.el.replaceClass(this.dropStatus, cssClass);
3408             this.dropStatus = cssClass;
3409         }
3410     },
3411
3412     /**
3413      * Resets the status indicator to the default dropNotAllowed value
3414      * @param {Boolean} clearGhost True to also remove all content from the ghost, false to preserve it
3415      */
3416     reset : function(clearGhost){
3417         this.el.dom.className = "x-dd-drag-proxy " + this.dropNotAllowed;
3418         this.dropStatus = this.dropNotAllowed;
3419         if(clearGhost){
3420             this.ghost.update("");
3421         }
3422     },
3423
3424     /**
3425      * Updates the contents of the ghost element
3426      * @param {String} html The html that will replace the current innerHTML of the ghost element
3427      */
3428     update : function(html){
3429         if(typeof html == "string"){
3430             this.ghost.update(html);
3431         }else{
3432             this.ghost.update("");
3433             html.style.margin = "0";
3434             this.ghost.dom.appendChild(html);
3435         }
3436         // ensure float = none set?? cant remember why though.
3437         var el = this.ghost.dom.firstChild;
3438                 if(el){
3439                         Roo.fly(el).setStyle('float', 'none');
3440                 }
3441     },
3442     
3443     /**
3444      * Returns the underlying proxy {@link Roo.Layer}
3445      * @return {Roo.Layer} el
3446     */
3447     getEl : function(){
3448         return this.el;
3449     },
3450
3451     /**
3452      * Returns the ghost element
3453      * @return {Roo.Element} el
3454      */
3455     getGhost : function(){
3456         return this.ghost;
3457     },
3458
3459     /**
3460      * Hides the proxy
3461      * @param {Boolean} clear True to reset the status and clear the ghost contents, false to preserve them
3462      */
3463     hide : function(clear){
3464         this.el.hide();
3465         if(clear){
3466             this.reset(true);
3467         }
3468     },
3469
3470     /**
3471      * Stops the repair animation if it's currently running
3472      */
3473     stop : function(){
3474         if(this.anim && this.anim.isAnimated && this.anim.isAnimated()){
3475             this.anim.stop();
3476         }
3477     },
3478
3479     /**
3480      * Displays this proxy
3481      */
3482     show : function(){
3483         this.el.show();
3484     },
3485
3486     /**
3487      * Force the Layer to sync its shadow and shim positions to the element
3488      */
3489     sync : function(){
3490         this.el.sync();
3491     },
3492
3493     /**
3494      * Causes the proxy to return to its position of origin via an animation.  Should be called after an
3495      * invalid drop operation by the item being dragged.
3496      * @param {Array} xy The XY position of the element ([x, y])
3497      * @param {Function} callback The function to call after the repair is complete
3498      * @param {Object} scope The scope in which to execute the callback
3499      */
3500     repair : function(xy, callback, scope){
3501         this.callback = callback;
3502         this.scope = scope;
3503         if(xy && this.animRepair !== false){
3504             this.el.addClass("x-dd-drag-repair");
3505             this.el.hideUnders(true);
3506             this.anim = this.el.shift({
3507                 duration: this.repairDuration || .5,
3508                 easing: 'easeOut',
3509                 xy: xy,
3510                 stopFx: true,
3511                 callback: this.afterRepair,
3512                 scope: this
3513             });
3514         }else{
3515             this.afterRepair();
3516         }
3517     },
3518
3519     // private
3520     afterRepair : function(){
3521         this.hide(true);
3522         if(typeof this.callback == "function"){
3523             this.callback.call(this.scope || this);
3524         }
3525         this.callback = null;
3526         this.scope = null;
3527     }
3528 };/*
3529  * Based on:
3530  * Ext JS Library 1.1.1
3531  * Copyright(c) 2006-2007, Ext JS, LLC.
3532  *
3533  * Originally Released Under LGPL - original licence link has changed is not relivant.
3534  *
3535  * Fork - LGPL
3536  * <script type="text/javascript">
3537  */
3538
3539 /**
3540  * @class Roo.dd.DragSource
3541  * @extends Roo.dd.DDProxy
3542  * A simple class that provides the basic implementation needed to make any element draggable.
3543  * @constructor
3544  * @param {String/HTMLElement/Element} el The container element
3545  * @param {Object} config
3546  */
3547 Roo.dd.DragSource = function(el, config){
3548     this.el = Roo.get(el);
3549     this.dragData = {};
3550     
3551     Roo.apply(this, config);
3552     
3553     if(!this.proxy){
3554         this.proxy = new Roo.dd.StatusProxy();
3555     }
3556
3557     Roo.dd.DragSource.superclass.constructor.call(this, this.el.dom, this.ddGroup || this.group,
3558           {dragElId : this.proxy.id, resizeFrame: false, isTarget: false, scroll: this.scroll === true});
3559     
3560     this.dragging = false;
3561 };
3562
3563 Roo.extend(Roo.dd.DragSource, Roo.dd.DDProxy, {
3564     /**
3565      * @cfg {String} dropAllowed
3566      * The CSS class returned to the drag source when drop is allowed (defaults to "x-dd-drop-ok").
3567      */
3568     dropAllowed : "x-dd-drop-ok",
3569     /**
3570      * @cfg {String} dropNotAllowed
3571      * The CSS class returned to the drag source when drop is not allowed (defaults to "x-dd-drop-nodrop").
3572      */
3573     dropNotAllowed : "x-dd-drop-nodrop",
3574
3575     /**
3576      * Returns the data object associated with this drag source
3577      * @return {Object} data An object containing arbitrary data
3578      */
3579     getDragData : function(e){
3580         return this.dragData;
3581     },
3582
3583     // private
3584     onDragEnter : function(e, id){
3585         var target = Roo.dd.DragDropMgr.getDDById(id);
3586         this.cachedTarget = target;
3587         if(this.beforeDragEnter(target, e, id) !== false){
3588             if(target.isNotifyTarget){
3589                 var status = target.notifyEnter(this, e, this.dragData);
3590                 this.proxy.setStatus(status);
3591             }else{
3592                 this.proxy.setStatus(this.dropAllowed);
3593             }
3594             
3595             if(this.afterDragEnter){
3596                 /**
3597                  * An empty function by default, but provided so that you can perform a custom action
3598                  * when the dragged item enters the drop target by providing an implementation.
3599                  * @param {Roo.dd.DragDrop} target The drop target
3600                  * @param {Event} e The event object
3601                  * @param {String} id The id of the dragged element
3602                  * @method afterDragEnter
3603                  */
3604                 this.afterDragEnter(target, e, id);
3605             }
3606         }
3607     },
3608
3609     /**
3610      * An empty function by default, but provided so that you can perform a custom action
3611      * before the dragged item enters the drop target and optionally cancel the onDragEnter.
3612      * @param {Roo.dd.DragDrop} target The drop target
3613      * @param {Event} e The event object
3614      * @param {String} id The id of the dragged element
3615      * @return {Boolean} isValid True if the drag event is valid, else false to cancel
3616      */
3617     beforeDragEnter : function(target, e, id){
3618         return true;
3619     },
3620
3621     // private
3622     alignElWithMouse: function() {
3623         Roo.dd.DragSource.superclass.alignElWithMouse.apply(this, arguments);
3624         this.proxy.sync();
3625     },
3626
3627     // private
3628     onDragOver : function(e, id){
3629         var target = this.cachedTarget || Roo.dd.DragDropMgr.getDDById(id);
3630         if(this.beforeDragOver(target, e, id) !== false){
3631             if(target.isNotifyTarget){
3632                 var status = target.notifyOver(this, e, this.dragData);
3633                 this.proxy.setStatus(status);
3634             }
3635
3636             if(this.afterDragOver){
3637                 /**
3638                  * An empty function by default, but provided so that you can perform a custom action
3639                  * while the dragged item is over the drop target by providing an implementation.
3640                  * @param {Roo.dd.DragDrop} target The drop target
3641                  * @param {Event} e The event object
3642                  * @param {String} id The id of the dragged element
3643                  * @method afterDragOver
3644                  */
3645                 this.afterDragOver(target, e, id);
3646             }
3647         }
3648     },
3649
3650     /**
3651      * An empty function by default, but provided so that you can perform a custom action
3652      * while the dragged item is over the drop target and optionally cancel the onDragOver.
3653      * @param {Roo.dd.DragDrop} target The drop target
3654      * @param {Event} e The event object
3655      * @param {String} id The id of the dragged element
3656      * @return {Boolean} isValid True if the drag event is valid, else false to cancel
3657      */
3658     beforeDragOver : function(target, e, id){
3659         return true;
3660     },
3661
3662     // private
3663     onDragOut : function(e, id){
3664         var target = this.cachedTarget || Roo.dd.DragDropMgr.getDDById(id);
3665         if(this.beforeDragOut(target, e, id) !== false){
3666             if(target.isNotifyTarget){
3667                 target.notifyOut(this, e, this.dragData);
3668             }
3669             this.proxy.reset();
3670             if(this.afterDragOut){
3671                 /**
3672                  * An empty function by default, but provided so that you can perform a custom action
3673                  * after the dragged item is dragged out of the target without dropping.
3674                  * @param {Roo.dd.DragDrop} target The drop target
3675                  * @param {Event} e The event object
3676                  * @param {String} id The id of the dragged element
3677                  * @method afterDragOut
3678                  */
3679                 this.afterDragOut(target, e, id);
3680             }
3681         }
3682         this.cachedTarget = null;
3683     },
3684
3685     /**
3686      * An empty function by default, but provided so that you can perform a custom action before the dragged
3687      * item is dragged out of the target without dropping, and optionally cancel the onDragOut.
3688      * @param {Roo.dd.DragDrop} target The drop target
3689      * @param {Event} e The event object
3690      * @param {String} id The id of the dragged element
3691      * @return {Boolean} isValid True if the drag event is valid, else false to cancel
3692      */
3693     beforeDragOut : function(target, e, id){
3694         return true;
3695     },
3696     
3697     // private
3698     onDragDrop : function(e, id){
3699         var target = this.cachedTarget || Roo.dd.DragDropMgr.getDDById(id);
3700         if(this.beforeDragDrop(target, e, id) !== false){
3701             if(target.isNotifyTarget){
3702                 if(target.notifyDrop(this, e, this.dragData)){ // valid drop?
3703                     this.onValidDrop(target, e, id);
3704                 }else{
3705                     this.onInvalidDrop(target, e, id);
3706                 }
3707             }else{
3708                 this.onValidDrop(target, e, id);
3709             }
3710             
3711             if(this.afterDragDrop){
3712                 /**
3713                  * An empty function by default, but provided so that you can perform a custom action
3714                  * after a valid drag drop has occurred by providing an implementation.
3715                  * @param {Roo.dd.DragDrop} target The drop target
3716                  * @param {Event} e The event object
3717                  * @param {String} id The id of the dropped element
3718                  * @method afterDragDrop
3719                  */
3720                 this.afterDragDrop(target, e, id);
3721             }
3722         }
3723         delete this.cachedTarget;
3724     },
3725
3726     /**
3727      * An empty function by default, but provided so that you can perform a custom action before the dragged
3728      * item is dropped onto the target and optionally cancel the onDragDrop.
3729      * @param {Roo.dd.DragDrop} target The drop target
3730      * @param {Event} e The event object
3731      * @param {String} id The id of the dragged element
3732      * @return {Boolean} isValid True if the drag drop event is valid, else false to cancel
3733      */
3734     beforeDragDrop : function(target, e, id){
3735         return true;
3736     },
3737
3738     // private
3739     onValidDrop : function(target, e, id){
3740         this.hideProxy();
3741         if(this.afterValidDrop){
3742             /**
3743              * An empty function by default, but provided so that you can perform a custom action
3744              * after a valid drop has occurred by providing an implementation.
3745              * @param {Object} target The target DD 
3746              * @param {Event} e The event object
3747              * @param {String} id The id of the dropped element
3748              * @method afterInvalidDrop
3749              */
3750             this.afterValidDrop(target, e, id);
3751         }
3752     },
3753
3754     // private
3755     getRepairXY : function(e, data){
3756         return this.el.getXY();  
3757     },
3758
3759     // private
3760     onInvalidDrop : function(target, e, id){
3761         this.beforeInvalidDrop(target, e, id);
3762         if(this.cachedTarget){
3763             if(this.cachedTarget.isNotifyTarget){
3764                 this.cachedTarget.notifyOut(this, e, this.dragData);
3765             }
3766             this.cacheTarget = null;
3767         }
3768         this.proxy.repair(this.getRepairXY(e, this.dragData), this.afterRepair, this);
3769
3770         if(this.afterInvalidDrop){
3771             /**
3772              * An empty function by default, but provided so that you can perform a custom action
3773              * after an invalid drop has occurred by providing an implementation.
3774              * @param {Event} e The event object
3775              * @param {String} id The id of the dropped element
3776              * @method afterInvalidDrop
3777              */
3778             this.afterInvalidDrop(e, id);
3779         }
3780     },
3781
3782     // private
3783     afterRepair : function(){
3784         if(Roo.enableFx){
3785             this.el.highlight(this.hlColor || "c3daf9");
3786         }
3787         this.dragging = false;
3788     },
3789
3790     /**
3791      * An empty function by default, but provided so that you can perform a custom action after an invalid
3792      * drop has occurred.
3793      * @param {Roo.dd.DragDrop} target The drop target
3794      * @param {Event} e The event object
3795      * @param {String} id The id of the dragged element
3796      * @return {Boolean} isValid True if the invalid drop should proceed, else false to cancel
3797      */
3798     beforeInvalidDrop : function(target, e, id){
3799         return true;
3800     },
3801
3802     // private
3803     handleMouseDown : function(e){
3804         if(this.dragging) {
3805             return;
3806         }
3807         var data = this.getDragData(e);
3808         if(data && this.onBeforeDrag(data, e) !== false){
3809             this.dragData = data;
3810             this.proxy.stop();
3811             Roo.dd.DragSource.superclass.handleMouseDown.apply(this, arguments);
3812         } 
3813     },
3814
3815     /**
3816      * An empty function by default, but provided so that you can perform a custom action before the initial
3817      * drag event begins and optionally cancel it.
3818      * @param {Object} data An object containing arbitrary data to be shared with drop targets
3819      * @param {Event} e The event object
3820      * @return {Boolean} isValid True if the drag event is valid, else false to cancel
3821      */
3822     onBeforeDrag : function(data, e){
3823         return true;
3824     },
3825
3826     /**
3827      * An empty function by default, but provided so that you can perform a custom action once the initial
3828      * drag event has begun.  The drag cannot be canceled from this function.
3829      * @param {Number} x The x position of the click on the dragged object
3830      * @param {Number} y The y position of the click on the dragged object
3831      */
3832     onStartDrag : Roo.emptyFn,
3833
3834     // private - YUI override
3835     startDrag : function(x, y){
3836         this.proxy.reset();
3837         this.dragging = true;
3838         this.proxy.update("");
3839         this.onInitDrag(x, y);
3840         this.proxy.show();
3841     },
3842
3843     // private
3844     onInitDrag : function(x, y){
3845         var clone = this.el.dom.cloneNode(true);
3846         clone.id = Roo.id(); // prevent duplicate ids
3847         this.proxy.update(clone);
3848         this.onStartDrag(x, y);
3849         return true;
3850     },
3851
3852     /**
3853      * Returns the drag source's underlying {@link Roo.dd.StatusProxy}
3854      * @return {Roo.dd.StatusProxy} proxy The StatusProxy
3855      */
3856     getProxy : function(){
3857         return this.proxy;  
3858     },
3859
3860     /**
3861      * Hides the drag source's {@link Roo.dd.StatusProxy}
3862      */
3863     hideProxy : function(){
3864         this.proxy.hide();  
3865         this.proxy.reset(true);
3866         this.dragging = false;
3867     },
3868
3869     // private
3870     triggerCacheRefresh : function(){
3871         Roo.dd.DDM.refreshCache(this.groups);
3872     },
3873
3874     // private - override to prevent hiding
3875     b4EndDrag: function(e) {
3876     },
3877
3878     // private - override to prevent moving
3879     endDrag : function(e){
3880         this.onEndDrag(this.dragData, e);
3881     },
3882
3883     // private
3884     onEndDrag : function(data, e){
3885     },
3886     
3887     // private - pin to cursor
3888     autoOffset : function(x, y) {
3889         this.setDelta(-12, -20);
3890     }    
3891 });/*
3892  * Based on:
3893  * Ext JS Library 1.1.1
3894  * Copyright(c) 2006-2007, Ext JS, LLC.
3895  *
3896  * Originally Released Under LGPL - original licence link has changed is not relivant.
3897  *
3898  * Fork - LGPL
3899  * <script type="text/javascript">
3900  */
3901
3902
3903 /**
3904  * @class Roo.dd.DropTarget
3905  * @extends Roo.dd.DDTarget
3906  * A simple class that provides the basic implementation needed to make any element a drop target that can have
3907  * draggable items dropped onto it.  The drop has no effect until an implementation of notifyDrop is provided.
3908  * @constructor
3909  * @param {String/HTMLElement/Element} el The container element
3910  * @param {Object} config
3911  */
3912 Roo.dd.DropTarget = function(el, config){
3913     this.el = Roo.get(el);
3914     
3915     var listeners = false; ;
3916     if (config && config.listeners) {
3917         listeners= config.listeners;
3918         delete config.listeners;
3919     }
3920     Roo.apply(this, config);
3921     
3922     if(this.containerScroll){
3923         Roo.dd.ScrollManager.register(this.el);
3924     }
3925     this.addEvents( {
3926          /**
3927          * @scope Roo.dd.DropTarget
3928          */
3929          
3930          /**
3931          * @event enter
3932          * The function a {@link Roo.dd.DragSource} calls once to notify this drop target that the source is now over the
3933          * target.  This default implementation adds the CSS class specified by overClass (if any) to the drop element
3934          * and returns the dropAllowed config value.  This method should be overridden if drop validation is required.
3935          * 
3936          * IMPORTANT : it should set this.overClass and this.dropAllowed
3937          * 
3938          * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop target
3939          * @param {Event} e The event
3940          * @param {Object} data An object containing arbitrary data supplied by the drag source
3941          */
3942         "enter" : true,
3943         
3944          /**
3945          * @event over
3946          * The function a {@link Roo.dd.DragSource} calls continuously while it is being dragged over the target.
3947          * This method will be called on every mouse movement while the drag source is over the drop target.
3948          * This default implementation simply returns the dropAllowed config value.
3949          * 
3950          * IMPORTANT : it should set this.dropAllowed
3951          * 
3952          * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop target
3953          * @param {Event} e The event
3954          * @param {Object} data An object containing arbitrary data supplied by the drag source
3955          
3956          */
3957         "over" : true,
3958         /**
3959          * @event out
3960          * The function a {@link Roo.dd.DragSource} calls once to notify this drop target that the source has been dragged
3961          * out of the target without dropping.  This default implementation simply removes the CSS class specified by
3962          * overClass (if any) from the drop element.
3963          * 
3964          * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop target
3965          * @param {Event} e The event
3966          * @param {Object} data An object containing arbitrary data supplied by the drag source
3967          */
3968          "out" : true,
3969          
3970         /**
3971          * @event drop
3972          * The function a {@link Roo.dd.DragSource} calls once to notify this drop target that the dragged item has
3973          * been dropped on it.  This method has no default implementation and returns false, so you must provide an
3974          * implementation that does something to process the drop event and returns true so that the drag source's
3975          * repair action does not run.
3976          * 
3977          * IMPORTANT : it should set this.success
3978          * 
3979          * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop target
3980          * @param {Event} e The event
3981          * @param {Object} data An object containing arbitrary data supplied by the drag source
3982         */
3983          "drop" : true
3984     });
3985             
3986      
3987     Roo.dd.DropTarget.superclass.constructor.call(  this, 
3988         this.el.dom, 
3989         this.ddGroup || this.group,
3990         {
3991             isTarget: true,
3992             listeners : listeners || {} 
3993            
3994         
3995         }
3996     );
3997
3998 };
3999
4000 Roo.extend(Roo.dd.DropTarget, Roo.dd.DDTarget, {
4001     /**
4002      * @cfg {String} overClass
4003      * The CSS class applied to the drop target element while the drag source is over it (defaults to "").
4004      */
4005      /**
4006      * @cfg {String} ddGroup
4007      * The drag drop group to handle drop events for
4008      */
4009      
4010     /**
4011      * @cfg {String} dropAllowed
4012      * The CSS class returned to the drag source when drop is allowed (defaults to "x-dd-drop-ok").
4013      */
4014     dropAllowed : "x-dd-drop-ok",
4015     /**
4016      * @cfg {String} dropNotAllowed
4017      * The CSS class returned to the drag source when drop is not allowed (defaults to "x-dd-drop-nodrop").
4018      */
4019     dropNotAllowed : "x-dd-drop-nodrop",
4020     /**
4021      * @cfg {boolean} success
4022      * set this after drop listener.. 
4023      */
4024     success : false,
4025     /**
4026      * @cfg {boolean|String} valid true/false or string (ok-add/ok-sub/ok/nodrop)
4027      * if the drop point is valid for over/enter..
4028      */
4029     valid : false,
4030     // private
4031     isTarget : true,
4032
4033     // private
4034     isNotifyTarget : true,
4035     
4036     /**
4037      * @hide
4038      */
4039     notifyEnter : function(dd, e, data)
4040     {
4041         this.valid = true;
4042         this.fireEvent('enter', dd, e, data);
4043         if(this.overClass){
4044             this.el.addClass(this.overClass);
4045         }
4046         return typeof(this.valid) == 'string' ? 'x-dd-drop-' + this.valid : (
4047             this.valid ? this.dropAllowed : this.dropNotAllowed
4048         );
4049     },
4050
4051     /**
4052      * @hide
4053      */
4054     notifyOver : function(dd, e, data)
4055     {
4056         this.valid = true;
4057         this.fireEvent('over', dd, e, data);
4058         return typeof(this.valid) == 'string' ? 'x-dd-drop-' + this.valid : (
4059             this.valid ? this.dropAllowed : this.dropNotAllowed
4060         );
4061     },
4062
4063     /**
4064      * @hide
4065      */
4066     notifyOut : function(dd, e, data)
4067     {
4068         this.fireEvent('out', dd, e, data);
4069         if(this.overClass){
4070             this.el.removeClass(this.overClass);
4071         }
4072     },
4073
4074     /**
4075      * @hide
4076      */
4077     notifyDrop : function(dd, e, data)
4078     {
4079         this.success = false;
4080         this.fireEvent('drop', dd, e, data);
4081         return this.success;
4082     }
4083 });/*
4084  * Based on:
4085  * Ext JS Library 1.1.1
4086  * Copyright(c) 2006-2007, Ext JS, LLC.
4087  *
4088  * Originally Released Under LGPL - original licence link has changed is not relivant.
4089  *
4090  * Fork - LGPL
4091  * <script type="text/javascript">
4092  */
4093
4094
4095 /**
4096  * @class Roo.dd.DragZone
4097  * @extends Roo.dd.DragSource
4098  * This class provides a container DD instance that proxies for multiple child node sources.<br />
4099  * By default, this class requires that draggable child nodes are registered with {@link Roo.dd.Registry}.
4100  * @constructor
4101  * @param {String/HTMLElement/Element} el The container element
4102  * @param {Object} config
4103  */
4104 Roo.dd.DragZone = function(el, config){
4105     Roo.dd.DragZone.superclass.constructor.call(this, el, config);
4106     if(this.containerScroll){
4107         Roo.dd.ScrollManager.register(this.el);
4108     }
4109 };
4110
4111 Roo.extend(Roo.dd.DragZone, Roo.dd.DragSource, {
4112     /**
4113      * @cfg {Boolean} containerScroll True to register this container with the Scrollmanager
4114      * for auto scrolling during drag operations.
4115      */
4116     /**
4117      * @cfg {String} hlColor The color to use when visually highlighting the drag source in the afterRepair
4118      * method after a failed drop (defaults to "c3daf9" - light blue)
4119      */
4120
4121     /**
4122      * Called when a mousedown occurs in this container. Looks in {@link Roo.dd.Registry}
4123      * for a valid target to drag based on the mouse down. Override this method
4124      * to provide your own lookup logic (e.g. finding a child by class name). Make sure your returned
4125      * object has a "ddel" attribute (with an HTML Element) for other functions to work.
4126      * @param {EventObject} e The mouse down event
4127      * @return {Object} The dragData
4128      */
4129     getDragData : function(e){
4130         return Roo.dd.Registry.getHandleFromEvent(e);
4131     },
4132     
4133     /**
4134      * Called once drag threshold has been reached to initialize the proxy element. By default, it clones the
4135      * this.dragData.ddel
4136      * @param {Number} x The x position of the click on the dragged object
4137      * @param {Number} y The y position of the click on the dragged object
4138      * @return {Boolean} true to continue the drag, false to cancel
4139      */
4140     onInitDrag : function(x, y){
4141         this.proxy.update(this.dragData.ddel.cloneNode(true));
4142         this.onStartDrag(x, y);
4143         return true;
4144     },
4145     
4146     /**
4147      * Called after a repair of an invalid drop. By default, highlights this.dragData.ddel 
4148      */
4149     afterRepair : function(){
4150         if(Roo.enableFx){
4151             Roo.Element.fly(this.dragData.ddel).highlight(this.hlColor || "c3daf9");
4152         }
4153         this.dragging = false;
4154     },
4155
4156     /**
4157      * Called before a repair of an invalid drop to get the XY to animate to. By default returns
4158      * the XY of this.dragData.ddel
4159      * @param {EventObject} e The mouse up event
4160      * @return {Array} The xy location (e.g. [100, 200])
4161      */
4162     getRepairXY : function(e){
4163         return Roo.Element.fly(this.dragData.ddel).getXY();  
4164     }
4165 });/*
4166  * Based on:
4167  * Ext JS Library 1.1.1
4168  * Copyright(c) 2006-2007, Ext JS, LLC.
4169  *
4170  * Originally Released Under LGPL - original licence link has changed is not relivant.
4171  *
4172  * Fork - LGPL
4173  * <script type="text/javascript">
4174  */
4175 /**
4176  * @class Roo.dd.DropZone
4177  * @extends Roo.dd.DropTarget
4178  * This class provides a container DD instance that proxies for multiple child node targets.<br />
4179  * By default, this class requires that child nodes accepting drop are registered with {@link Roo.dd.Registry}.
4180  * @constructor
4181  * @param {String/HTMLElement/Element} el The container element
4182  * @param {Object} config
4183  */
4184 Roo.dd.DropZone = function(el, config){
4185     Roo.dd.DropZone.superclass.constructor.call(this, el, config);
4186 };
4187
4188 Roo.extend(Roo.dd.DropZone, Roo.dd.DropTarget, {
4189     /**
4190      * Returns a custom data object associated with the DOM node that is the target of the event.  By default
4191      * this looks up the event target in the {@link Roo.dd.Registry}, although you can override this method to
4192      * provide your own custom lookup.
4193      * @param {Event} e The event
4194      * @return {Object} data The custom data
4195      */
4196     getTargetFromEvent : function(e){
4197         return Roo.dd.Registry.getTargetFromEvent(e);
4198     },
4199
4200     /**
4201      * Called internally when the DropZone determines that a {@link Roo.dd.DragSource} has entered a drop node
4202      * that it has registered.  This method has no default implementation and should be overridden to provide
4203      * node-specific processing if necessary.
4204      * @param {Object} nodeData The custom data associated with the drop node (this is the same value returned from 
4205      * {@link #getTargetFromEvent} for this node)
4206      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
4207      * @param {Event} e The event
4208      * @param {Object} data An object containing arbitrary data supplied by the drag source
4209      */
4210     onNodeEnter : function(n, dd, e, data){
4211         
4212     },
4213
4214     /**
4215      * Called internally while the DropZone determines that a {@link Roo.dd.DragSource} is over a drop node
4216      * that it has registered.  The default implementation returns this.dropNotAllowed, so it should be
4217      * overridden to provide the proper feedback.
4218      * @param {Object} nodeData The custom data associated with the drop node (this is the same value returned from
4219      * {@link #getTargetFromEvent} for this node)
4220      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
4221      * @param {Event} e The event
4222      * @param {Object} data An object containing arbitrary data supplied by the drag source
4223      * @return {String} status The CSS class that communicates the drop status back to the source so that the
4224      * underlying {@link Roo.dd.StatusProxy} can be updated
4225      */
4226     onNodeOver : function(n, dd, e, data){
4227         return this.dropAllowed;
4228     },
4229
4230     /**
4231      * Called internally when the DropZone determines that a {@link Roo.dd.DragSource} has been dragged out of
4232      * the drop node without dropping.  This method has no default implementation and should be overridden to provide
4233      * node-specific processing if necessary.
4234      * @param {Object} nodeData The custom data associated with the drop node (this is the same value returned from
4235      * {@link #getTargetFromEvent} for this node)
4236      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
4237      * @param {Event} e The event
4238      * @param {Object} data An object containing arbitrary data supplied by the drag source
4239      */
4240     onNodeOut : function(n, dd, e, data){
4241         
4242     },
4243
4244     /**
4245      * Called internally when the DropZone determines that a {@link Roo.dd.DragSource} has been dropped onto
4246      * the drop node.  The default implementation returns false, so it should be overridden to provide the
4247      * appropriate processing of the drop event and return true so that the drag source's repair action does not run.
4248      * @param {Object} nodeData The custom data associated with the drop node (this is the same value returned from
4249      * {@link #getTargetFromEvent} for this node)
4250      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
4251      * @param {Event} e The event
4252      * @param {Object} data An object containing arbitrary data supplied by the drag source
4253      * @return {Boolean} True if the drop was valid, else false
4254      */
4255     onNodeDrop : function(n, dd, e, data){
4256         return false;
4257     },
4258
4259     /**
4260      * Called internally while the DropZone determines that a {@link Roo.dd.DragSource} is being dragged over it,
4261      * but not over any of its registered drop nodes.  The default implementation returns this.dropNotAllowed, so
4262      * it should be overridden to provide the proper feedback if necessary.
4263      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
4264      * @param {Event} e The event
4265      * @param {Object} data An object containing arbitrary data supplied by the drag source
4266      * @return {String} status The CSS class that communicates the drop status back to the source so that the
4267      * underlying {@link Roo.dd.StatusProxy} can be updated
4268      */
4269     onContainerOver : function(dd, e, data){
4270         return this.dropNotAllowed;
4271     },
4272
4273     /**
4274      * Called internally when the DropZone determines that a {@link Roo.dd.DragSource} has been dropped on it,
4275      * but not on any of its registered drop nodes.  The default implementation returns false, so it should be
4276      * overridden to provide the appropriate processing of the drop event if you need the drop zone itself to
4277      * be able to accept drops.  It should return true when valid so that the drag source's repair action does not run.
4278      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
4279      * @param {Event} e The event
4280      * @param {Object} data An object containing arbitrary data supplied by the drag source
4281      * @return {Boolean} True if the drop was valid, else false
4282      */
4283     onContainerDrop : function(dd, e, data){
4284         return false;
4285     },
4286
4287     /**
4288      * The function a {@link Roo.dd.DragSource} calls once to notify this drop zone that the source is now over
4289      * the zone.  The default implementation returns this.dropNotAllowed and expects that only registered drop
4290      * nodes can process drag drop operations, so if you need the drop zone itself to be able to process drops
4291      * you should override this method and provide a custom implementation.
4292      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
4293      * @param {Event} e The event
4294      * @param {Object} data An object containing arbitrary data supplied by the drag source
4295      * @return {String} status The CSS class that communicates the drop status back to the source so that the
4296      * underlying {@link Roo.dd.StatusProxy} can be updated
4297      */
4298     notifyEnter : function(dd, e, data){
4299         return this.dropNotAllowed;
4300     },
4301
4302     /**
4303      * The function a {@link Roo.dd.DragSource} calls continuously while it is being dragged over the drop zone.
4304      * This method will be called on every mouse movement while the drag source is over the drop zone.
4305      * It will call {@link #onNodeOver} while the drag source is over a registered node, and will also automatically
4306      * delegate to the appropriate node-specific methods as necessary when the drag source enters and exits
4307      * registered nodes ({@link #onNodeEnter}, {@link #onNodeOut}). If the drag source is not currently over a
4308      * registered node, it will call {@link #onContainerOver}.
4309      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
4310      * @param {Event} e The event
4311      * @param {Object} data An object containing arbitrary data supplied by the drag source
4312      * @return {String} status The CSS class that communicates the drop status back to the source so that the
4313      * underlying {@link Roo.dd.StatusProxy} can be updated
4314      */
4315     notifyOver : function(dd, e, data){
4316         var n = this.getTargetFromEvent(e);
4317         if(!n){ // not over valid drop target
4318             if(this.lastOverNode){
4319                 this.onNodeOut(this.lastOverNode, dd, e, data);
4320                 this.lastOverNode = null;
4321             }
4322             return this.onContainerOver(dd, e, data);
4323         }
4324         if(this.lastOverNode != n){
4325             if(this.lastOverNode){
4326                 this.onNodeOut(this.lastOverNode, dd, e, data);
4327             }
4328             this.onNodeEnter(n, dd, e, data);
4329             this.lastOverNode = n;
4330         }
4331         return this.onNodeOver(n, dd, e, data);
4332     },
4333
4334     /**
4335      * The function a {@link Roo.dd.DragSource} calls once to notify this drop zone that the source has been dragged
4336      * out of the zone without dropping.  If the drag source is currently over a registered node, the notification
4337      * will be delegated to {@link #onNodeOut} for node-specific handling, otherwise it will be ignored.
4338      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop target
4339      * @param {Event} e The event
4340      * @param {Object} data An object containing arbitrary data supplied by the drag zone
4341      */
4342     notifyOut : function(dd, e, data){
4343         if(this.lastOverNode){
4344             this.onNodeOut(this.lastOverNode, dd, e, data);
4345             this.lastOverNode = null;
4346         }
4347     },
4348
4349     /**
4350      * The function a {@link Roo.dd.DragSource} calls once to notify this drop zone that the dragged item has
4351      * been dropped on it.  The drag zone will look up the target node based on the event passed in, and if there
4352      * is a node registered for that event, it will delegate to {@link #onNodeDrop} for node-specific handling,
4353      * otherwise it will call {@link #onContainerDrop}.
4354      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
4355      * @param {Event} e The event
4356      * @param {Object} data An object containing arbitrary data supplied by the drag source
4357      * @return {Boolean} True if the drop was valid, else false
4358      */
4359     notifyDrop : function(dd, e, data){
4360         if(this.lastOverNode){
4361             this.onNodeOut(this.lastOverNode, dd, e, data);
4362             this.lastOverNode = null;
4363         }
4364         var n = this.getTargetFromEvent(e);
4365         return n ?
4366             this.onNodeDrop(n, dd, e, data) :
4367             this.onContainerDrop(dd, e, data);
4368     },
4369
4370     // private
4371     triggerCacheRefresh : function(){
4372         Roo.dd.DDM.refreshCache(this.groups);
4373     }  
4374 });/*
4375  * Based on:
4376  * Ext JS Library 1.1.1
4377  * Copyright(c) 2006-2007, Ext JS, LLC.
4378  *
4379  * Originally Released Under LGPL - original licence link has changed is not relivant.
4380  *
4381  * Fork - LGPL
4382  * <script type="text/javascript">
4383  */
4384
4385
4386 /**
4387  * @class Roo.data.SortTypes
4388  * @singleton
4389  * Defines the default sorting (casting?) comparison functions used when sorting data.
4390  */
4391 Roo.data.SortTypes = {
4392     /**
4393      * Default sort that does nothing
4394      * @param {Mixed} s The value being converted
4395      * @return {Mixed} The comparison value
4396      */
4397     none : function(s){
4398         return s;
4399     },
4400     
4401     /**
4402      * The regular expression used to strip tags
4403      * @type {RegExp}
4404      * @property
4405      */
4406     stripTagsRE : /<\/?[^>]+>/gi,
4407     
4408     /**
4409      * Strips all HTML tags to sort on text only
4410      * @param {Mixed} s The value being converted
4411      * @return {String} The comparison value
4412      */
4413     asText : function(s){
4414         return String(s).replace(this.stripTagsRE, "");
4415     },
4416     
4417     /**
4418      * Strips all HTML tags to sort on text only - Case insensitive
4419      * @param {Mixed} s The value being converted
4420      * @return {String} The comparison value
4421      */
4422     asUCText : function(s){
4423         return String(s).toUpperCase().replace(this.stripTagsRE, "");
4424     },
4425     
4426     /**
4427      * Case insensitive string
4428      * @param {Mixed} s The value being converted
4429      * @return {String} The comparison value
4430      */
4431     asUCString : function(s) {
4432         return String(s).toUpperCase();
4433     },
4434     
4435     /**
4436      * Date sorting
4437      * @param {Mixed} s The value being converted
4438      * @return {Number} The comparison value
4439      */
4440     asDate : function(s) {
4441         if(!s){
4442             return 0;
4443         }
4444         if(s instanceof Date){
4445             return s.getTime();
4446         }
4447         return Date.parse(String(s));
4448     },
4449     
4450     /**
4451      * Float sorting
4452      * @param {Mixed} s The value being converted
4453      * @return {Float} The comparison value
4454      */
4455     asFloat : function(s) {
4456         var val = parseFloat(String(s).replace(/,/g, ""));
4457         if(isNaN(val)) val = 0;
4458         return val;
4459     },
4460     
4461     /**
4462      * Integer sorting
4463      * @param {Mixed} s The value being converted
4464      * @return {Number} The comparison value
4465      */
4466     asInt : function(s) {
4467         var val = parseInt(String(s).replace(/,/g, ""));
4468         if(isNaN(val)) val = 0;
4469         return val;
4470     }
4471 };/*
4472  * Based on:
4473  * Ext JS Library 1.1.1
4474  * Copyright(c) 2006-2007, Ext JS, LLC.
4475  *
4476  * Originally Released Under LGPL - original licence link has changed is not relivant.
4477  *
4478  * Fork - LGPL
4479  * <script type="text/javascript">
4480  */
4481
4482 /**
4483 * @class Roo.data.Record
4484  * Instances of this class encapsulate both record <em>definition</em> information, and record
4485  * <em>value</em> information for use in {@link Roo.data.Store} objects, or any code which needs
4486  * to access Records cached in an {@link Roo.data.Store} object.<br>
4487  * <p>
4488  * Constructors for this class are generated by passing an Array of field definition objects to {@link #create}.
4489  * Instances are usually only created by {@link Roo.data.Reader} implementations when processing unformatted data
4490  * objects.<br>
4491  * <p>
4492  * Record objects generated by this constructor inherit all the methods of Roo.data.Record listed below.
4493  * @constructor
4494  * This constructor should not be used to create Record objects. Instead, use the constructor generated by
4495  * {@link #create}. The parameters are the same.
4496  * @param {Array} data An associative Array of data values keyed by the field name.
4497  * @param {Object} id (Optional) The id of the record. This id should be unique, and is used by the
4498  * {@link Roo.data.Store} object which owns the Record to index its collection of Records. If
4499  * not specified an integer id is generated.
4500  */
4501 Roo.data.Record = function(data, id){
4502     this.id = (id || id === 0) ? id : ++Roo.data.Record.AUTO_ID;
4503     this.data = data;
4504 };
4505
4506 /**
4507  * Generate a constructor for a specific record layout.
4508  * @param {Array} o An Array of field definition objects which specify field names, and optionally,
4509  * data types, and a mapping for an {@link Roo.data.Reader} to extract the field's value from a data object.
4510  * Each field definition object may contain the following properties: <ul>
4511  * <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,
4512  * for example the <em>dataIndex</em> property in column definition objects passed to {@link Roo.grid.ColumnModel}</p></li>
4513  * <li><b>mapping</b> : String<p style="margin-left:1em">(Optional) A path specification for use by the {@link Roo.data.Reader} implementation
4514  * that is creating the Record to access the data value from the data object. If an {@link Roo.data.JsonReader}
4515  * is being used, then this is a string containing the javascript expression to reference the data relative to 
4516  * the record item's root. If an {@link Roo.data.XmlReader} is being used, this is an {@link Roo.DomQuery} path
4517  * to the data item relative to the record element. If the mapping expression is the same as the field name,
4518  * this may be omitted.</p></li>
4519  * <li><b>type</b> : String<p style="margin-left:1em">(Optional) The data type for conversion to displayable value. Possible values are
4520  * <ul><li>auto (Default, implies no conversion)</li>
4521  * <li>string</li>
4522  * <li>int</li>
4523  * <li>float</li>
4524  * <li>boolean</li>
4525  * <li>date</li></ul></p></li>
4526  * <li><b>sortType</b> : Mixed<p style="margin-left:1em">(Optional) A member of {@link Roo.data.SortTypes}.</p></li>
4527  * <li><b>sortDir</b> : String<p style="margin-left:1em">(Optional) Initial direction to sort. "ASC" or "DESC"</p></li>
4528  * <li><b>convert</b> : Function<p style="margin-left:1em">(Optional) A function which converts the value provided
4529  * by the Reader into an object that will be stored in the Record. It is passed the
4530  * following parameters:<ul>
4531  * <li><b>v</b> : Mixed<p style="margin-left:1em">The data value as read by the Reader.</p></li>
4532  * </ul></p></li>
4533  * <li><b>dateFormat</b> : String<p style="margin-left:1em">(Optional) A format String for the Date.parseDate function.</p></li>
4534  * </ul>
4535  * <br>usage:<br><pre><code>
4536 var TopicRecord = Roo.data.Record.create(
4537     {name: 'title', mapping: 'topic_title'},
4538     {name: 'author', mapping: 'username'},
4539     {name: 'totalPosts', mapping: 'topic_replies', type: 'int'},
4540     {name: 'lastPost', mapping: 'post_time', type: 'date'},
4541     {name: 'lastPoster', mapping: 'user2'},
4542     {name: 'excerpt', mapping: 'post_text'}
4543 );
4544
4545 var myNewRecord = new TopicRecord({
4546     title: 'Do my job please',
4547     author: 'noobie',
4548     totalPosts: 1,
4549     lastPost: new Date(),
4550     lastPoster: 'Animal',
4551     excerpt: 'No way dude!'
4552 });
4553 myStore.add(myNewRecord);
4554 </code></pre>
4555  * @method create
4556  * @static
4557  */
4558 Roo.data.Record.create = function(o){
4559     var f = function(){
4560         f.superclass.constructor.apply(this, arguments);
4561     };
4562     Roo.extend(f, Roo.data.Record);
4563     var p = f.prototype;
4564     p.fields = new Roo.util.MixedCollection(false, function(field){
4565         return field.name;
4566     });
4567     for(var i = 0, len = o.length; i < len; i++){
4568         p.fields.add(new Roo.data.Field(o[i]));
4569     }
4570     f.getField = function(name){
4571         return p.fields.get(name);  
4572     };
4573     return f;
4574 };
4575
4576 Roo.data.Record.AUTO_ID = 1000;
4577 Roo.data.Record.EDIT = 'edit';
4578 Roo.data.Record.REJECT = 'reject';
4579 Roo.data.Record.COMMIT = 'commit';
4580
4581 Roo.data.Record.prototype = {
4582     /**
4583      * Readonly flag - true if this record has been modified.
4584      * @type Boolean
4585      */
4586     dirty : false,
4587     editing : false,
4588     error: null,
4589     modified: null,
4590
4591     // private
4592     join : function(store){
4593         this.store = store;
4594     },
4595
4596     /**
4597      * Set the named field to the specified value.
4598      * @param {String} name The name of the field to set.
4599      * @param {Object} value The value to set the field to.
4600      */
4601     set : function(name, value){
4602         if(this.data[name] == value){
4603             return;
4604         }
4605         this.dirty = true;
4606         if(!this.modified){
4607             this.modified = {};
4608         }
4609         if(typeof this.modified[name] == 'undefined'){
4610             this.modified[name] = this.data[name];
4611         }
4612         this.data[name] = value;
4613         if(!this.editing && this.store){
4614             this.store.afterEdit(this);
4615         }       
4616     },
4617
4618     /**
4619      * Get the value of the named field.
4620      * @param {String} name The name of the field to get the value of.
4621      * @return {Object} The value of the field.
4622      */
4623     get : function(name){
4624         return this.data[name]; 
4625     },
4626
4627     // private
4628     beginEdit : function(){
4629         this.editing = true;
4630         this.modified = {}; 
4631     },
4632
4633     // private
4634     cancelEdit : function(){
4635         this.editing = false;
4636         delete this.modified;
4637     },
4638
4639     // private
4640     endEdit : function(){
4641         this.editing = false;
4642         if(this.dirty && this.store){
4643             this.store.afterEdit(this);
4644         }
4645     },
4646
4647     /**
4648      * Usually called by the {@link Roo.data.Store} which owns the Record.
4649      * Rejects all changes made to the Record since either creation, or the last commit operation.
4650      * Modified fields are reverted to their original values.
4651      * <p>
4652      * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
4653      * of reject operations.
4654      */
4655     reject : function(){
4656         var m = this.modified;
4657         for(var n in m){
4658             if(typeof m[n] != "function"){
4659                 this.data[n] = m[n];
4660             }
4661         }
4662         this.dirty = false;
4663         delete this.modified;
4664         this.editing = false;
4665         if(this.store){
4666             this.store.afterReject(this);
4667         }
4668     },
4669
4670     /**
4671      * Usually called by the {@link Roo.data.Store} which owns the Record.
4672      * Commits all changes made to the Record since either creation, or the last commit operation.
4673      * <p>
4674      * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
4675      * of commit operations.
4676      */
4677     commit : function(){
4678         this.dirty = false;
4679         delete this.modified;
4680         this.editing = false;
4681         if(this.store){
4682             this.store.afterCommit(this);
4683         }
4684     },
4685
4686     // private
4687     hasError : function(){
4688         return this.error != null;
4689     },
4690
4691     // private
4692     clearError : function(){
4693         this.error = null;
4694     },
4695
4696     /**
4697      * Creates a copy of this record.
4698      * @param {String} id (optional) A new record id if you don't want to use this record's id
4699      * @return {Record}
4700      */
4701     copy : function(newId) {
4702         return new this.constructor(Roo.apply({}, this.data), newId || this.id);
4703     }
4704 };/*
4705  * Based on:
4706  * Ext JS Library 1.1.1
4707  * Copyright(c) 2006-2007, Ext JS, LLC.
4708  *
4709  * Originally Released Under LGPL - original licence link has changed is not relivant.
4710  *
4711  * Fork - LGPL
4712  * <script type="text/javascript">
4713  */
4714
4715
4716
4717 /**
4718  * @class Roo.data.Store
4719  * @extends Roo.util.Observable
4720  * The Store class encapsulates a client side cache of {@link Roo.data.Record} objects which provide input data
4721  * for widgets such as the Roo.grid.Grid, or the Roo.form.ComboBox.<br>
4722  * <p>
4723  * 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
4724  * has no knowledge of the format of the data returned by the Proxy.<br>
4725  * <p>
4726  * A Store object uses its configured implementation of {@link Roo.data.DataReader} to create {@link Roo.data.Record}
4727  * instances from the data object. These records are cached and made available through accessor functions.
4728  * @constructor
4729  * Creates a new Store.
4730  * @param {Object} config A config object containing the objects needed for the Store to access data,
4731  * and read the data into Records.
4732  */
4733 Roo.data.Store = function(config){
4734     this.data = new Roo.util.MixedCollection(false);
4735     this.data.getKey = function(o){
4736         return o.id;
4737     };
4738     this.baseParams = {};
4739     // private
4740     this.paramNames = {
4741         "start" : "start",
4742         "limit" : "limit",
4743         "sort" : "sort",
4744         "dir" : "dir",
4745         "multisort" : "_multisort"
4746     };
4747
4748     if(config && config.data){
4749         this.inlineData = config.data;
4750         delete config.data;
4751     }
4752
4753     Roo.apply(this, config);
4754     
4755     if(this.reader){ // reader passed
4756         this.reader = Roo.factory(this.reader, Roo.data);
4757         this.reader.xmodule = this.xmodule || false;
4758         if(!this.recordType){
4759             this.recordType = this.reader.recordType;
4760         }
4761         if(this.reader.onMetaChange){
4762             this.reader.onMetaChange = this.onMetaChange.createDelegate(this);
4763         }
4764     }
4765
4766     if(this.recordType){
4767         this.fields = this.recordType.prototype.fields;
4768     }
4769     this.modified = [];
4770
4771     this.addEvents({
4772         /**
4773          * @event datachanged
4774          * Fires when the data cache has changed, and a widget which is using this Store
4775          * as a Record cache should refresh its view.
4776          * @param {Store} this
4777          */
4778         datachanged : true,
4779         /**
4780          * @event metachange
4781          * Fires when this store's reader provides new metadata (fields). This is currently only support for JsonReaders.
4782          * @param {Store} this
4783          * @param {Object} meta The JSON metadata
4784          */
4785         metachange : true,
4786         /**
4787          * @event add
4788          * Fires when Records have been added to the Store
4789          * @param {Store} this
4790          * @param {Roo.data.Record[]} records The array of Records added
4791          * @param {Number} index The index at which the record(s) were added
4792          */
4793         add : true,
4794         /**
4795          * @event remove
4796          * Fires when a Record has been removed from the Store
4797          * @param {Store} this
4798          * @param {Roo.data.Record} record The Record that was removed
4799          * @param {Number} index The index at which the record was removed
4800          */
4801         remove : true,
4802         /**
4803          * @event update
4804          * Fires when a Record has been updated
4805          * @param {Store} this
4806          * @param {Roo.data.Record} record The Record that was updated
4807          * @param {String} operation The update operation being performed.  Value may be one of:
4808          * <pre><code>
4809  Roo.data.Record.EDIT
4810  Roo.data.Record.REJECT
4811  Roo.data.Record.COMMIT
4812          * </code></pre>
4813          */
4814         update : true,
4815         /**
4816          * @event clear
4817          * Fires when the data cache has been cleared.
4818          * @param {Store} this
4819          */
4820         clear : true,
4821         /**
4822          * @event beforeload
4823          * Fires before a request is made for a new data object.  If the beforeload handler returns false
4824          * the load action will be canceled.
4825          * @param {Store} this
4826          * @param {Object} options The loading options that were specified (see {@link #load} for details)
4827          */
4828         beforeload : true,
4829         /**
4830          * @event beforeloadadd
4831          * Fires after a new set of Records has been loaded.
4832          * @param {Store} this
4833          * @param {Roo.data.Record[]} records The Records that were loaded
4834          * @param {Object} options The loading options that were specified (see {@link #load} for details)
4835          */
4836         beforeloadadd : true,
4837         /**
4838          * @event load
4839          * Fires after a new set of Records has been loaded, before they are added to the store.
4840          * @param {Store} this
4841          * @param {Roo.data.Record[]} records The Records that were loaded
4842          * @param {Object} options The loading options that were specified (see {@link #load} for details)
4843          * @params {Object} return from reader
4844          */
4845         load : true,
4846         /**
4847          * @event loadexception
4848          * Fires if an exception occurs in the Proxy during loading.
4849          * Called with the signature of the Proxy's "loadexception" event.
4850          * If you return Json { data: [] , success: false, .... } then this will be thrown with the following args
4851          * 
4852          * @param {Proxy} 
4853          * @param {Object} return from JsonData.reader() - success, totalRecords, records
4854          * @param {Object} load options 
4855          * @param {Object} jsonData from your request (normally this contains the Exception)
4856          */
4857         loadexception : true
4858     });
4859     
4860     if(this.proxy){
4861         this.proxy = Roo.factory(this.proxy, Roo.data);
4862         this.proxy.xmodule = this.xmodule || false;
4863         this.relayEvents(this.proxy,  ["loadexception"]);
4864     }
4865     this.sortToggle = {};
4866     this.sortOrder = []; // array of order of sorting - updated by grid if multisort is enabled.
4867
4868     Roo.data.Store.superclass.constructor.call(this);
4869
4870     if(this.inlineData){
4871         this.loadData(this.inlineData);
4872         delete this.inlineData;
4873     }
4874 };
4875
4876 Roo.extend(Roo.data.Store, Roo.util.Observable, {
4877      /**
4878     * @cfg {boolean} isLocal   flag if data is locally available (and can be always looked up
4879     * without a remote query - used by combo/forms at present.
4880     */
4881     
4882     /**
4883     * @cfg {Roo.data.DataProxy} proxy The Proxy object which provides access to a data object.
4884     */
4885     /**
4886     * @cfg {Array} data Inline data to be loaded when the store is initialized.
4887     */
4888     /**
4889     * @cfg {Roo.data.Reader} reader The Reader object which processes the data object and returns
4890     * an Array of Roo.data.record objects which are cached keyed by their <em>id</em> property.
4891     */
4892     /**
4893     * @cfg {Object} baseParams An object containing properties which are to be sent as parameters
4894     * on any HTTP request
4895     */
4896     /**
4897     * @cfg {Object} sortInfo A config object in the format: {field: "fieldName", direction: "ASC|DESC"}
4898     */
4899     /**
4900     * @cfg {Boolean} multiSort enable multi column sorting (sort is based on the order of columns, remote only at present)
4901     */
4902     multiSort: false,
4903     /**
4904     * @cfg {boolean} remoteSort True if sorting is to be handled by requesting the Proxy to provide a refreshed
4905     * version of the data object in sorted order, as opposed to sorting the Record cache in place (defaults to false).
4906     */
4907     remoteSort : false,
4908
4909     /**
4910     * @cfg {boolean} pruneModifiedRecords True to clear all modified record information each time the store is
4911      * loaded or when a record is removed. (defaults to false).
4912     */
4913     pruneModifiedRecords : false,
4914
4915     // private
4916     lastOptions : null,
4917
4918     /**
4919      * Add Records to the Store and fires the add event.
4920      * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
4921      */
4922     add : function(records){
4923         records = [].concat(records);
4924         for(var i = 0, len = records.length; i < len; i++){
4925             records[i].join(this);
4926         }
4927         var index = this.data.length;
4928         this.data.addAll(records);
4929         this.fireEvent("add", this, records, index);
4930     },
4931
4932     /**
4933      * Remove a Record from the Store and fires the remove event.
4934      * @param {Ext.data.Record} record The Roo.data.Record object to remove from the cache.
4935      */
4936     remove : function(record){
4937         var index = this.data.indexOf(record);
4938         this.data.removeAt(index);
4939         if(this.pruneModifiedRecords){
4940             this.modified.remove(record);
4941         }
4942         this.fireEvent("remove", this, record, index);
4943     },
4944
4945     /**
4946      * Remove all Records from the Store and fires the clear event.
4947      */
4948     removeAll : function(){
4949         this.data.clear();
4950         if(this.pruneModifiedRecords){
4951             this.modified = [];
4952         }
4953         this.fireEvent("clear", this);
4954     },
4955
4956     /**
4957      * Inserts Records to the Store at the given index and fires the add event.
4958      * @param {Number} index The start index at which to insert the passed Records.
4959      * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
4960      */
4961     insert : function(index, records){
4962         records = [].concat(records);
4963         for(var i = 0, len = records.length; i < len; i++){
4964             this.data.insert(index, records[i]);
4965             records[i].join(this);
4966         }
4967         this.fireEvent("add", this, records, index);
4968     },
4969
4970     /**
4971      * Get the index within the cache of the passed Record.
4972      * @param {Roo.data.Record} record The Roo.data.Record object to to find.
4973      * @return {Number} The index of the passed Record. Returns -1 if not found.
4974      */
4975     indexOf : function(record){
4976         return this.data.indexOf(record);
4977     },
4978
4979     /**
4980      * Get the index within the cache of the Record with the passed id.
4981      * @param {String} id The id of the Record to find.
4982      * @return {Number} The index of the Record. Returns -1 if not found.
4983      */
4984     indexOfId : function(id){
4985         return this.data.indexOfKey(id);
4986     },
4987
4988     /**
4989      * Get the Record with the specified id.
4990      * @param {String} id The id of the Record to find.
4991      * @return {Roo.data.Record} The Record with the passed id. Returns undefined if not found.
4992      */
4993     getById : function(id){
4994         return this.data.key(id);
4995     },
4996
4997     /**
4998      * Get the Record at the specified index.
4999      * @param {Number} index The index of the Record to find.
5000      * @return {Roo.data.Record} The Record at the passed index. Returns undefined if not found.
5001      */
5002     getAt : function(index){
5003         return this.data.itemAt(index);
5004     },
5005
5006     /**
5007      * Returns a range of Records between specified indices.
5008      * @param {Number} startIndex (optional) The starting index (defaults to 0)
5009      * @param {Number} endIndex (optional) The ending index (defaults to the last Record in the Store)
5010      * @return {Roo.data.Record[]} An array of Records
5011      */
5012     getRange : function(start, end){
5013         return this.data.getRange(start, end);
5014     },
5015
5016     // private
5017     storeOptions : function(o){
5018         o = Roo.apply({}, o);
5019         delete o.callback;
5020         delete o.scope;
5021         this.lastOptions = o;
5022     },
5023
5024     /**
5025      * Loads the Record cache from the configured Proxy using the configured Reader.
5026      * <p>
5027      * If using remote paging, then the first load call must specify the <em>start</em>
5028      * and <em>limit</em> properties in the options.params property to establish the initial
5029      * position within the dataset, and the number of Records to cache on each read from the Proxy.
5030      * <p>
5031      * <strong>It is important to note that for remote data sources, loading is asynchronous,
5032      * and this call will return before the new data has been loaded. Perform any post-processing
5033      * in a callback function, or in a "load" event handler.</strong>
5034      * <p>
5035      * @param {Object} options An object containing properties which control loading options:<ul>
5036      * <li>params {Object} An object containing properties to pass as HTTP parameters to a remote data source.</li>
5037      * <li>callback {Function} A function to be called after the Records have been loaded. The callback is
5038      * passed the following arguments:<ul>
5039      * <li>r : Roo.data.Record[]</li>
5040      * <li>options: Options object from the load call</li>
5041      * <li>success: Boolean success indicator</li></ul></li>
5042      * <li>scope {Object} Scope with which to call the callback (defaults to the Store object)</li>
5043      * <li>add {Boolean} indicator to append loaded records rather than replace the current cache.</li>
5044      * </ul>
5045      */
5046     load : function(options){
5047         options = options || {};
5048         if(this.fireEvent("beforeload", this, options) !== false){
5049             this.storeOptions(options);
5050             var p = Roo.apply(options.params || {}, this.baseParams);
5051             // if meta was not loaded from remote source.. try requesting it.
5052             if (!this.reader.metaFromRemote) {
5053                 p._requestMeta = 1;
5054             }
5055             if(this.sortInfo && this.remoteSort){
5056                 var pn = this.paramNames;
5057                 p[pn["sort"]] = this.sortInfo.field;
5058                 p[pn["dir"]] = this.sortInfo.direction;
5059             }
5060             if (this.multiSort) {
5061                 var pn = this.paramNames;
5062                 p[pn["multisort"]] = Roo.encode( { sort : this.sortToggle, order: this.sortOrder });
5063             }
5064             
5065             this.proxy.load(p, this.reader, this.loadRecords, this, options);
5066         }
5067     },
5068
5069     /**
5070      * Reloads the Record cache from the configured Proxy using the configured Reader and
5071      * the options from the last load operation performed.
5072      * @param {Object} options (optional) An object containing properties which may override the options
5073      * used in the last load operation. See {@link #load} for details (defaults to null, in which case
5074      * the most recently used options are reused).
5075      */
5076     reload : function(options){
5077         this.load(Roo.applyIf(options||{}, this.lastOptions));
5078     },
5079
5080     // private
5081     // Called as a callback by the Reader during a load operation.
5082     loadRecords : function(o, options, success){
5083         if(!o || success === false){
5084             if(success !== false){
5085                 this.fireEvent("load", this, [], options, o);
5086             }
5087             if(options.callback){
5088                 options.callback.call(options.scope || this, [], options, false);
5089             }
5090             return;
5091         }
5092         // if data returned failure - throw an exception.
5093         if (o.success === false) {
5094             // show a message if no listener is registered.
5095             if (!this.hasListener('loadexception') && typeof(o.raw.errorMsg) != 'undefined') {
5096                     Roo.MessageBox.alert("Error loading",o.raw.errorMsg);
5097             }
5098             // loadmask wil be hooked into this..
5099             this.fireEvent("loadexception", this, o, options, o.raw.errorMsg);
5100             return;
5101         }
5102         var r = o.records, t = o.totalRecords || r.length;
5103         
5104         this.fireEvent("beforeloadadd", this, r, options, o);
5105         
5106         if(!options || options.add !== true){
5107             if(this.pruneModifiedRecords){
5108                 this.modified = [];
5109             }
5110             for(var i = 0, len = r.length; i < len; i++){
5111                 r[i].join(this);
5112             }
5113             if(this.snapshot){
5114                 this.data = this.snapshot;
5115                 delete this.snapshot;
5116             }
5117             this.data.clear();
5118             this.data.addAll(r);
5119             this.totalLength = t;
5120             this.applySort();
5121             this.fireEvent("datachanged", this);
5122         }else{
5123             this.totalLength = Math.max(t, this.data.length+r.length);
5124             this.add(r);
5125         }
5126         this.fireEvent("load", this, r, options, o);
5127         if(options.callback){
5128             options.callback.call(options.scope || this, r, options, true);
5129         }
5130     },
5131
5132
5133     /**
5134      * Loads data from a passed data block. A Reader which understands the format of the data
5135      * must have been configured in the constructor.
5136      * @param {Object} data The data block from which to read the Records.  The format of the data expected
5137      * is dependent on the type of Reader that is configured and should correspond to that Reader's readRecords parameter.
5138      * @param {Boolean} append (Optional) True to append the new Records rather than replace the existing cache.
5139      */
5140     loadData : function(o, append){
5141         var r = this.reader.readRecords(o);
5142         this.loadRecords(r, {add: append}, true);
5143     },
5144
5145     /**
5146      * Gets the number of cached records.
5147      * <p>
5148      * <em>If using paging, this may not be the total size of the dataset. If the data object
5149      * used by the Reader contains the dataset size, then the getTotalCount() function returns
5150      * the data set size</em>
5151      */
5152     getCount : function(){
5153         return this.data.length || 0;
5154     },
5155
5156     /**
5157      * Gets the total number of records in the dataset as returned by the server.
5158      * <p>
5159      * <em>If using paging, for this to be accurate, the data object used by the Reader must contain
5160      * the dataset size</em>
5161      */
5162     getTotalCount : function(){
5163         return this.totalLength || 0;
5164     },
5165
5166     /**
5167      * Returns the sort state of the Store as an object with two properties:
5168      * <pre><code>
5169  field {String} The name of the field by which the Records are sorted
5170  direction {String} The sort order, "ASC" or "DESC"
5171      * </code></pre>
5172      */
5173     getSortState : function(){
5174         return this.sortInfo;
5175     },
5176
5177     // private
5178     applySort : function(){
5179         if(this.sortInfo && !this.remoteSort){
5180             var s = this.sortInfo, f = s.field;
5181             var st = this.fields.get(f).sortType;
5182             var fn = function(r1, r2){
5183                 var v1 = st(r1.data[f]), v2 = st(r2.data[f]);
5184                 return v1 > v2 ? 1 : (v1 < v2 ? -1 : 0);
5185             };
5186             this.data.sort(s.direction, fn);
5187             if(this.snapshot && this.snapshot != this.data){
5188                 this.snapshot.sort(s.direction, fn);
5189             }
5190         }
5191     },
5192
5193     /**
5194      * Sets the default sort column and order to be used by the next load operation.
5195      * @param {String} fieldName The name of the field to sort by.
5196      * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
5197      */
5198     setDefaultSort : function(field, dir){
5199         this.sortInfo = {field: field, direction: dir ? dir.toUpperCase() : "ASC"};
5200     },
5201
5202     /**
5203      * Sort the Records.
5204      * If remote sorting is used, the sort is performed on the server, and the cache is
5205      * reloaded. If local sorting is used, the cache is sorted internally.
5206      * @param {String} fieldName The name of the field to sort by.
5207      * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
5208      */
5209     sort : function(fieldName, dir){
5210         var f = this.fields.get(fieldName);
5211         if(!dir){
5212             this.sortToggle[f.name] = this.sortToggle[f.name] || f.sortDir;
5213             
5214             if(this.multiSort || (this.sortInfo && this.sortInfo.field == f.name) ){ // toggle sort dir
5215                 dir = (this.sortToggle[f.name] || "ASC").toggle("ASC", "DESC");
5216             }else{
5217                 dir = f.sortDir;
5218             }
5219         }
5220         this.sortToggle[f.name] = dir;
5221         this.sortInfo = {field: f.name, direction: dir};
5222         if(!this.remoteSort){
5223             this.applySort();
5224             this.fireEvent("datachanged", this);
5225         }else{
5226             this.load(this.lastOptions);
5227         }
5228     },
5229
5230     /**
5231      * Calls the specified function for each of the Records in the cache.
5232      * @param {Function} fn The function to call. The Record is passed as the first parameter.
5233      * Returning <em>false</em> aborts and exits the iteration.
5234      * @param {Object} scope (optional) The scope in which to call the function (defaults to the Record).
5235      */
5236     each : function(fn, scope){
5237         this.data.each(fn, scope);
5238     },
5239
5240     /**
5241      * Gets all records modified since the last commit.  Modified records are persisted across load operations
5242      * (e.g., during paging).
5243      * @return {Roo.data.Record[]} An array of Records containing outstanding modifications.
5244      */
5245     getModifiedRecords : function(){
5246         return this.modified;
5247     },
5248
5249     // private
5250     createFilterFn : function(property, value, anyMatch){
5251         if(!value.exec){ // not a regex
5252             value = String(value);
5253             if(value.length == 0){
5254                 return false;
5255             }
5256             value = new RegExp((anyMatch === true ? '' : '^') + Roo.escapeRe(value), "i");
5257         }
5258         return function(r){
5259             return value.test(r.data[property]);
5260         };
5261     },
5262
5263     /**
5264      * Sums the value of <i>property</i> for each record between start and end and returns the result.
5265      * @param {String} property A field on your records
5266      * @param {Number} start The record index to start at (defaults to 0)
5267      * @param {Number} end The last record index to include (defaults to length - 1)
5268      * @return {Number} The sum
5269      */
5270     sum : function(property, start, end){
5271         var rs = this.data.items, v = 0;
5272         start = start || 0;
5273         end = (end || end === 0) ? end : rs.length-1;
5274
5275         for(var i = start; i <= end; i++){
5276             v += (rs[i].data[property] || 0);
5277         }
5278         return v;
5279     },
5280
5281     /**
5282      * Filter the records by a specified property.
5283      * @param {String} field A field on your records
5284      * @param {String/RegExp} value Either a string that the field
5285      * should start with or a RegExp to test against the field
5286      * @param {Boolean} anyMatch True to match any part not just the beginning
5287      */
5288     filter : function(property, value, anyMatch){
5289         var fn = this.createFilterFn(property, value, anyMatch);
5290         return fn ? this.filterBy(fn) : this.clearFilter();
5291     },
5292
5293     /**
5294      * Filter by a function. The specified function will be called with each
5295      * record in this data source. If the function returns true the record is included,
5296      * otherwise it is filtered.
5297      * @param {Function} fn The function to be called, it will receive 2 args (record, id)
5298      * @param {Object} scope (optional) The scope of the function (defaults to this)
5299      */
5300     filterBy : function(fn, scope){
5301         this.snapshot = this.snapshot || this.data;
5302         this.data = this.queryBy(fn, scope||this);
5303         this.fireEvent("datachanged", this);
5304     },
5305
5306     /**
5307      * Query the records by a specified property.
5308      * @param {String} field A field on your records
5309      * @param {String/RegExp} value Either a string that the field
5310      * should start with or a RegExp to test against the field
5311      * @param {Boolean} anyMatch True to match any part not just the beginning
5312      * @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
5313      */
5314     query : function(property, value, anyMatch){
5315         var fn = this.createFilterFn(property, value, anyMatch);
5316         return fn ? this.queryBy(fn) : this.data.clone();
5317     },
5318
5319     /**
5320      * Query by a function. The specified function will be called with each
5321      * record in this data source. If the function returns true the record is included
5322      * in the results.
5323      * @param {Function} fn The function to be called, it will receive 2 args (record, id)
5324      * @param {Object} scope (optional) The scope of the function (defaults to this)
5325       @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
5326      **/
5327     queryBy : function(fn, scope){
5328         var data = this.snapshot || this.data;
5329         return data.filterBy(fn, scope||this);
5330     },
5331
5332     /**
5333      * Collects unique values for a particular dataIndex from this store.
5334      * @param {String} dataIndex The property to collect
5335      * @param {Boolean} allowNull (optional) Pass true to allow null, undefined or empty string values
5336      * @param {Boolean} bypassFilter (optional) Pass true to collect from all records, even ones which are filtered
5337      * @return {Array} An array of the unique values
5338      **/
5339     collect : function(dataIndex, allowNull, bypassFilter){
5340         var d = (bypassFilter === true && this.snapshot) ?
5341                 this.snapshot.items : this.data.items;
5342         var v, sv, r = [], l = {};
5343         for(var i = 0, len = d.length; i < len; i++){
5344             v = d[i].data[dataIndex];
5345             sv = String(v);
5346             if((allowNull || !Roo.isEmpty(v)) && !l[sv]){
5347                 l[sv] = true;
5348                 r[r.length] = v;
5349             }
5350         }
5351         return r;
5352     },
5353
5354     /**
5355      * Revert to a view of the Record cache with no filtering applied.
5356      * @param {Boolean} suppressEvent If true the filter is cleared silently without notifying listeners
5357      */
5358     clearFilter : function(suppressEvent){
5359         if(this.snapshot && this.snapshot != this.data){
5360             this.data = this.snapshot;
5361             delete this.snapshot;
5362             if(suppressEvent !== true){
5363                 this.fireEvent("datachanged", this);
5364             }
5365         }
5366     },
5367
5368     // private
5369     afterEdit : function(record){
5370         if(this.modified.indexOf(record) == -1){
5371             this.modified.push(record);
5372         }
5373         this.fireEvent("update", this, record, Roo.data.Record.EDIT);
5374     },
5375     
5376     // private
5377     afterReject : function(record){
5378         this.modified.remove(record);
5379         this.fireEvent("update", this, record, Roo.data.Record.REJECT);
5380     },
5381
5382     // private
5383     afterCommit : function(record){
5384         this.modified.remove(record);
5385         this.fireEvent("update", this, record, Roo.data.Record.COMMIT);
5386     },
5387
5388     /**
5389      * Commit all Records with outstanding changes. To handle updates for changes, subscribe to the
5390      * Store's "update" event, and perform updating when the third parameter is Roo.data.Record.COMMIT.
5391      */
5392     commitChanges : function(){
5393         var m = this.modified.slice(0);
5394         this.modified = [];
5395         for(var i = 0, len = m.length; i < len; i++){
5396             m[i].commit();
5397         }
5398     },
5399
5400     /**
5401      * Cancel outstanding changes on all changed records.
5402      */
5403     rejectChanges : function(){
5404         var m = this.modified.slice(0);
5405         this.modified = [];
5406         for(var i = 0, len = m.length; i < len; i++){
5407             m[i].reject();
5408         }
5409     },
5410
5411     onMetaChange : function(meta, rtype, o){
5412         this.recordType = rtype;
5413         this.fields = rtype.prototype.fields;
5414         delete this.snapshot;
5415         this.sortInfo = meta.sortInfo || this.sortInfo;
5416         this.modified = [];
5417         this.fireEvent('metachange', this, this.reader.meta);
5418     }
5419 });/*
5420  * Based on:
5421  * Ext JS Library 1.1.1
5422  * Copyright(c) 2006-2007, Ext JS, LLC.
5423  *
5424  * Originally Released Under LGPL - original licence link has changed is not relivant.
5425  *
5426  * Fork - LGPL
5427  * <script type="text/javascript">
5428  */
5429
5430 /**
5431  * @class Roo.data.SimpleStore
5432  * @extends Roo.data.Store
5433  * Small helper class to make creating Stores from Array data easier.
5434  * @cfg {Number} id The array index of the record id. Leave blank to auto generate ids.
5435  * @cfg {Array} fields An array of field definition objects, or field name strings.
5436  * @cfg {Array} data The multi-dimensional array of data
5437  * @constructor
5438  * @param {Object} config
5439  */
5440 Roo.data.SimpleStore = function(config){
5441     Roo.data.SimpleStore.superclass.constructor.call(this, {
5442         isLocal : true,
5443         reader: new Roo.data.ArrayReader({
5444                 id: config.id
5445             },
5446             Roo.data.Record.create(config.fields)
5447         ),
5448         proxy : new Roo.data.MemoryProxy(config.data)
5449     });
5450     this.load();
5451 };
5452 Roo.extend(Roo.data.SimpleStore, Roo.data.Store);/*
5453  * Based on:
5454  * Ext JS Library 1.1.1
5455  * Copyright(c) 2006-2007, Ext JS, LLC.
5456  *
5457  * Originally Released Under LGPL - original licence link has changed is not relivant.
5458  *
5459  * Fork - LGPL
5460  * <script type="text/javascript">
5461  */
5462
5463 /**
5464 /**
5465  * @extends Roo.data.Store
5466  * @class Roo.data.JsonStore
5467  * Small helper class to make creating Stores for JSON data easier. <br/>
5468 <pre><code>
5469 var store = new Roo.data.JsonStore({
5470     url: 'get-images.php',
5471     root: 'images',
5472     fields: ['name', 'url', {name:'size', type: 'float'}, {name:'lastmod', type:'date'}]
5473 });
5474 </code></pre>
5475  * <b>Note: Although they are not listed, this class inherits all of the config options of Store,
5476  * JsonReader and HttpProxy (unless inline data is provided).</b>
5477  * @cfg {Array} fields An array of field definition objects, or field name strings.
5478  * @constructor
5479  * @param {Object} config
5480  */
5481 Roo.data.JsonStore = function(c){
5482     Roo.data.JsonStore.superclass.constructor.call(this, Roo.apply(c, {
5483         proxy: !c.data ? new Roo.data.HttpProxy({url: c.url}) : undefined,
5484         reader: new Roo.data.JsonReader(c, c.fields)
5485     }));
5486 };
5487 Roo.extend(Roo.data.JsonStore, Roo.data.Store);/*
5488  * Based on:
5489  * Ext JS Library 1.1.1
5490  * Copyright(c) 2006-2007, Ext JS, LLC.
5491  *
5492  * Originally Released Under LGPL - original licence link has changed is not relivant.
5493  *
5494  * Fork - LGPL
5495  * <script type="text/javascript">
5496  */
5497
5498  
5499 Roo.data.Field = function(config){
5500     if(typeof config == "string"){
5501         config = {name: config};
5502     }
5503     Roo.apply(this, config);
5504     
5505     if(!this.type){
5506         this.type = "auto";
5507     }
5508     
5509     var st = Roo.data.SortTypes;
5510     // named sortTypes are supported, here we look them up
5511     if(typeof this.sortType == "string"){
5512         this.sortType = st[this.sortType];
5513     }
5514     
5515     // set default sortType for strings and dates
5516     if(!this.sortType){
5517         switch(this.type){
5518             case "string":
5519                 this.sortType = st.asUCString;
5520                 break;
5521             case "date":
5522                 this.sortType = st.asDate;
5523                 break;
5524             default:
5525                 this.sortType = st.none;
5526         }
5527     }
5528
5529     // define once
5530     var stripRe = /[\$,%]/g;
5531
5532     // prebuilt conversion function for this field, instead of
5533     // switching every time we're reading a value
5534     if(!this.convert){
5535         var cv, dateFormat = this.dateFormat;
5536         switch(this.type){
5537             case "":
5538             case "auto":
5539             case undefined:
5540                 cv = function(v){ return v; };
5541                 break;
5542             case "string":
5543                 cv = function(v){ return (v === undefined || v === null) ? '' : String(v); };
5544                 break;
5545             case "int":
5546                 cv = function(v){
5547                     return v !== undefined && v !== null && v !== '' ?
5548                            parseInt(String(v).replace(stripRe, ""), 10) : '';
5549                     };
5550                 break;
5551             case "float":
5552                 cv = function(v){
5553                     return v !== undefined && v !== null && v !== '' ?
5554                            parseFloat(String(v).replace(stripRe, ""), 10) : ''; 
5555                     };
5556                 break;
5557             case "bool":
5558             case "boolean":
5559                 cv = function(v){ return v === true || v === "true" || v == 1; };
5560                 break;
5561             case "date":
5562                 cv = function(v){
5563                     if(!v){
5564                         return '';
5565                     }
5566                     if(v instanceof Date){
5567                         return v;
5568                     }
5569                     if(dateFormat){
5570                         if(dateFormat == "timestamp"){
5571                             return new Date(v*1000);
5572                         }
5573                         return Date.parseDate(v, dateFormat);
5574                     }
5575                     var parsed = Date.parse(v);
5576                     return parsed ? new Date(parsed) : null;
5577                 };
5578              break;
5579             
5580         }
5581         this.convert = cv;
5582     }
5583 };
5584
5585 Roo.data.Field.prototype = {
5586     dateFormat: null,
5587     defaultValue: "",
5588     mapping: null,
5589     sortType : null,
5590     sortDir : "ASC"
5591 };/*
5592  * Based on:
5593  * Ext JS Library 1.1.1
5594  * Copyright(c) 2006-2007, Ext JS, LLC.
5595  *
5596  * Originally Released Under LGPL - original licence link has changed is not relivant.
5597  *
5598  * Fork - LGPL
5599  * <script type="text/javascript">
5600  */
5601  
5602 // Base class for reading structured data from a data source.  This class is intended to be
5603 // extended (see ArrayReader, JsonReader and XmlReader) and should not be created directly.
5604
5605 /**
5606  * @class Roo.data.DataReader
5607  * Base class for reading structured data from a data source.  This class is intended to be
5608  * extended (see {Roo.data.ArrayReader}, {Roo.data.JsonReader} and {Roo.data.XmlReader}) and should not be created directly.
5609  */
5610
5611 Roo.data.DataReader = function(meta, recordType){
5612     
5613     this.meta = meta;
5614     
5615     this.recordType = recordType instanceof Array ? 
5616         Roo.data.Record.create(recordType) : recordType;
5617 };
5618
5619 Roo.data.DataReader.prototype = {
5620      /**
5621      * Create an empty record
5622      * @param {Object} data (optional) - overlay some values
5623      * @return {Roo.data.Record} record created.
5624      */
5625     newRow :  function(d) {
5626         var da =  {};
5627         this.recordType.prototype.fields.each(function(c) {
5628             switch( c.type) {
5629                 case 'int' : da[c.name] = 0; break;
5630                 case 'date' : da[c.name] = new Date(); break;
5631                 case 'float' : da[c.name] = 0.0; break;
5632                 case 'boolean' : da[c.name] = false; break;
5633                 default : da[c.name] = ""; break;
5634             }
5635             
5636         });
5637         return new this.recordType(Roo.apply(da, d));
5638     }
5639     
5640 };/*
5641  * Based on:
5642  * Ext JS Library 1.1.1
5643  * Copyright(c) 2006-2007, Ext JS, LLC.
5644  *
5645  * Originally Released Under LGPL - original licence link has changed is not relivant.
5646  *
5647  * Fork - LGPL
5648  * <script type="text/javascript">
5649  */
5650
5651 /**
5652  * @class Roo.data.DataProxy
5653  * @extends Roo.data.Observable
5654  * This class is an abstract base class for implementations which provide retrieval of
5655  * unformatted data objects.<br>
5656  * <p>
5657  * DataProxy implementations are usually used in conjunction with an implementation of Roo.data.DataReader
5658  * (of the appropriate type which knows how to parse the data object) to provide a block of
5659  * {@link Roo.data.Records} to an {@link Roo.data.Store}.<br>
5660  * <p>
5661  * Custom implementations must implement the load method as described in
5662  * {@link Roo.data.HttpProxy#load}.
5663  */
5664 Roo.data.DataProxy = function(){
5665     this.addEvents({
5666         /**
5667          * @event beforeload
5668          * Fires before a network request is made to retrieve a data object.
5669          * @param {Object} This DataProxy object.
5670          * @param {Object} params The params parameter to the load function.
5671          */
5672         beforeload : true,
5673         /**
5674          * @event load
5675          * Fires before the load method's callback is called.
5676          * @param {Object} This DataProxy object.
5677          * @param {Object} o The data object.
5678          * @param {Object} arg The callback argument object passed to the load function.
5679          */
5680         load : true,
5681         /**
5682          * @event loadexception
5683          * Fires if an Exception occurs during data retrieval.
5684          * @param {Object} This DataProxy object.
5685          * @param {Object} o The data object.
5686          * @param {Object} arg The callback argument object passed to the load function.
5687          * @param {Object} e The Exception.
5688          */
5689         loadexception : true
5690     });
5691     Roo.data.DataProxy.superclass.constructor.call(this);
5692 };
5693
5694 Roo.extend(Roo.data.DataProxy, Roo.util.Observable);
5695
5696     /**
5697      * @cfg {void} listeners (Not available) Constructor blocks listeners from being set
5698      */
5699 /*
5700  * Based on:
5701  * Ext JS Library 1.1.1
5702  * Copyright(c) 2006-2007, Ext JS, LLC.
5703  *
5704  * Originally Released Under LGPL - original licence link has changed is not relivant.
5705  *
5706  * Fork - LGPL
5707  * <script type="text/javascript">
5708  */
5709 /**
5710  * @class Roo.data.MemoryProxy
5711  * An implementation of Roo.data.DataProxy that simply passes the data specified in its constructor
5712  * to the Reader when its load method is called.
5713  * @constructor
5714  * @param {Object} data The data object which the Reader uses to construct a block of Roo.data.Records.
5715  */
5716 Roo.data.MemoryProxy = function(data){
5717     if (data.data) {
5718         data = data.data;
5719     }
5720     Roo.data.MemoryProxy.superclass.constructor.call(this);
5721     this.data = data;
5722 };
5723
5724 Roo.extend(Roo.data.MemoryProxy, Roo.data.DataProxy, {
5725     /**
5726      * Load data from the requested source (in this case an in-memory
5727      * data object passed to the constructor), read the data object into
5728      * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
5729      * process that block using the passed callback.
5730      * @param {Object} params This parameter is not used by the MemoryProxy class.
5731      * @param {Roo.data.DataReader} reader The Reader object which converts the data
5732      * object into a block of Roo.data.Records.
5733      * @param {Function} callback The function into which to pass the block of Roo.data.records.
5734      * The function must be passed <ul>
5735      * <li>The Record block object</li>
5736      * <li>The "arg" argument from the load function</li>
5737      * <li>A boolean success indicator</li>
5738      * </ul>
5739      * @param {Object} scope The scope in which to call the callback
5740      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
5741      */
5742     load : function(params, reader, callback, scope, arg){
5743         params = params || {};
5744         var result;
5745         try {
5746             result = reader.readRecords(this.data);
5747         }catch(e){
5748             this.fireEvent("loadexception", this, arg, null, e);
5749             callback.call(scope, null, arg, false);
5750             return;
5751         }
5752         callback.call(scope, result, arg, true);
5753     },
5754     
5755     // private
5756     update : function(params, records){
5757         
5758     }
5759 });/*
5760  * Based on:
5761  * Ext JS Library 1.1.1
5762  * Copyright(c) 2006-2007, Ext JS, LLC.
5763  *
5764  * Originally Released Under LGPL - original licence link has changed is not relivant.
5765  *
5766  * Fork - LGPL
5767  * <script type="text/javascript">
5768  */
5769 /**
5770  * @class Roo.data.HttpProxy
5771  * @extends Roo.data.DataProxy
5772  * An implementation of {@link Roo.data.DataProxy} that reads a data object from an {@link Roo.data.Connection} object
5773  * configured to reference a certain URL.<br><br>
5774  * <p>
5775  * <em>Note that this class cannot be used to retrieve data from a domain other than the domain
5776  * from which the running page was served.<br><br>
5777  * <p>
5778  * For cross-domain access to remote data, use an {@link Roo.data.ScriptTagProxy}.</em><br><br>
5779  * <p>
5780  * Be aware that to enable the browser to parse an XML document, the server must set
5781  * the Content-Type header in the HTTP response to "text/xml".
5782  * @constructor
5783  * @param {Object} conn Connection config options to add to each request (e.g. {url: 'foo.php'} or
5784  * an {@link Roo.data.Connection} object.  If a Connection config is passed, the singleton {@link Roo.Ajax} object
5785  * will be used to make the request.
5786  */
5787 Roo.data.HttpProxy = function(conn){
5788     Roo.data.HttpProxy.superclass.constructor.call(this);
5789     // is conn a conn config or a real conn?
5790     this.conn = conn;
5791     this.useAjax = !conn || !conn.events;
5792   
5793 };
5794
5795 Roo.extend(Roo.data.HttpProxy, Roo.data.DataProxy, {
5796     // thse are take from connection...
5797     
5798     /**
5799      * @cfg {String} url (Optional) The default URL to be used for requests to the server. (defaults to undefined)
5800      */
5801     /**
5802      * @cfg {Object} extraParams (Optional) An object containing properties which are used as
5803      * extra parameters to each request made by this object. (defaults to undefined)
5804      */
5805     /**
5806      * @cfg {Object} defaultHeaders (Optional) An object containing request headers which are added
5807      *  to each request made by this object. (defaults to undefined)
5808      */
5809     /**
5810      * @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)
5811      */
5812     /**
5813      * @cfg {Number} timeout (Optional) The timeout in milliseconds to be used for requests. (defaults to 30000)
5814      */
5815      /**
5816      * @cfg {Boolean} autoAbort (Optional) Whether this request should abort any pending requests. (defaults to false)
5817      * @type Boolean
5818      */
5819   
5820
5821     /**
5822      * @cfg {Boolean} disableCaching (Optional) True to add a unique cache-buster param to GET requests. (defaults to true)
5823      * @type Boolean
5824      */
5825     /**
5826      * Return the {@link Roo.data.Connection} object being used by this Proxy.
5827      * @return {Connection} The Connection object. This object may be used to subscribe to events on
5828      * a finer-grained basis than the DataProxy events.
5829      */
5830     getConnection : function(){
5831         return this.useAjax ? Roo.Ajax : this.conn;
5832     },
5833
5834     /**
5835      * Load data from the configured {@link Roo.data.Connection}, read the data object into
5836      * a block of Roo.data.Records using the passed {@link Roo.data.DataReader} implementation, and
5837      * process that block using the passed callback.
5838      * @param {Object} params An object containing properties which are to be used as HTTP parameters
5839      * for the request to the remote server.
5840      * @param {Roo.data.DataReader} reader The Reader object which converts the data
5841      * object into a block of Roo.data.Records.
5842      * @param {Function} callback The function into which to pass the block of Roo.data.Records.
5843      * The function must be passed <ul>
5844      * <li>The Record block object</li>
5845      * <li>The "arg" argument from the load function</li>
5846      * <li>A boolean success indicator</li>
5847      * </ul>
5848      * @param {Object} scope The scope in which to call the callback
5849      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
5850      */
5851     load : function(params, reader, callback, scope, arg){
5852         if(this.fireEvent("beforeload", this, params) !== false){
5853             var  o = {
5854                 params : params || {},
5855                 request: {
5856                     callback : callback,
5857                     scope : scope,
5858                     arg : arg
5859                 },
5860                 reader: reader,
5861                 callback : this.loadResponse,
5862                 scope: this
5863             };
5864             if(this.useAjax){
5865                 Roo.applyIf(o, this.conn);
5866                 if(this.activeRequest){
5867                     Roo.Ajax.abort(this.activeRequest);
5868                 }
5869                 this.activeRequest = Roo.Ajax.request(o);
5870             }else{
5871                 this.conn.request(o);
5872             }
5873         }else{
5874             callback.call(scope||this, null, arg, false);
5875         }
5876     },
5877
5878     // private
5879     loadResponse : function(o, success, response){
5880         delete this.activeRequest;
5881         if(!success){
5882             this.fireEvent("loadexception", this, o, response);
5883             o.request.callback.call(o.request.scope, null, o.request.arg, false);
5884             return;
5885         }
5886         var result;
5887         try {
5888             result = o.reader.read(response);
5889         }catch(e){
5890             this.fireEvent("loadexception", this, o, response, e);
5891             o.request.callback.call(o.request.scope, null, o.request.arg, false);
5892             return;
5893         }
5894         
5895         this.fireEvent("load", this, o, o.request.arg);
5896         o.request.callback.call(o.request.scope, result, o.request.arg, true);
5897     },
5898
5899     // private
5900     update : function(dataSet){
5901
5902     },
5903
5904     // private
5905     updateResponse : function(dataSet){
5906
5907     }
5908 });/*
5909  * Based on:
5910  * Ext JS Library 1.1.1
5911  * Copyright(c) 2006-2007, Ext JS, LLC.
5912  *
5913  * Originally Released Under LGPL - original licence link has changed is not relivant.
5914  *
5915  * Fork - LGPL
5916  * <script type="text/javascript">
5917  */
5918
5919 /**
5920  * @class Roo.data.ScriptTagProxy
5921  * An implementation of Roo.data.DataProxy that reads a data object from a URL which may be in a domain
5922  * other than the originating domain of the running page.<br><br>
5923  * <p>
5924  * <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
5925  * of the running page, you must use this class, rather than DataProxy.</em><br><br>
5926  * <p>
5927  * The content passed back from a server resource requested by a ScriptTagProxy is executable JavaScript
5928  * source code that is used as the source inside a &lt;script> tag.<br><br>
5929  * <p>
5930  * In order for the browser to process the returned data, the server must wrap the data object
5931  * with a call to a callback function, the name of which is passed as a parameter by the ScriptTagProxy.
5932  * Below is a Java example for a servlet which returns data for either a ScriptTagProxy, or an HttpProxy
5933  * depending on whether the callback name was passed:
5934  * <p>
5935  * <pre><code>
5936 boolean scriptTag = false;
5937 String cb = request.getParameter("callback");
5938 if (cb != null) {
5939     scriptTag = true;
5940     response.setContentType("text/javascript");
5941 } else {
5942     response.setContentType("application/x-json");
5943 }
5944 Writer out = response.getWriter();
5945 if (scriptTag) {
5946     out.write(cb + "(");
5947 }
5948 out.print(dataBlock.toJsonString());
5949 if (scriptTag) {
5950     out.write(");");
5951 }
5952 </pre></code>
5953  *
5954  * @constructor
5955  * @param {Object} config A configuration object.
5956  */
5957 Roo.data.ScriptTagProxy = function(config){
5958     Roo.data.ScriptTagProxy.superclass.constructor.call(this);
5959     Roo.apply(this, config);
5960     this.head = document.getElementsByTagName("head")[0];
5961 };
5962
5963 Roo.data.ScriptTagProxy.TRANS_ID = 1000;
5964
5965 Roo.extend(Roo.data.ScriptTagProxy, Roo.data.DataProxy, {
5966     /**
5967      * @cfg {String} url The URL from which to request the data object.
5968      */
5969     /**
5970      * @cfg {Number} timeout (Optional) The number of milliseconds to wait for a response. Defaults to 30 seconds.
5971      */
5972     timeout : 30000,
5973     /**
5974      * @cfg {String} callbackParam (Optional) The name of the parameter to pass to the server which tells
5975      * the server the name of the callback function set up by the load call to process the returned data object.
5976      * Defaults to "callback".<p>The server-side processing must read this parameter value, and generate
5977      * javascript output which calls this named function passing the data object as its only parameter.
5978      */
5979     callbackParam : "callback",
5980     /**
5981      *  @cfg {Boolean} nocache (Optional) Defaults to true. Disable cacheing by adding a unique parameter
5982      * name to the request.
5983      */
5984     nocache : true,
5985
5986     /**
5987      * Load data from the configured URL, read the data object into
5988      * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
5989      * process that block using the passed callback.
5990      * @param {Object} params An object containing properties which are to be used as HTTP parameters
5991      * for the request to the remote server.
5992      * @param {Roo.data.DataReader} reader The Reader object which converts the data
5993      * object into a block of Roo.data.Records.
5994      * @param {Function} callback The function into which to pass the block of Roo.data.Records.
5995      * The function must be passed <ul>
5996      * <li>The Record block object</li>
5997      * <li>The "arg" argument from the load function</li>
5998      * <li>A boolean success indicator</li>
5999      * </ul>
6000      * @param {Object} scope The scope in which to call the callback
6001      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
6002      */
6003     load : function(params, reader, callback, scope, arg){
6004         if(this.fireEvent("beforeload", this, params) !== false){
6005
6006             var p = Roo.urlEncode(Roo.apply(params, this.extraParams));
6007
6008             var url = this.url;
6009             url += (url.indexOf("?") != -1 ? "&" : "?") + p;
6010             if(this.nocache){
6011                 url += "&_dc=" + (new Date().getTime());
6012             }
6013             var transId = ++Roo.data.ScriptTagProxy.TRANS_ID;
6014             var trans = {
6015                 id : transId,
6016                 cb : "stcCallback"+transId,
6017                 scriptId : "stcScript"+transId,
6018                 params : params,
6019                 arg : arg,
6020                 url : url,
6021                 callback : callback,
6022                 scope : scope,
6023                 reader : reader
6024             };
6025             var conn = this;
6026
6027             window[trans.cb] = function(o){
6028                 conn.handleResponse(o, trans);
6029             };
6030
6031             url += String.format("&{0}={1}", this.callbackParam, trans.cb);
6032
6033             if(this.autoAbort !== false){
6034                 this.abort();
6035             }
6036
6037             trans.timeoutId = this.handleFailure.defer(this.timeout, this, [trans]);
6038
6039             var script = document.createElement("script");
6040             script.setAttribute("src", url);
6041             script.setAttribute("type", "text/javascript");
6042             script.setAttribute("id", trans.scriptId);
6043             this.head.appendChild(script);
6044
6045             this.trans = trans;
6046         }else{
6047             callback.call(scope||this, null, arg, false);
6048         }
6049     },
6050
6051     // private
6052     isLoading : function(){
6053         return this.trans ? true : false;
6054     },
6055
6056     /**
6057      * Abort the current server request.
6058      */
6059     abort : function(){
6060         if(this.isLoading()){
6061             this.destroyTrans(this.trans);
6062         }
6063     },
6064
6065     // private
6066     destroyTrans : function(trans, isLoaded){
6067         this.head.removeChild(document.getElementById(trans.scriptId));
6068         clearTimeout(trans.timeoutId);
6069         if(isLoaded){
6070             window[trans.cb] = undefined;
6071             try{
6072                 delete window[trans.cb];
6073             }catch(e){}
6074         }else{
6075             // if hasn't been loaded, wait for load to remove it to prevent script error
6076             window[trans.cb] = function(){
6077                 window[trans.cb] = undefined;
6078                 try{
6079                     delete window[trans.cb];
6080                 }catch(e){}
6081             };
6082         }
6083     },
6084
6085     // private
6086     handleResponse : function(o, trans){
6087         this.trans = false;
6088         this.destroyTrans(trans, true);
6089         var result;
6090         try {
6091             result = trans.reader.readRecords(o);
6092         }catch(e){
6093             this.fireEvent("loadexception", this, o, trans.arg, e);
6094             trans.callback.call(trans.scope||window, null, trans.arg, false);
6095             return;
6096         }
6097         this.fireEvent("load", this, o, trans.arg);
6098         trans.callback.call(trans.scope||window, result, trans.arg, true);
6099     },
6100
6101     // private
6102     handleFailure : function(trans){
6103         this.trans = false;
6104         this.destroyTrans(trans, false);
6105         this.fireEvent("loadexception", this, null, trans.arg);
6106         trans.callback.call(trans.scope||window, null, trans.arg, false);
6107     }
6108 });/*
6109  * Based on:
6110  * Ext JS Library 1.1.1
6111  * Copyright(c) 2006-2007, Ext JS, LLC.
6112  *
6113  * Originally Released Under LGPL - original licence link has changed is not relivant.
6114  *
6115  * Fork - LGPL
6116  * <script type="text/javascript">
6117  */
6118
6119 /**
6120  * @class Roo.data.JsonReader
6121  * @extends Roo.data.DataReader
6122  * Data reader class to create an Array of Roo.data.Record objects from a JSON response
6123  * based on mappings in a provided Roo.data.Record constructor.
6124  * 
6125  * The default behaviour of a store is to send ?_requestMeta=1, unless the class has recieved 'metaData' property
6126  * in the reply previously. 
6127  * 
6128  * <p>
6129  * Example code:
6130  * <pre><code>
6131 var RecordDef = Roo.data.Record.create([
6132     {name: 'name', mapping: 'name'},     // "mapping" property not needed if it's the same as "name"
6133     {name: 'occupation'}                 // This field will use "occupation" as the mapping.
6134 ]);
6135 var myReader = new Roo.data.JsonReader({
6136     totalProperty: "results",    // The property which contains the total dataset size (optional)
6137     root: "rows",                // The property which contains an Array of row objects
6138     id: "id"                     // The property within each row object that provides an ID for the record (optional)
6139 }, RecordDef);
6140 </code></pre>
6141  * <p>
6142  * This would consume a JSON file like this:
6143  * <pre><code>
6144 { 'results': 2, 'rows': [
6145     { 'id': 1, 'name': 'Bill', occupation: 'Gardener' },
6146     { 'id': 2, 'name': 'Ben', occupation: 'Horticulturalist' } ]
6147 }
6148 </code></pre>
6149  * @cfg {String} totalProperty Name of the property from which to retrieve the total number of records
6150  * in the dataset. This is only needed if the whole dataset is not passed in one go, but is being
6151  * paged from the remote server.
6152  * @cfg {String} successProperty Name of the property from which to retrieve the success attribute used by forms.
6153  * @cfg {String} root name of the property which contains the Array of row objects.
6154  * @cfg {String} id Name of the property within a row object that contains a record identifier value.
6155  * @constructor
6156  * Create a new JsonReader
6157  * @param {Object} meta Metadata configuration options
6158  * @param {Object} recordType Either an Array of field definition objects,
6159  * or an {@link Roo.data.Record} object created using {@link Roo.data.Record#create}.
6160  */
6161 Roo.data.JsonReader = function(meta, recordType){
6162     
6163     meta = meta || {};
6164     // set some defaults:
6165     Roo.applyIf(meta, {
6166         totalProperty: 'total',
6167         successProperty : 'success',
6168         root : 'data',
6169         id : 'id'
6170     });
6171     
6172     Roo.data.JsonReader.superclass.constructor.call(this, meta, recordType||meta.fields);
6173 };
6174 Roo.extend(Roo.data.JsonReader, Roo.data.DataReader, {
6175     
6176     /**
6177      * @prop {Boolean} metaFromRemote  - if the meta data was loaded from the remote source.
6178      * Used by Store query builder to append _requestMeta to params.
6179      * 
6180      */
6181     metaFromRemote : false,
6182     /**
6183      * This method is only used by a DataProxy which has retrieved data from a remote server.
6184      * @param {Object} response The XHR object which contains the JSON data in its responseText.
6185      * @return {Object} data A data block which is used by an Roo.data.Store object as
6186      * a cache of Roo.data.Records.
6187      */
6188     read : function(response){
6189         var json = response.responseText;
6190        
6191         var o = /* eval:var:o */ eval("("+json+")");
6192         if(!o) {
6193             throw {message: "JsonReader.read: Json object not found"};
6194         }
6195         
6196         if(o.metaData){
6197             
6198             delete this.ef;
6199             this.metaFromRemote = true;
6200             this.meta = o.metaData;
6201             this.recordType = Roo.data.Record.create(o.metaData.fields);
6202             this.onMetaChange(this.meta, this.recordType, o);
6203         }
6204         return this.readRecords(o);
6205     },
6206
6207     // private function a store will implement
6208     onMetaChange : function(meta, recordType, o){
6209
6210     },
6211
6212     /**
6213          * @ignore
6214          */
6215     simpleAccess: function(obj, subsc) {
6216         return obj[subsc];
6217     },
6218
6219         /**
6220          * @ignore
6221          */
6222     getJsonAccessor: function(){
6223         var re = /[\[\.]/;
6224         return function(expr) {
6225             try {
6226                 return(re.test(expr))
6227                     ? new Function("obj", "return obj." + expr)
6228                     : function(obj){
6229                         return obj[expr];
6230                     };
6231             } catch(e){}
6232             return Roo.emptyFn;
6233         };
6234     }(),
6235
6236     /**
6237      * Create a data block containing Roo.data.Records from an XML document.
6238      * @param {Object} o An object which contains an Array of row objects in the property specified
6239      * in the config as 'root, and optionally a property, specified in the config as 'totalProperty'
6240      * which contains the total size of the dataset.
6241      * @return {Object} data A data block which is used by an Roo.data.Store object as
6242      * a cache of Roo.data.Records.
6243      */
6244     readRecords : function(o){
6245         /**
6246          * After any data loads, the raw JSON data is available for further custom processing.
6247          * @type Object
6248          */
6249         this.o = o;
6250         var s = this.meta, Record = this.recordType,
6251             f = Record.prototype.fields, fi = f.items, fl = f.length;
6252
6253 //      Generate extraction functions for the totalProperty, the root, the id, and for each field
6254         if (!this.ef) {
6255             if(s.totalProperty) {
6256                     this.getTotal = this.getJsonAccessor(s.totalProperty);
6257                 }
6258                 if(s.successProperty) {
6259                     this.getSuccess = this.getJsonAccessor(s.successProperty);
6260                 }
6261                 this.getRoot = s.root ? this.getJsonAccessor(s.root) : function(p){return p;};
6262                 if (s.id) {
6263                         var g = this.getJsonAccessor(s.id);
6264                         this.getId = function(rec) {
6265                                 var r = g(rec);
6266                                 return (r === undefined || r === "") ? null : r;
6267                         };
6268                 } else {
6269                         this.getId = function(){return null;};
6270                 }
6271             this.ef = [];
6272             for(var jj = 0; jj < fl; jj++){
6273                 f = fi[jj];
6274                 var map = (f.mapping !== undefined && f.mapping !== null) ? f.mapping : f.name;
6275                 this.ef[jj] = this.getJsonAccessor(map);
6276             }
6277         }
6278
6279         var root = this.getRoot(o), c = root.length, totalRecords = c, success = true;
6280         if(s.totalProperty){
6281             var vt = parseInt(this.getTotal(o), 10);
6282             if(!isNaN(vt)){
6283                 totalRecords = vt;
6284             }
6285         }
6286         if(s.successProperty){
6287             var vs = this.getSuccess(o);
6288             if(vs === false || vs === 'false'){
6289                 success = false;
6290             }
6291         }
6292         var records = [];
6293             for(var i = 0; i < c; i++){
6294                     var n = root[i];
6295                 var values = {};
6296                 var id = this.getId(n);
6297                 for(var j = 0; j < fl; j++){
6298                     f = fi[j];
6299                 var v = this.ef[j](n);
6300                 if (!f.convert) {
6301                     Roo.log('missing convert for ' + f.name);
6302                     Roo.log(f);
6303                     continue;
6304                 }
6305                 values[f.name] = f.convert((v !== undefined) ? v : f.defaultValue);
6306                 }
6307                 var record = new Record(values, id);
6308                 record.json = n;
6309                 records[i] = record;
6310             }
6311             return {
6312             raw : o,
6313                 success : success,
6314                 records : records,
6315                 totalRecords : totalRecords
6316             };
6317     }
6318 });/*
6319  * Based on:
6320  * Ext JS Library 1.1.1
6321  * Copyright(c) 2006-2007, Ext JS, LLC.
6322  *
6323  * Originally Released Under LGPL - original licence link has changed is not relivant.
6324  *
6325  * Fork - LGPL
6326  * <script type="text/javascript">
6327  */
6328
6329 /**
6330  * @class Roo.data.XmlReader
6331  * @extends Roo.data.DataReader
6332  * Data reader class to create an Array of {@link Roo.data.Record} objects from an XML document
6333  * based on mappings in a provided Roo.data.Record constructor.<br><br>
6334  * <p>
6335  * <em>Note that in order for the browser to parse a returned XML document, the Content-Type
6336  * header in the HTTP response must be set to "text/xml".</em>
6337  * <p>
6338  * Example code:
6339  * <pre><code>
6340 var RecordDef = Roo.data.Record.create([
6341    {name: 'name', mapping: 'name'},     // "mapping" property not needed if it's the same as "name"
6342    {name: 'occupation'}                 // This field will use "occupation" as the mapping.
6343 ]);
6344 var myReader = new Roo.data.XmlReader({
6345    totalRecords: "results", // The element which contains the total dataset size (optional)
6346    record: "row",           // The repeated element which contains row information
6347    id: "id"                 // The element within the row that provides an ID for the record (optional)
6348 }, RecordDef);
6349 </code></pre>
6350  * <p>
6351  * This would consume an XML file like this:
6352  * <pre><code>
6353 &lt;?xml?>
6354 &lt;dataset>
6355  &lt;results>2&lt;/results>
6356  &lt;row>
6357    &lt;id>1&lt;/id>
6358    &lt;name>Bill&lt;/name>
6359    &lt;occupation>Gardener&lt;/occupation>
6360  &lt;/row>
6361  &lt;row>
6362    &lt;id>2&lt;/id>
6363    &lt;name>Ben&lt;/name>
6364    &lt;occupation>Horticulturalist&lt;/occupation>
6365  &lt;/row>
6366 &lt;/dataset>
6367 </code></pre>
6368  * @cfg {String} totalRecords The DomQuery path from which to retrieve the total number of records
6369  * in the dataset. This is only needed if the whole dataset is not passed in one go, but is being
6370  * paged from the remote server.
6371  * @cfg {String} record The DomQuery path to the repeated element which contains record information.
6372  * @cfg {String} success The DomQuery path to the success attribute used by forms.
6373  * @cfg {String} id The DomQuery path relative from the record element to the element that contains
6374  * a record identifier value.
6375  * @constructor
6376  * Create a new XmlReader
6377  * @param {Object} meta Metadata configuration options
6378  * @param {Mixed} recordType The definition of the data record type to produce.  This can be either a valid
6379  * Record subclass created with {@link Roo.data.Record#create}, or an array of objects with which to call
6380  * Roo.data.Record.create.  See the {@link Roo.data.Record} class for more details.
6381  */
6382 Roo.data.XmlReader = function(meta, recordType){
6383     meta = meta || {};
6384     Roo.data.XmlReader.superclass.constructor.call(this, meta, recordType||meta.fields);
6385 };
6386 Roo.extend(Roo.data.XmlReader, Roo.data.DataReader, {
6387     /**
6388      * This method is only used by a DataProxy which has retrieved data from a remote server.
6389          * @param {Object} response The XHR object which contains the parsed XML document.  The response is expected
6390          * to contain a method called 'responseXML' that returns an XML document object.
6391      * @return {Object} records A data block which is used by an {@link Roo.data.Store} as
6392      * a cache of Roo.data.Records.
6393      */
6394     read : function(response){
6395         var doc = response.responseXML;
6396         if(!doc) {
6397             throw {message: "XmlReader.read: XML Document not available"};
6398         }
6399         return this.readRecords(doc);
6400     },
6401
6402     /**
6403      * Create a data block containing Roo.data.Records from an XML document.
6404          * @param {Object} doc A parsed XML document.
6405      * @return {Object} records A data block which is used by an {@link Roo.data.Store} as
6406      * a cache of Roo.data.Records.
6407      */
6408     readRecords : function(doc){
6409         /**
6410          * After any data loads/reads, the raw XML Document is available for further custom processing.
6411          * @type XMLDocument
6412          */
6413         this.xmlData = doc;
6414         var root = doc.documentElement || doc;
6415         var q = Roo.DomQuery;
6416         var recordType = this.recordType, fields = recordType.prototype.fields;
6417         var sid = this.meta.id;
6418         var totalRecords = 0, success = true;
6419         if(this.meta.totalRecords){
6420             totalRecords = q.selectNumber(this.meta.totalRecords, root, 0);
6421         }
6422         
6423         if(this.meta.success){
6424             var sv = q.selectValue(this.meta.success, root, true);
6425             success = sv !== false && sv !== 'false';
6426         }
6427         var records = [];
6428         var ns = q.select(this.meta.record, root);
6429         for(var i = 0, len = ns.length; i < len; i++) {
6430                 var n = ns[i];
6431                 var values = {};
6432                 var id = sid ? q.selectValue(sid, n) : undefined;
6433                 for(var j = 0, jlen = fields.length; j < jlen; j++){
6434                     var f = fields.items[j];
6435                 var v = q.selectValue(f.mapping || f.name, n, f.defaultValue);
6436                     v = f.convert(v);
6437                     values[f.name] = v;
6438                 }
6439                 var record = new recordType(values, id);
6440                 record.node = n;
6441                 records[records.length] = record;
6442             }
6443
6444             return {
6445                 success : success,
6446                 records : records,
6447                 totalRecords : totalRecords || records.length
6448             };
6449     }
6450 });/*
6451  * Based on:
6452  * Ext JS Library 1.1.1
6453  * Copyright(c) 2006-2007, Ext JS, LLC.
6454  *
6455  * Originally Released Under LGPL - original licence link has changed is not relivant.
6456  *
6457  * Fork - LGPL
6458  * <script type="text/javascript">
6459  */
6460
6461 /**
6462  * @class Roo.data.ArrayReader
6463  * @extends Roo.data.DataReader
6464  * Data reader class to create an Array of Roo.data.Record objects from an Array.
6465  * Each element of that Array represents a row of data fields. The
6466  * fields are pulled into a Record object using as a subscript, the <em>mapping</em> property
6467  * of the field definition if it exists, or the field's ordinal position in the definition.<br>
6468  * <p>
6469  * Example code:.
6470  * <pre><code>
6471 var RecordDef = Roo.data.Record.create([
6472     {name: 'name', mapping: 1},         // "mapping" only needed if an "id" field is present which
6473     {name: 'occupation', mapping: 2}    // precludes using the ordinal position as the index.
6474 ]);
6475 var myReader = new Roo.data.ArrayReader({
6476     id: 0                     // The subscript within row Array that provides an ID for the Record (optional)
6477 }, RecordDef);
6478 </code></pre>
6479  * <p>
6480  * This would consume an Array like this:
6481  * <pre><code>
6482 [ [1, 'Bill', 'Gardener'], [2, 'Ben', 'Horticulturalist'] ]
6483   </code></pre>
6484  * @cfg {String} id (optional) The subscript within row Array that provides an ID for the Record
6485  * @constructor
6486  * Create a new JsonReader
6487  * @param {Object} meta Metadata configuration options.
6488  * @param {Object} recordType Either an Array of field definition objects
6489  * as specified to {@link Roo.data.Record#create},
6490  * or an {@link Roo.data.Record} object
6491  * created using {@link Roo.data.Record#create}.
6492  */
6493 Roo.data.ArrayReader = function(meta, recordType){
6494     Roo.data.ArrayReader.superclass.constructor.call(this, meta, recordType);
6495 };
6496
6497 Roo.extend(Roo.data.ArrayReader, Roo.data.JsonReader, {
6498     /**
6499      * Create a data block containing Roo.data.Records from an XML document.
6500      * @param {Object} o An Array of row objects which represents the dataset.
6501      * @return {Object} data A data block which is used by an Roo.data.Store object as
6502      * a cache of Roo.data.Records.
6503      */
6504     readRecords : function(o){
6505         var sid = this.meta ? this.meta.id : null;
6506         var recordType = this.recordType, fields = recordType.prototype.fields;
6507         var records = [];
6508         var root = o;
6509             for(var i = 0; i < root.length; i++){
6510                     var n = root[i];
6511                 var values = {};
6512                 var id = ((sid || sid === 0) && n[sid] !== undefined && n[sid] !== "" ? n[sid] : null);
6513                 for(var j = 0, jlen = fields.length; j < jlen; j++){
6514                 var f = fields.items[j];
6515                 var k = f.mapping !== undefined && f.mapping !== null ? f.mapping : j;
6516                 var v = n[k] !== undefined ? n[k] : f.defaultValue;
6517                 v = f.convert(v);
6518                 values[f.name] = v;
6519             }
6520                 var record = new recordType(values, id);
6521                 record.json = n;
6522                 records[records.length] = record;
6523             }
6524             return {
6525                 records : records,
6526                 totalRecords : records.length
6527             };
6528     }
6529 });/*
6530  * Based on:
6531  * Ext JS Library 1.1.1
6532  * Copyright(c) 2006-2007, Ext JS, LLC.
6533  *
6534  * Originally Released Under LGPL - original licence link has changed is not relivant.
6535  *
6536  * Fork - LGPL
6537  * <script type="text/javascript">
6538  */
6539
6540
6541 /**
6542  * @class Roo.data.Tree
6543  * @extends Roo.util.Observable
6544  * Represents a tree data structure and bubbles all the events for its nodes. The nodes
6545  * in the tree have most standard DOM functionality.
6546  * @constructor
6547  * @param {Node} root (optional) The root node
6548  */
6549 Roo.data.Tree = function(root){
6550    this.nodeHash = {};
6551    /**
6552     * The root node for this tree
6553     * @type Node
6554     */
6555    this.root = null;
6556    if(root){
6557        this.setRootNode(root);
6558    }
6559    this.addEvents({
6560        /**
6561         * @event append
6562         * Fires when a new child node is appended to a node in this tree.
6563         * @param {Tree} tree The owner tree
6564         * @param {Node} parent The parent node
6565         * @param {Node} node The newly appended node
6566         * @param {Number} index The index of the newly appended node
6567         */
6568        "append" : true,
6569        /**
6570         * @event remove
6571         * Fires when a child node is removed from a node in this tree.
6572         * @param {Tree} tree The owner tree
6573         * @param {Node} parent The parent node
6574         * @param {Node} node The child node removed
6575         */
6576        "remove" : true,
6577        /**
6578         * @event move
6579         * Fires when a node is moved to a new location in the tree
6580         * @param {Tree} tree The owner tree
6581         * @param {Node} node The node moved
6582         * @param {Node} oldParent The old parent of this node
6583         * @param {Node} newParent The new parent of this node
6584         * @param {Number} index The index it was moved to
6585         */
6586        "move" : true,
6587        /**
6588         * @event insert
6589         * Fires when a new child node is inserted in a node in this tree.
6590         * @param {Tree} tree The owner tree
6591         * @param {Node} parent The parent node
6592         * @param {Node} node The child node inserted
6593         * @param {Node} refNode The child node the node was inserted before
6594         */
6595        "insert" : true,
6596        /**
6597         * @event beforeappend
6598         * Fires before a new child is appended to a node in this tree, return false to cancel the append.
6599         * @param {Tree} tree The owner tree
6600         * @param {Node} parent The parent node
6601         * @param {Node} node The child node to be appended
6602         */
6603        "beforeappend" : true,
6604        /**
6605         * @event beforeremove
6606         * Fires before a child is removed from a node in this tree, return false to cancel the remove.
6607         * @param {Tree} tree The owner tree
6608         * @param {Node} parent The parent node
6609         * @param {Node} node The child node to be removed
6610         */
6611        "beforeremove" : true,
6612        /**
6613         * @event beforemove
6614         * Fires before a node is moved to a new location in the tree. Return false to cancel the move.
6615         * @param {Tree} tree The owner tree
6616         * @param {Node} node The node being moved
6617         * @param {Node} oldParent The parent of the node
6618         * @param {Node} newParent The new parent the node is moving to
6619         * @param {Number} index The index it is being moved to
6620         */
6621        "beforemove" : true,
6622        /**
6623         * @event beforeinsert
6624         * Fires before a new child is inserted in a node in this tree, return false to cancel the insert.
6625         * @param {Tree} tree The owner tree
6626         * @param {Node} parent The parent node
6627         * @param {Node} node The child node to be inserted
6628         * @param {Node} refNode The child node the node is being inserted before
6629         */
6630        "beforeinsert" : true
6631    });
6632
6633     Roo.data.Tree.superclass.constructor.call(this);
6634 };
6635
6636 Roo.extend(Roo.data.Tree, Roo.util.Observable, {
6637     pathSeparator: "/",
6638
6639     proxyNodeEvent : function(){
6640         return this.fireEvent.apply(this, arguments);
6641     },
6642
6643     /**
6644      * Returns the root node for this tree.
6645      * @return {Node}
6646      */
6647     getRootNode : function(){
6648         return this.root;
6649     },
6650
6651     /**
6652      * Sets the root node for this tree.
6653      * @param {Node} node
6654      * @return {Node}
6655      */
6656     setRootNode : function(node){
6657         this.root = node;
6658         node.ownerTree = this;
6659         node.isRoot = true;
6660         this.registerNode(node);
6661         return node;
6662     },
6663
6664     /**
6665      * Gets a node in this tree by its id.
6666      * @param {String} id
6667      * @return {Node}
6668      */
6669     getNodeById : function(id){
6670         return this.nodeHash[id];
6671     },
6672
6673     registerNode : function(node){
6674         this.nodeHash[node.id] = node;
6675     },
6676
6677     unregisterNode : function(node){
6678         delete this.nodeHash[node.id];
6679     },
6680
6681     toString : function(){
6682         return "[Tree"+(this.id?" "+this.id:"")+"]";
6683     }
6684 });
6685
6686 /**
6687  * @class Roo.data.Node
6688  * @extends Roo.util.Observable
6689  * @cfg {Boolean} leaf true if this node is a leaf and does not have children
6690  * @cfg {String} id The id for this node. If one is not specified, one is generated.
6691  * @constructor
6692  * @param {Object} attributes The attributes/config for the node
6693  */
6694 Roo.data.Node = function(attributes){
6695     /**
6696      * The attributes supplied for the node. You can use this property to access any custom attributes you supplied.
6697      * @type {Object}
6698      */
6699     this.attributes = attributes || {};
6700     this.leaf = this.attributes.leaf;
6701     /**
6702      * The node id. @type String
6703      */
6704     this.id = this.attributes.id;
6705     if(!this.id){
6706         this.id = Roo.id(null, "ynode-");
6707         this.attributes.id = this.id;
6708     }
6709      
6710     
6711     /**
6712      * All child nodes of this node. @type Array
6713      */
6714     this.childNodes = [];
6715     if(!this.childNodes.indexOf){ // indexOf is a must
6716         this.childNodes.indexOf = function(o){
6717             for(var i = 0, len = this.length; i < len; i++){
6718                 if(this[i] == o) {
6719                     return i;
6720                 }
6721             }
6722             return -1;
6723         };
6724     }
6725     /**
6726      * The parent node for this node. @type Node
6727      */
6728     this.parentNode = null;
6729     /**
6730      * The first direct child node of this node, or null if this node has no child nodes. @type Node
6731      */
6732     this.firstChild = null;
6733     /**
6734      * The last direct child node of this node, or null if this node has no child nodes. @type Node
6735      */
6736     this.lastChild = null;
6737     /**
6738      * The node immediately preceding this node in the tree, or null if there is no sibling node. @type Node
6739      */
6740     this.previousSibling = null;
6741     /**
6742      * The node immediately following this node in the tree, or null if there is no sibling node. @type Node
6743      */
6744     this.nextSibling = null;
6745
6746     this.addEvents({
6747        /**
6748         * @event append
6749         * Fires when a new child node is appended
6750         * @param {Tree} tree The owner tree
6751         * @param {Node} this This node
6752         * @param {Node} node The newly appended node
6753         * @param {Number} index The index of the newly appended node
6754         */
6755        "append" : true,
6756        /**
6757         * @event remove
6758         * Fires when a child node is removed
6759         * @param {Tree} tree The owner tree
6760         * @param {Node} this This node
6761         * @param {Node} node The removed node
6762         */
6763        "remove" : true,
6764        /**
6765         * @event move
6766         * Fires when this node is moved to a new location in the tree
6767         * @param {Tree} tree The owner tree
6768         * @param {Node} this This node
6769         * @param {Node} oldParent The old parent of this node
6770         * @param {Node} newParent The new parent of this node
6771         * @param {Number} index The index it was moved to
6772         */
6773        "move" : true,
6774        /**
6775         * @event insert
6776         * Fires when a new child node is inserted.
6777         * @param {Tree} tree The owner tree
6778         * @param {Node} this This node
6779         * @param {Node} node The child node inserted
6780         * @param {Node} refNode The child node the node was inserted before
6781         */
6782        "insert" : true,
6783        /**
6784         * @event beforeappend
6785         * Fires before a new child is appended, return false to cancel the append.
6786         * @param {Tree} tree The owner tree
6787         * @param {Node} this This node
6788         * @param {Node} node The child node to be appended
6789         */
6790        "beforeappend" : true,
6791        /**
6792         * @event beforeremove
6793         * Fires before a child is removed, return false to cancel the remove.
6794         * @param {Tree} tree The owner tree
6795         * @param {Node} this This node
6796         * @param {Node} node The child node to be removed
6797         */
6798        "beforeremove" : true,
6799        /**
6800         * @event beforemove
6801         * Fires before this node is moved to a new location in the tree. Return false to cancel the move.
6802         * @param {Tree} tree The owner tree
6803         * @param {Node} this This node
6804         * @param {Node} oldParent The parent of this node
6805         * @param {Node} newParent The new parent this node is moving to
6806         * @param {Number} index The index it is being moved to
6807         */
6808        "beforemove" : true,
6809        /**
6810         * @event beforeinsert
6811         * Fires before a new child is inserted, return false to cancel the insert.
6812         * @param {Tree} tree The owner tree
6813         * @param {Node} this This node
6814         * @param {Node} node The child node to be inserted
6815         * @param {Node} refNode The child node the node is being inserted before
6816         */
6817        "beforeinsert" : true
6818    });
6819     this.listeners = this.attributes.listeners;
6820     Roo.data.Node.superclass.constructor.call(this);
6821 };
6822
6823 Roo.extend(Roo.data.Node, Roo.util.Observable, {
6824     fireEvent : function(evtName){
6825         // first do standard event for this node
6826         if(Roo.data.Node.superclass.fireEvent.apply(this, arguments) === false){
6827             return false;
6828         }
6829         // then bubble it up to the tree if the event wasn't cancelled
6830         var ot = this.getOwnerTree();
6831         if(ot){
6832             if(ot.proxyNodeEvent.apply(ot, arguments) === false){
6833                 return false;
6834             }
6835         }
6836         return true;
6837     },
6838
6839     /**
6840      * Returns true if this node is a leaf
6841      * @return {Boolean}
6842      */
6843     isLeaf : function(){
6844         return this.leaf === true;
6845     },
6846
6847     // private
6848     setFirstChild : function(node){
6849         this.firstChild = node;
6850     },
6851
6852     //private
6853     setLastChild : function(node){
6854         this.lastChild = node;
6855     },
6856
6857
6858     /**
6859      * Returns true if this node is the last child of its parent
6860      * @return {Boolean}
6861      */
6862     isLast : function(){
6863        return (!this.parentNode ? true : this.parentNode.lastChild == this);
6864     },
6865
6866     /**
6867      * Returns true if this node is the first child of its parent
6868      * @return {Boolean}
6869      */
6870     isFirst : function(){
6871        return (!this.parentNode ? true : this.parentNode.firstChild == this);
6872     },
6873
6874     hasChildNodes : function(){
6875         return !this.isLeaf() && this.childNodes.length > 0;
6876     },
6877
6878     /**
6879      * Insert node(s) as the last child node of this node.
6880      * @param {Node/Array} node The node or Array of nodes to append
6881      * @return {Node} The appended node if single append, or null if an array was passed
6882      */
6883     appendChild : function(node){
6884         var multi = false;
6885         if(node instanceof Array){
6886             multi = node;
6887         }else if(arguments.length > 1){
6888             multi = arguments;
6889         }
6890         // if passed an array or multiple args do them one by one
6891         if(multi){
6892             for(var i = 0, len = multi.length; i < len; i++) {
6893                 this.appendChild(multi[i]);
6894             }
6895         }else{
6896             if(this.fireEvent("beforeappend", this.ownerTree, this, node) === false){
6897                 return false;
6898             }
6899             var index = this.childNodes.length;
6900             var oldParent = node.parentNode;
6901             // it's a move, make sure we move it cleanly
6902             if(oldParent){
6903                 if(node.fireEvent("beforemove", node.getOwnerTree(), node, oldParent, this, index) === false){
6904                     return false;
6905                 }
6906                 oldParent.removeChild(node);
6907             }
6908             index = this.childNodes.length;
6909             if(index == 0){
6910                 this.setFirstChild(node);
6911             }
6912             this.childNodes.push(node);
6913             node.parentNode = this;
6914             var ps = this.childNodes[index-1];
6915             if(ps){
6916                 node.previousSibling = ps;
6917                 ps.nextSibling = node;
6918             }else{
6919                 node.previousSibling = null;
6920             }
6921             node.nextSibling = null;
6922             this.setLastChild(node);
6923             node.setOwnerTree(this.getOwnerTree());
6924             this.fireEvent("append", this.ownerTree, this, node, index);
6925             if(oldParent){
6926                 node.fireEvent("move", this.ownerTree, node, oldParent, this, index);
6927             }
6928             return node;
6929         }
6930     },
6931
6932     /**
6933      * Removes a child node from this node.
6934      * @param {Node} node The node to remove
6935      * @return {Node} The removed node
6936      */
6937     removeChild : function(node){
6938         var index = this.childNodes.indexOf(node);
6939         if(index == -1){
6940             return false;
6941         }
6942         if(this.fireEvent("beforeremove", this.ownerTree, this, node) === false){
6943             return false;
6944         }
6945
6946         // remove it from childNodes collection
6947         this.childNodes.splice(index, 1);
6948
6949         // update siblings
6950         if(node.previousSibling){
6951             node.previousSibling.nextSibling = node.nextSibling;
6952         }
6953         if(node.nextSibling){
6954             node.nextSibling.previousSibling = node.previousSibling;
6955         }
6956
6957         // update child refs
6958         if(this.firstChild == node){
6959             this.setFirstChild(node.nextSibling);
6960         }
6961         if(this.lastChild == node){
6962             this.setLastChild(node.previousSibling);
6963         }
6964
6965         node.setOwnerTree(null);
6966         // clear any references from the node
6967         node.parentNode = null;
6968         node.previousSibling = null;
6969         node.nextSibling = null;
6970         this.fireEvent("remove", this.ownerTree, this, node);
6971         return node;
6972     },
6973
6974     /**
6975      * Inserts the first node before the second node in this nodes childNodes collection.
6976      * @param {Node} node The node to insert
6977      * @param {Node} refNode The node to insert before (if null the node is appended)
6978      * @return {Node} The inserted node
6979      */
6980     insertBefore : function(node, refNode){
6981         if(!refNode){ // like standard Dom, refNode can be null for append
6982             return this.appendChild(node);
6983         }
6984         // nothing to do
6985         if(node == refNode){
6986             return false;
6987         }
6988
6989         if(this.fireEvent("beforeinsert", this.ownerTree, this, node, refNode) === false){
6990             return false;
6991         }
6992         var index = this.childNodes.indexOf(refNode);
6993         var oldParent = node.parentNode;
6994         var refIndex = index;
6995
6996         // when moving internally, indexes will change after remove
6997         if(oldParent == this && this.childNodes.indexOf(node) < index){
6998             refIndex--;
6999         }
7000
7001         // it's a move, make sure we move it cleanly
7002         if(oldParent){
7003             if(node.fireEvent("beforemove", node.getOwnerTree(), node, oldParent, this, index, refNode) === false){
7004                 return false;
7005             }
7006             oldParent.removeChild(node);
7007         }
7008         if(refIndex == 0){
7009             this.setFirstChild(node);
7010         }
7011         this.childNodes.splice(refIndex, 0, node);
7012         node.parentNode = this;
7013         var ps = this.childNodes[refIndex-1];
7014         if(ps){
7015             node.previousSibling = ps;
7016             ps.nextSibling = node;
7017         }else{
7018             node.previousSibling = null;
7019         }
7020         node.nextSibling = refNode;
7021         refNode.previousSibling = node;
7022         node.setOwnerTree(this.getOwnerTree());
7023         this.fireEvent("insert", this.ownerTree, this, node, refNode);
7024         if(oldParent){
7025             node.fireEvent("move", this.ownerTree, node, oldParent, this, refIndex, refNode);
7026         }
7027         return node;
7028     },
7029
7030     /**
7031      * Returns the child node at the specified index.
7032      * @param {Number} index
7033      * @return {Node}
7034      */
7035     item : function(index){
7036         return this.childNodes[index];
7037     },
7038
7039     /**
7040      * Replaces one child node in this node with another.
7041      * @param {Node} newChild The replacement node
7042      * @param {Node} oldChild The node to replace
7043      * @return {Node} The replaced node
7044      */
7045     replaceChild : function(newChild, oldChild){
7046         this.insertBefore(newChild, oldChild);
7047         this.removeChild(oldChild);
7048         return oldChild;
7049     },
7050
7051     /**
7052      * Returns the index of a child node
7053      * @param {Node} node
7054      * @return {Number} The index of the node or -1 if it was not found
7055      */
7056     indexOf : function(child){
7057         return this.childNodes.indexOf(child);
7058     },
7059
7060     /**
7061      * Returns the tree this node is in.
7062      * @return {Tree}
7063      */
7064     getOwnerTree : function(){
7065         // if it doesn't have one, look for one
7066         if(!this.ownerTree){
7067             var p = this;
7068             while(p){
7069                 if(p.ownerTree){
7070                     this.ownerTree = p.ownerTree;
7071                     break;
7072                 }
7073                 p = p.parentNode;
7074             }
7075         }
7076         return this.ownerTree;
7077     },
7078
7079     /**
7080      * Returns depth of this node (the root node has a depth of 0)
7081      * @return {Number}
7082      */
7083     getDepth : function(){
7084         var depth = 0;
7085         var p = this;
7086         while(p.parentNode){
7087             ++depth;
7088             p = p.parentNode;
7089         }
7090         return depth;
7091     },
7092
7093     // private
7094     setOwnerTree : function(tree){
7095         // if it's move, we need to update everyone
7096         if(tree != this.ownerTree){
7097             if(this.ownerTree){
7098                 this.ownerTree.unregisterNode(this);
7099             }
7100             this.ownerTree = tree;
7101             var cs = this.childNodes;
7102             for(var i = 0, len = cs.length; i < len; i++) {
7103                 cs[i].setOwnerTree(tree);
7104             }
7105             if(tree){
7106                 tree.registerNode(this);
7107             }
7108         }
7109     },
7110
7111     /**
7112      * Returns the path for this node. The path can be used to expand or select this node programmatically.
7113      * @param {String} attr (optional) The attr to use for the path (defaults to the node's id)
7114      * @return {String} The path
7115      */
7116     getPath : function(attr){
7117         attr = attr || "id";
7118         var p = this.parentNode;
7119         var b = [this.attributes[attr]];
7120         while(p){
7121             b.unshift(p.attributes[attr]);
7122             p = p.parentNode;
7123         }
7124         var sep = this.getOwnerTree().pathSeparator;
7125         return sep + b.join(sep);
7126     },
7127
7128     /**
7129      * Bubbles up the tree from this node, calling the specified function with each node. The scope (<i>this</i>) of
7130      * function call will be the scope provided or the current node. The arguments to the function
7131      * will be the args provided or the current node. If the function returns false at any point,
7132      * the bubble is stopped.
7133      * @param {Function} fn The function to call
7134      * @param {Object} scope (optional) The scope of the function (defaults to current node)
7135      * @param {Array} args (optional) The args to call the function with (default to passing the current node)
7136      */
7137     bubble : function(fn, scope, args){
7138         var p = this;
7139         while(p){
7140             if(fn.call(scope || p, args || p) === false){
7141                 break;
7142             }
7143             p = p.parentNode;
7144         }
7145     },
7146
7147     /**
7148      * Cascades down the tree from this node, calling the specified function with each node. The scope (<i>this</i>) of
7149      * function call will be the scope provided or the current node. The arguments to the function
7150      * will be the args provided or the current node. If the function returns false at any point,
7151      * the cascade is stopped on that branch.
7152      * @param {Function} fn The function to call
7153      * @param {Object} scope (optional) The scope of the function (defaults to current node)
7154      * @param {Array} args (optional) The args to call the function with (default to passing the current node)
7155      */
7156     cascade : function(fn, scope, args){
7157         if(fn.call(scope || this, args || this) !== false){
7158             var cs = this.childNodes;
7159             for(var i = 0, len = cs.length; i < len; i++) {
7160                 cs[i].cascade(fn, scope, args);
7161             }
7162         }
7163     },
7164
7165     /**
7166      * Interates the child nodes of this node, calling the specified function with each node. The scope (<i>this</i>) of
7167      * function call will be the scope provided or the current node. The arguments to the function
7168      * will be the args provided or the current node. If the function returns false at any point,
7169      * the iteration stops.
7170      * @param {Function} fn The function to call
7171      * @param {Object} scope (optional) The scope of the function (defaults to current node)
7172      * @param {Array} args (optional) The args to call the function with (default to passing the current node)
7173      */
7174     eachChild : function(fn, scope, args){
7175         var cs = this.childNodes;
7176         for(var i = 0, len = cs.length; i < len; i++) {
7177                 if(fn.call(scope || this, args || cs[i]) === false){
7178                     break;
7179                 }
7180         }
7181     },
7182
7183     /**
7184      * Finds the first child that has the attribute with the specified value.
7185      * @param {String} attribute The attribute name
7186      * @param {Mixed} value The value to search for
7187      * @return {Node} The found child or null if none was found
7188      */
7189     findChild : function(attribute, value){
7190         var cs = this.childNodes;
7191         for(var i = 0, len = cs.length; i < len; i++) {
7192                 if(cs[i].attributes[attribute] == value){
7193                     return cs[i];
7194                 }
7195         }
7196         return null;
7197     },
7198
7199     /**
7200      * Finds the first child by a custom function. The child matches if the function passed
7201      * returns true.
7202      * @param {Function} fn
7203      * @param {Object} scope (optional)
7204      * @return {Node} The found child or null if none was found
7205      */
7206     findChildBy : function(fn, scope){
7207         var cs = this.childNodes;
7208         for(var i = 0, len = cs.length; i < len; i++) {
7209                 if(fn.call(scope||cs[i], cs[i]) === true){
7210                     return cs[i];
7211                 }
7212         }
7213         return null;
7214     },
7215
7216     /**
7217      * Sorts this nodes children using the supplied sort function
7218      * @param {Function} fn
7219      * @param {Object} scope (optional)
7220      */
7221     sort : function(fn, scope){
7222         var cs = this.childNodes;
7223         var len = cs.length;
7224         if(len > 0){
7225             var sortFn = scope ? function(){fn.apply(scope, arguments);} : fn;
7226             cs.sort(sortFn);
7227             for(var i = 0; i < len; i++){
7228                 var n = cs[i];
7229                 n.previousSibling = cs[i-1];
7230                 n.nextSibling = cs[i+1];
7231                 if(i == 0){
7232                     this.setFirstChild(n);
7233                 }
7234                 if(i == len-1){
7235                     this.setLastChild(n);
7236                 }
7237             }
7238         }
7239     },
7240
7241     /**
7242      * Returns true if this node is an ancestor (at any point) of the passed node.
7243      * @param {Node} node
7244      * @return {Boolean}
7245      */
7246     contains : function(node){
7247         return node.isAncestor(this);
7248     },
7249
7250     /**
7251      * Returns true if the passed node is an ancestor (at any point) of this node.
7252      * @param {Node} node
7253      * @return {Boolean}
7254      */
7255     isAncestor : function(node){
7256         var p = this.parentNode;
7257         while(p){
7258             if(p == node){
7259                 return true;
7260             }
7261             p = p.parentNode;
7262         }
7263         return false;
7264     },
7265
7266     toString : function(){
7267         return "[Node"+(this.id?" "+this.id:"")+"]";
7268     }
7269 });/*
7270  * Based on:
7271  * Ext JS Library 1.1.1
7272  * Copyright(c) 2006-2007, Ext JS, LLC.
7273  *
7274  * Originally Released Under LGPL - original licence link has changed is not relivant.
7275  *
7276  * Fork - LGPL
7277  * <script type="text/javascript">
7278  */
7279  
7280
7281 /**
7282  * @class Roo.ComponentMgr
7283  * Provides a common registry of all components on a page so that they can be easily accessed by component id (see {@link Roo.getCmp}).
7284  * @singleton
7285  */
7286 Roo.ComponentMgr = function(){
7287     var all = new Roo.util.MixedCollection();
7288
7289     return {
7290         /**
7291          * Registers a component.
7292          * @param {Roo.Component} c The component
7293          */
7294         register : function(c){
7295             all.add(c);
7296         },
7297
7298         /**
7299          * Unregisters a component.
7300          * @param {Roo.Component} c The component
7301          */
7302         unregister : function(c){
7303             all.remove(c);
7304         },
7305
7306         /**
7307          * Returns a component by id
7308          * @param {String} id The component id
7309          */
7310         get : function(id){
7311             return all.get(id);
7312         },
7313
7314         /**
7315          * Registers a function that will be called when a specified component is added to ComponentMgr
7316          * @param {String} id The component id
7317          * @param {Funtction} fn The callback function
7318          * @param {Object} scope The scope of the callback
7319          */
7320         onAvailable : function(id, fn, scope){
7321             all.on("add", function(index, o){
7322                 if(o.id == id){
7323                     fn.call(scope || o, o);
7324                     all.un("add", fn, scope);
7325                 }
7326             });
7327         }
7328     };
7329 }();/*
7330  * Based on:
7331  * Ext JS Library 1.1.1
7332  * Copyright(c) 2006-2007, Ext JS, LLC.
7333  *
7334  * Originally Released Under LGPL - original licence link has changed is not relivant.
7335  *
7336  * Fork - LGPL
7337  * <script type="text/javascript">
7338  */
7339  
7340 /**
7341  * @class Roo.Component
7342  * @extends Roo.util.Observable
7343  * Base class for all major Roo components.  All subclasses of Component can automatically participate in the standard
7344  * Roo component lifecycle of creation, rendering and destruction.  They also have automatic support for basic hide/show
7345  * and enable/disable behavior.  Component allows any subclass to be lazy-rendered into any {@link Roo.Container} and
7346  * to be automatically registered with the {@link Roo.ComponentMgr} so that it can be referenced at any time via {@link Roo.getCmp}.
7347  * All visual components (widgets) that require rendering into a layout should subclass Component.
7348  * @constructor
7349  * @param {Roo.Element/String/Object} config The configuration options.  If an element is passed, it is set as the internal
7350  * 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
7351  * and is used as the component id.  Otherwise, it is assumed to be a standard config object and is applied to the component.
7352  */
7353 Roo.Component = function(config){
7354     config = config || {};
7355     if(config.tagName || config.dom || typeof config == "string"){ // element object
7356         config = {el: config, id: config.id || config};
7357     }
7358     this.initialConfig = config;
7359
7360     Roo.apply(this, config);
7361     this.addEvents({
7362         /**
7363          * @event disable
7364          * Fires after the component is disabled.
7365              * @param {Roo.Component} this
7366              */
7367         disable : true,
7368         /**
7369          * @event enable
7370          * Fires after the component is enabled.
7371              * @param {Roo.Component} this
7372              */
7373         enable : true,
7374         /**
7375          * @event beforeshow
7376          * Fires before the component is shown.  Return false to stop the show.
7377              * @param {Roo.Component} this
7378              */
7379         beforeshow : true,
7380         /**
7381          * @event show
7382          * Fires after the component is shown.
7383              * @param {Roo.Component} this
7384              */
7385         show : true,
7386         /**
7387          * @event beforehide
7388          * Fires before the component is hidden. Return false to stop the hide.
7389              * @param {Roo.Component} this
7390              */
7391         beforehide : true,
7392         /**
7393          * @event hide
7394          * Fires after the component is hidden.
7395              * @param {Roo.Component} this
7396              */
7397         hide : true,
7398         /**
7399          * @event beforerender
7400          * Fires before the component is rendered. Return false to stop the render.
7401              * @param {Roo.Component} this
7402              */
7403         beforerender : true,
7404         /**
7405          * @event render
7406          * Fires after the component is rendered.
7407              * @param {Roo.Component} this
7408              */
7409         render : true,
7410         /**
7411          * @event beforedestroy
7412          * Fires before the component is destroyed. Return false to stop the destroy.
7413              * @param {Roo.Component} this
7414              */
7415         beforedestroy : true,
7416         /**
7417          * @event destroy
7418          * Fires after the component is destroyed.
7419              * @param {Roo.Component} this
7420              */
7421         destroy : true
7422     });
7423     if(!this.id){
7424         this.id = "ext-comp-" + (++Roo.Component.AUTO_ID);
7425     }
7426     Roo.ComponentMgr.register(this);
7427     Roo.Component.superclass.constructor.call(this);
7428     this.initComponent();
7429     if(this.renderTo){ // not supported by all components yet. use at your own risk!
7430         this.render(this.renderTo);
7431         delete this.renderTo;
7432     }
7433 };
7434
7435 /** @private */
7436 Roo.Component.AUTO_ID = 1000;
7437
7438 Roo.extend(Roo.Component, Roo.util.Observable, {
7439     /**
7440      * @scope Roo.Component.prototype
7441      * @type {Boolean}
7442      * true if this component is hidden. Read-only.
7443      */
7444     hidden : false,
7445     /**
7446      * @type {Boolean}
7447      * true if this component is disabled. Read-only.
7448      */
7449     disabled : false,
7450     /**
7451      * @type {Boolean}
7452      * true if this component has been rendered. Read-only.
7453      */
7454     rendered : false,
7455     
7456     /** @cfg {String} disableClass
7457      * CSS class added to the component when it is disabled (defaults to "x-item-disabled").
7458      */
7459     disabledClass : "x-item-disabled",
7460         /** @cfg {Boolean} allowDomMove
7461          * Whether the component can move the Dom node when rendering (defaults to true).
7462          */
7463     allowDomMove : true,
7464     /** @cfg {String} hideMode
7465      * How this component should hidden. Supported values are
7466      * "visibility" (css visibility), "offsets" (negative offset position) and
7467      * "display" (css display) - defaults to "display".
7468      */
7469     hideMode: 'display',
7470
7471     /** @private */
7472     ctype : "Roo.Component",
7473
7474     /**
7475      * @cfg {String} actionMode 
7476      * which property holds the element that used for  hide() / show() / disable() / enable()
7477      * default is 'el' 
7478      */
7479     actionMode : "el",
7480
7481     /** @private */
7482     getActionEl : function(){
7483         return this[this.actionMode];
7484     },
7485
7486     initComponent : Roo.emptyFn,
7487     /**
7488      * If this is a lazy rendering component, render it to its container element.
7489      * @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.
7490      */
7491     render : function(container, position){
7492         if(!this.rendered && this.fireEvent("beforerender", this) !== false){
7493             if(!container && this.el){
7494                 this.el = Roo.get(this.el);
7495                 container = this.el.dom.parentNode;
7496                 this.allowDomMove = false;
7497             }
7498             this.container = Roo.get(container);
7499             this.rendered = true;
7500             if(position !== undefined){
7501                 if(typeof position == 'number'){
7502                     position = this.container.dom.childNodes[position];
7503                 }else{
7504                     position = Roo.getDom(position);
7505                 }
7506             }
7507             this.onRender(this.container, position || null);
7508             if(this.cls){
7509                 this.el.addClass(this.cls);
7510                 delete this.cls;
7511             }
7512             if(this.style){
7513                 this.el.applyStyles(this.style);
7514                 delete this.style;
7515             }
7516             this.fireEvent("render", this);
7517             this.afterRender(this.container);
7518             if(this.hidden){
7519                 this.hide();
7520             }
7521             if(this.disabled){
7522                 this.disable();
7523             }
7524         }
7525         return this;
7526     },
7527
7528     /** @private */
7529     // default function is not really useful
7530     onRender : function(ct, position){
7531         if(this.el){
7532             this.el = Roo.get(this.el);
7533             if(this.allowDomMove !== false){
7534                 ct.dom.insertBefore(this.el.dom, position);
7535             }
7536         }
7537     },
7538
7539     /** @private */
7540     getAutoCreate : function(){
7541         var cfg = typeof this.autoCreate == "object" ?
7542                       this.autoCreate : Roo.apply({}, this.defaultAutoCreate);
7543         if(this.id && !cfg.id){
7544             cfg.id = this.id;
7545         }
7546         return cfg;
7547     },
7548
7549     /** @private */
7550     afterRender : Roo.emptyFn,
7551
7552     /**
7553      * Destroys this component by purging any event listeners, removing the component's element from the DOM,
7554      * removing the component from its {@link Roo.Container} (if applicable) and unregistering it from {@link Roo.ComponentMgr}.
7555      */
7556     destroy : function(){
7557         if(this.fireEvent("beforedestroy", this) !== false){
7558             this.purgeListeners();
7559             this.beforeDestroy();
7560             if(this.rendered){
7561                 this.el.removeAllListeners();
7562                 this.el.remove();
7563                 if(this.actionMode == "container"){
7564                     this.container.remove();
7565                 }
7566             }
7567             this.onDestroy();
7568             Roo.ComponentMgr.unregister(this);
7569             this.fireEvent("destroy", this);
7570         }
7571     },
7572
7573         /** @private */
7574     beforeDestroy : function(){
7575
7576     },
7577
7578         /** @private */
7579         onDestroy : function(){
7580
7581     },
7582
7583     /**
7584      * Returns the underlying {@link Roo.Element}.
7585      * @return {Roo.Element} The element
7586      */
7587     getEl : function(){
7588         return this.el;
7589     },
7590
7591     /**
7592      * Returns the id of this component.
7593      * @return {String}
7594      */
7595     getId : function(){
7596         return this.id;
7597     },
7598
7599     /**
7600      * Try to focus this component.
7601      * @param {Boolean} selectText True to also select the text in this component (if applicable)
7602      * @return {Roo.Component} this
7603      */
7604     focus : function(selectText){
7605         if(this.rendered){
7606             this.el.focus();
7607             if(selectText === true){
7608                 this.el.dom.select();
7609             }
7610         }
7611         return this;
7612     },
7613
7614     /** @private */
7615     blur : function(){
7616         if(this.rendered){
7617             this.el.blur();
7618         }
7619         return this;
7620     },
7621
7622     /**
7623      * Disable this component.
7624      * @return {Roo.Component} this
7625      */
7626     disable : function(){
7627         if(this.rendered){
7628             this.onDisable();
7629         }
7630         this.disabled = true;
7631         this.fireEvent("disable", this);
7632         return this;
7633     },
7634
7635         // private
7636     onDisable : function(){
7637         this.getActionEl().addClass(this.disabledClass);
7638         this.el.dom.disabled = true;
7639     },
7640
7641     /**
7642      * Enable this component.
7643      * @return {Roo.Component} this
7644      */
7645     enable : function(){
7646         if(this.rendered){
7647             this.onEnable();
7648         }
7649         this.disabled = false;
7650         this.fireEvent("enable", this);
7651         return this;
7652     },
7653
7654         // private
7655     onEnable : function(){
7656         this.getActionEl().removeClass(this.disabledClass);
7657         this.el.dom.disabled = false;
7658     },
7659
7660     /**
7661      * Convenience function for setting disabled/enabled by boolean.
7662      * @param {Boolean} disabled
7663      */
7664     setDisabled : function(disabled){
7665         this[disabled ? "disable" : "enable"]();
7666     },
7667
7668     /**
7669      * Show this component.
7670      * @return {Roo.Component} this
7671      */
7672     show: function(){
7673         if(this.fireEvent("beforeshow", this) !== false){
7674             this.hidden = false;
7675             if(this.rendered){
7676                 this.onShow();
7677             }
7678             this.fireEvent("show", this);
7679         }
7680         return this;
7681     },
7682
7683     // private
7684     onShow : function(){
7685         var ae = this.getActionEl();
7686         if(this.hideMode == 'visibility'){
7687             ae.dom.style.visibility = "visible";
7688         }else if(this.hideMode == 'offsets'){
7689             ae.removeClass('x-hidden');
7690         }else{
7691             ae.dom.style.display = "";
7692         }
7693     },
7694
7695     /**
7696      * Hide this component.
7697      * @return {Roo.Component} this
7698      */
7699     hide: function(){
7700         if(this.fireEvent("beforehide", this) !== false){
7701             this.hidden = true;
7702             if(this.rendered){
7703                 this.onHide();
7704             }
7705             this.fireEvent("hide", this);
7706         }
7707         return this;
7708     },
7709
7710     // private
7711     onHide : function(){
7712         var ae = this.getActionEl();
7713         if(this.hideMode == 'visibility'){
7714             ae.dom.style.visibility = "hidden";
7715         }else if(this.hideMode == 'offsets'){
7716             ae.addClass('x-hidden');
7717         }else{
7718             ae.dom.style.display = "none";
7719         }
7720     },
7721
7722     /**
7723      * Convenience function to hide or show this component by boolean.
7724      * @param {Boolean} visible True to show, false to hide
7725      * @return {Roo.Component} this
7726      */
7727     setVisible: function(visible){
7728         if(visible) {
7729             this.show();
7730         }else{
7731             this.hide();
7732         }
7733         return this;
7734     },
7735
7736     /**
7737      * Returns true if this component is visible.
7738      */
7739     isVisible : function(){
7740         return this.getActionEl().isVisible();
7741     },
7742
7743     cloneConfig : function(overrides){
7744         overrides = overrides || {};
7745         var id = overrides.id || Roo.id();
7746         var cfg = Roo.applyIf(overrides, this.initialConfig);
7747         cfg.id = id; // prevent dup id
7748         return new this.constructor(cfg);
7749     }
7750 });/*
7751  * Based on:
7752  * Ext JS Library 1.1.1
7753  * Copyright(c) 2006-2007, Ext JS, LLC.
7754  *
7755  * Originally Released Under LGPL - original licence link has changed is not relivant.
7756  *
7757  * Fork - LGPL
7758  * <script type="text/javascript">
7759  */
7760  (function(){ 
7761 /**
7762  * @class Roo.Layer
7763  * @extends Roo.Element
7764  * An extended {@link Roo.Element} object that supports a shadow and shim, constrain to viewport and
7765  * automatic maintaining of shadow/shim positions.
7766  * @cfg {Boolean} shim False to disable the iframe shim in browsers which need one (defaults to true)
7767  * @cfg {String/Boolean} shadow True to create a shadow element with default class "x-layer-shadow", or
7768  * you can pass a string with a CSS class name. False turns off the shadow.
7769  * @cfg {Object} dh DomHelper object config to create element with (defaults to {tag: "div", cls: "x-layer"}).
7770  * @cfg {Boolean} constrain False to disable constrain to viewport (defaults to true)
7771  * @cfg {String} cls CSS class to add to the element
7772  * @cfg {Number} zindex Starting z-index (defaults to 11000)
7773  * @cfg {Number} shadowOffset Number of pixels to offset the shadow (defaults to 3)
7774  * @constructor
7775  * @param {Object} config An object with config options.
7776  * @param {String/HTMLElement} existingEl (optional) Uses an existing DOM element. If the element is not found it creates it.
7777  */
7778
7779 Roo.Layer = function(config, existingEl){
7780     config = config || {};
7781     var dh = Roo.DomHelper;
7782     var cp = config.parentEl, pel = cp ? Roo.getDom(cp) : document.body;
7783     if(existingEl){
7784         this.dom = Roo.getDom(existingEl);
7785     }
7786     if(!this.dom){
7787         var o = config.dh || {tag: "div", cls: "x-layer"};
7788         this.dom = dh.append(pel, o);
7789     }
7790     if(config.cls){
7791         this.addClass(config.cls);
7792     }
7793     this.constrain = config.constrain !== false;
7794     this.visibilityMode = Roo.Element.VISIBILITY;
7795     if(config.id){
7796         this.id = this.dom.id = config.id;
7797     }else{
7798         this.id = Roo.id(this.dom);
7799     }
7800     this.zindex = config.zindex || this.getZIndex();
7801     this.position("absolute", this.zindex);
7802     if(config.shadow){
7803         this.shadowOffset = config.shadowOffset || 4;
7804         this.shadow = new Roo.Shadow({
7805             offset : this.shadowOffset,
7806             mode : config.shadow
7807         });
7808     }else{
7809         this.shadowOffset = 0;
7810     }
7811     this.useShim = config.shim !== false && Roo.useShims;
7812     this.useDisplay = config.useDisplay;
7813     this.hide();
7814 };
7815
7816 var supr = Roo.Element.prototype;
7817
7818 // shims are shared among layer to keep from having 100 iframes
7819 var shims = [];
7820
7821 Roo.extend(Roo.Layer, Roo.Element, {
7822
7823     getZIndex : function(){
7824         return this.zindex || parseInt(this.getStyle("z-index"), 10) || 11000;
7825     },
7826
7827     getShim : function(){
7828         if(!this.useShim){
7829             return null;
7830         }
7831         if(this.shim){
7832             return this.shim;
7833         }
7834         var shim = shims.shift();
7835         if(!shim){
7836             shim = this.createShim();
7837             shim.enableDisplayMode('block');
7838             shim.dom.style.display = 'none';
7839             shim.dom.style.visibility = 'visible';
7840         }
7841         var pn = this.dom.parentNode;
7842         if(shim.dom.parentNode != pn){
7843             pn.insertBefore(shim.dom, this.dom);
7844         }
7845         shim.setStyle('z-index', this.getZIndex()-2);
7846         this.shim = shim;
7847         return shim;
7848     },
7849
7850     hideShim : function(){
7851         if(this.shim){
7852             this.shim.setDisplayed(false);
7853             shims.push(this.shim);
7854             delete this.shim;
7855         }
7856     },
7857
7858     disableShadow : function(){
7859         if(this.shadow){
7860             this.shadowDisabled = true;
7861             this.shadow.hide();
7862             this.lastShadowOffset = this.shadowOffset;
7863             this.shadowOffset = 0;
7864         }
7865     },
7866
7867     enableShadow : function(show){
7868         if(this.shadow){
7869             this.shadowDisabled = false;
7870             this.shadowOffset = this.lastShadowOffset;
7871             delete this.lastShadowOffset;
7872             if(show){
7873                 this.sync(true);
7874             }
7875         }
7876     },
7877
7878     // private
7879     // this code can execute repeatedly in milliseconds (i.e. during a drag) so
7880     // code size was sacrificed for effeciency (e.g. no getBox/setBox, no XY calls)
7881     sync : function(doShow){
7882         var sw = this.shadow;
7883         if(!this.updating && this.isVisible() && (sw || this.useShim)){
7884             var sh = this.getShim();
7885
7886             var w = this.getWidth(),
7887                 h = this.getHeight();
7888
7889             var l = this.getLeft(true),
7890                 t = this.getTop(true);
7891
7892             if(sw && !this.shadowDisabled){
7893                 if(doShow && !sw.isVisible()){
7894                     sw.show(this);
7895                 }else{
7896                     sw.realign(l, t, w, h);
7897                 }
7898                 if(sh){
7899                     if(doShow){
7900                        sh.show();
7901                     }
7902                     // fit the shim behind the shadow, so it is shimmed too
7903                     var a = sw.adjusts, s = sh.dom.style;
7904                     s.left = (Math.min(l, l+a.l))+"px";
7905                     s.top = (Math.min(t, t+a.t))+"px";
7906                     s.width = (w+a.w)+"px";
7907                     s.height = (h+a.h)+"px";
7908                 }
7909             }else if(sh){
7910                 if(doShow){
7911                    sh.show();
7912                 }
7913                 sh.setSize(w, h);
7914                 sh.setLeftTop(l, t);
7915             }
7916             
7917         }
7918     },
7919
7920     // private
7921     destroy : function(){
7922         this.hideShim();
7923         if(this.shadow){
7924             this.shadow.hide();
7925         }
7926         this.removeAllListeners();
7927         var pn = this.dom.parentNode;
7928         if(pn){
7929             pn.removeChild(this.dom);
7930         }
7931         Roo.Element.uncache(this.id);
7932     },
7933
7934     remove : function(){
7935         this.destroy();
7936     },
7937
7938     // private
7939     beginUpdate : function(){
7940         this.updating = true;
7941     },
7942
7943     // private
7944     endUpdate : function(){
7945         this.updating = false;
7946         this.sync(true);
7947     },
7948
7949     // private
7950     hideUnders : function(negOffset){
7951         if(this.shadow){
7952             this.shadow.hide();
7953         }
7954         this.hideShim();
7955     },
7956
7957     // private
7958     constrainXY : function(){
7959         if(this.constrain){
7960             var vw = Roo.lib.Dom.getViewWidth(),
7961                 vh = Roo.lib.Dom.getViewHeight();
7962             var s = Roo.get(document).getScroll();
7963
7964             var xy = this.getXY();
7965             var x = xy[0], y = xy[1];   
7966             var w = this.dom.offsetWidth+this.shadowOffset, h = this.dom.offsetHeight+this.shadowOffset;
7967             // only move it if it needs it
7968             var moved = false;
7969             // first validate right/bottom
7970             if((x + w) > vw+s.left){
7971                 x = vw - w - this.shadowOffset;
7972                 moved = true;
7973             }
7974             if((y + h) > vh+s.top){
7975                 y = vh - h - this.shadowOffset;
7976                 moved = true;
7977             }
7978             // then make sure top/left isn't negative
7979             if(x < s.left){
7980                 x = s.left;
7981                 moved = true;
7982             }
7983             if(y < s.top){
7984                 y = s.top;
7985                 moved = true;
7986             }
7987             if(moved){
7988                 if(this.avoidY){
7989                     var ay = this.avoidY;
7990                     if(y <= ay && (y+h) >= ay){
7991                         y = ay-h-5;   
7992                     }
7993                 }
7994                 xy = [x, y];
7995                 this.storeXY(xy);
7996                 supr.setXY.call(this, xy);
7997                 this.sync();
7998             }
7999         }
8000     },
8001
8002     isVisible : function(){
8003         return this.visible;    
8004     },
8005
8006     // private
8007     showAction : function(){
8008         this.visible = true; // track visibility to prevent getStyle calls
8009         if(this.useDisplay === true){
8010             this.setDisplayed("");
8011         }else if(this.lastXY){
8012             supr.setXY.call(this, this.lastXY);
8013         }else if(this.lastLT){
8014             supr.setLeftTop.call(this, this.lastLT[0], this.lastLT[1]);
8015         }
8016     },
8017
8018     // private
8019     hideAction : function(){
8020         this.visible = false;
8021         if(this.useDisplay === true){
8022             this.setDisplayed(false);
8023         }else{
8024             this.setLeftTop(-10000,-10000);
8025         }
8026     },
8027
8028     // overridden Element method
8029     setVisible : function(v, a, d, c, e){
8030         if(v){
8031             this.showAction();
8032         }
8033         if(a && v){
8034             var cb = function(){
8035                 this.sync(true);
8036                 if(c){
8037                     c();
8038                 }
8039             }.createDelegate(this);
8040             supr.setVisible.call(this, true, true, d, cb, e);
8041         }else{
8042             if(!v){
8043                 this.hideUnders(true);
8044             }
8045             var cb = c;
8046             if(a){
8047                 cb = function(){
8048                     this.hideAction();
8049                     if(c){
8050                         c();
8051                     }
8052                 }.createDelegate(this);
8053             }
8054             supr.setVisible.call(this, v, a, d, cb, e);
8055             if(v){
8056                 this.sync(true);
8057             }else if(!a){
8058                 this.hideAction();
8059             }
8060         }
8061     },
8062
8063     storeXY : function(xy){
8064         delete this.lastLT;
8065         this.lastXY = xy;
8066     },
8067
8068     storeLeftTop : function(left, top){
8069         delete this.lastXY;
8070         this.lastLT = [left, top];
8071     },
8072
8073     // private
8074     beforeFx : function(){
8075         this.beforeAction();
8076         return Roo.Layer.superclass.beforeFx.apply(this, arguments);
8077     },
8078
8079     // private
8080     afterFx : function(){
8081         Roo.Layer.superclass.afterFx.apply(this, arguments);
8082         this.sync(this.isVisible());
8083     },
8084
8085     // private
8086     beforeAction : function(){
8087         if(!this.updating && this.shadow){
8088             this.shadow.hide();
8089         }
8090     },
8091
8092     // overridden Element method
8093     setLeft : function(left){
8094         this.storeLeftTop(left, this.getTop(true));
8095         supr.setLeft.apply(this, arguments);
8096         this.sync();
8097     },
8098
8099     setTop : function(top){
8100         this.storeLeftTop(this.getLeft(true), top);
8101         supr.setTop.apply(this, arguments);
8102         this.sync();
8103     },
8104
8105     setLeftTop : function(left, top){
8106         this.storeLeftTop(left, top);
8107         supr.setLeftTop.apply(this, arguments);
8108         this.sync();
8109     },
8110
8111     setXY : function(xy, a, d, c, e){
8112         this.fixDisplay();
8113         this.beforeAction();
8114         this.storeXY(xy);
8115         var cb = this.createCB(c);
8116         supr.setXY.call(this, xy, a, d, cb, e);
8117         if(!a){
8118             cb();
8119         }
8120     },
8121
8122     // private
8123     createCB : function(c){
8124         var el = this;
8125         return function(){
8126             el.constrainXY();
8127             el.sync(true);
8128             if(c){
8129                 c();
8130             }
8131         };
8132     },
8133
8134     // overridden Element method
8135     setX : function(x, a, d, c, e){
8136         this.setXY([x, this.getY()], a, d, c, e);
8137     },
8138
8139     // overridden Element method
8140     setY : function(y, a, d, c, e){
8141         this.setXY([this.getX(), y], a, d, c, e);
8142     },
8143
8144     // overridden Element method
8145     setSize : function(w, h, a, d, c, e){
8146         this.beforeAction();
8147         var cb = this.createCB(c);
8148         supr.setSize.call(this, w, h, a, d, cb, e);
8149         if(!a){
8150             cb();
8151         }
8152     },
8153
8154     // overridden Element method
8155     setWidth : function(w, a, d, c, e){
8156         this.beforeAction();
8157         var cb = this.createCB(c);
8158         supr.setWidth.call(this, w, a, d, cb, e);
8159         if(!a){
8160             cb();
8161         }
8162     },
8163
8164     // overridden Element method
8165     setHeight : function(h, a, d, c, e){
8166         this.beforeAction();
8167         var cb = this.createCB(c);
8168         supr.setHeight.call(this, h, a, d, cb, e);
8169         if(!a){
8170             cb();
8171         }
8172     },
8173
8174     // overridden Element method
8175     setBounds : function(x, y, w, h, a, d, c, e){
8176         this.beforeAction();
8177         var cb = this.createCB(c);
8178         if(!a){
8179             this.storeXY([x, y]);
8180             supr.setXY.call(this, [x, y]);
8181             supr.setSize.call(this, w, h, a, d, cb, e);
8182             cb();
8183         }else{
8184             supr.setBounds.call(this, x, y, w, h, a, d, cb, e);
8185         }
8186         return this;
8187     },
8188     
8189     /**
8190      * Sets the z-index of this layer and adjusts any shadow and shim z-indexes. The layer z-index is automatically
8191      * incremented by two more than the value passed in so that it always shows above any shadow or shim (the shadow
8192      * element, if any, will be assigned z-index + 1, and the shim element, if any, will be assigned the unmodified z-index).
8193      * @param {Number} zindex The new z-index to set
8194      * @return {this} The Layer
8195      */
8196     setZIndex : function(zindex){
8197         this.zindex = zindex;
8198         this.setStyle("z-index", zindex + 2);
8199         if(this.shadow){
8200             this.shadow.setZIndex(zindex + 1);
8201         }
8202         if(this.shim){
8203             this.shim.setStyle("z-index", zindex);
8204         }
8205     }
8206 });
8207 })();/*
8208  * Based on:
8209  * Ext JS Library 1.1.1
8210  * Copyright(c) 2006-2007, Ext JS, LLC.
8211  *
8212  * Originally Released Under LGPL - original licence link has changed is not relivant.
8213  *
8214  * Fork - LGPL
8215  * <script type="text/javascript">
8216  */
8217
8218
8219 /**
8220  * @class Roo.Shadow
8221  * Simple class that can provide a shadow effect for any element.  Note that the element MUST be absolutely positioned,
8222  * and the shadow does not provide any shimming.  This should be used only in simple cases -- for more advanced
8223  * functionality that can also provide the same shadow effect, see the {@link Roo.Layer} class.
8224  * @constructor
8225  * Create a new Shadow
8226  * @param {Object} config The config object
8227  */
8228 Roo.Shadow = function(config){
8229     Roo.apply(this, config);
8230     if(typeof this.mode != "string"){
8231         this.mode = this.defaultMode;
8232     }
8233     var o = this.offset, a = {h: 0};
8234     var rad = Math.floor(this.offset/2);
8235     switch(this.mode.toLowerCase()){ // all this hideous nonsense calculates the various offsets for shadows
8236         case "drop":
8237             a.w = 0;
8238             a.l = a.t = o;
8239             a.t -= 1;
8240             if(Roo.isIE){
8241                 a.l -= this.offset + rad;
8242                 a.t -= this.offset + rad;
8243                 a.w -= rad;
8244                 a.h -= rad;
8245                 a.t += 1;
8246             }
8247         break;
8248         case "sides":
8249             a.w = (o*2);
8250             a.l = -o;
8251             a.t = o-1;
8252             if(Roo.isIE){
8253                 a.l -= (this.offset - rad);
8254                 a.t -= this.offset + rad;
8255                 a.l += 1;
8256                 a.w -= (this.offset - rad)*2;
8257                 a.w -= rad + 1;
8258                 a.h -= 1;
8259             }
8260         break;
8261         case "frame":
8262             a.w = a.h = (o*2);
8263             a.l = a.t = -o;
8264             a.t += 1;
8265             a.h -= 2;
8266             if(Roo.isIE){
8267                 a.l -= (this.offset - rad);
8268                 a.t -= (this.offset - rad);
8269                 a.l += 1;
8270                 a.w -= (this.offset + rad + 1);
8271                 a.h -= (this.offset + rad);
8272                 a.h += 1;
8273             }
8274         break;
8275     };
8276
8277     this.adjusts = a;
8278 };
8279
8280 Roo.Shadow.prototype = {
8281     /**
8282      * @cfg {String} mode
8283      * The shadow display mode.  Supports the following options:<br />
8284      * sides: Shadow displays on both sides and bottom only<br />
8285      * frame: Shadow displays equally on all four sides<br />
8286      * drop: Traditional bottom-right drop shadow (default)
8287      */
8288     /**
8289      * @cfg {String} offset
8290      * The number of pixels to offset the shadow from the element (defaults to 4)
8291      */
8292     offset: 4,
8293
8294     // private
8295     defaultMode: "drop",
8296
8297     /**
8298      * Displays the shadow under the target element
8299      * @param {String/HTMLElement/Element} targetEl The id or element under which the shadow should display
8300      */
8301     show : function(target){
8302         target = Roo.get(target);
8303         if(!this.el){
8304             this.el = Roo.Shadow.Pool.pull();
8305             if(this.el.dom.nextSibling != target.dom){
8306                 this.el.insertBefore(target);
8307             }
8308         }
8309         this.el.setStyle("z-index", this.zIndex || parseInt(target.getStyle("z-index"), 10)-1);
8310         if(Roo.isIE){
8311             this.el.dom.style.filter="progid:DXImageTransform.Microsoft.alpha(opacity=50) progid:DXImageTransform.Microsoft.Blur(pixelradius="+(this.offset)+")";
8312         }
8313         this.realign(
8314             target.getLeft(true),
8315             target.getTop(true),
8316             target.getWidth(),
8317             target.getHeight()
8318         );
8319         this.el.dom.style.display = "block";
8320     },
8321
8322     /**
8323      * Returns true if the shadow is visible, else false
8324      */
8325     isVisible : function(){
8326         return this.el ? true : false;  
8327     },
8328
8329     /**
8330      * Direct alignment when values are already available. Show must be called at least once before
8331      * calling this method to ensure it is initialized.
8332      * @param {Number} left The target element left position
8333      * @param {Number} top The target element top position
8334      * @param {Number} width The target element width
8335      * @param {Number} height The target element height
8336      */
8337     realign : function(l, t, w, h){
8338         if(!this.el){
8339             return;
8340         }
8341         var a = this.adjusts, d = this.el.dom, s = d.style;
8342         var iea = 0;
8343         s.left = (l+a.l)+"px";
8344         s.top = (t+a.t)+"px";
8345         var sw = (w+a.w), sh = (h+a.h), sws = sw +"px", shs = sh + "px";
8346  
8347         if(s.width != sws || s.height != shs){
8348             s.width = sws;
8349             s.height = shs;
8350             if(!Roo.isIE){
8351                 var cn = d.childNodes;
8352                 var sww = Math.max(0, (sw-12))+"px";
8353                 cn[0].childNodes[1].style.width = sww;
8354                 cn[1].childNodes[1].style.width = sww;
8355                 cn[2].childNodes[1].style.width = sww;
8356                 cn[1].style.height = Math.max(0, (sh-12))+"px";
8357             }
8358         }
8359     },
8360
8361     /**
8362      * Hides this shadow
8363      */
8364     hide : function(){
8365         if(this.el){
8366             this.el.dom.style.display = "none";
8367             Roo.Shadow.Pool.push(this.el);
8368             delete this.el;
8369         }
8370     },
8371
8372     /**
8373      * Adjust the z-index of this shadow
8374      * @param {Number} zindex The new z-index
8375      */
8376     setZIndex : function(z){
8377         this.zIndex = z;
8378         if(this.el){
8379             this.el.setStyle("z-index", z);
8380         }
8381     }
8382 };
8383
8384 // Private utility class that manages the internal Shadow cache
8385 Roo.Shadow.Pool = function(){
8386     var p = [];
8387     var markup = Roo.isIE ?
8388                  '<div class="x-ie-shadow"></div>' :
8389                  '<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>';
8390     return {
8391         pull : function(){
8392             var sh = p.shift();
8393             if(!sh){
8394                 sh = Roo.get(Roo.DomHelper.insertHtml("beforeBegin", document.body.firstChild, markup));
8395                 sh.autoBoxAdjust = false;
8396             }
8397             return sh;
8398         },
8399
8400         push : function(sh){
8401             p.push(sh);
8402         }
8403     };
8404 }();/*
8405  * Based on:
8406  * Ext JS Library 1.1.1
8407  * Copyright(c) 2006-2007, Ext JS, LLC.
8408  *
8409  * Originally Released Under LGPL - original licence link has changed is not relivant.
8410  *
8411  * Fork - LGPL
8412  * <script type="text/javascript">
8413  */
8414
8415 /**
8416  * @class Roo.BoxComponent
8417  * @extends Roo.Component
8418  * Base class for any visual {@link Roo.Component} that uses a box container.  BoxComponent provides automatic box
8419  * model adjustments for sizing and positioning and will work correctly withnin the Component rendering model.  All
8420  * container classes should subclass BoxComponent so that they will work consistently when nested within other Ext
8421  * layout containers.
8422  * @constructor
8423  * @param {Roo.Element/String/Object} config The configuration options.
8424  */
8425 Roo.BoxComponent = function(config){
8426     Roo.Component.call(this, config);
8427     this.addEvents({
8428         /**
8429          * @event resize
8430          * Fires after the component is resized.
8431              * @param {Roo.Component} this
8432              * @param {Number} adjWidth The box-adjusted width that was set
8433              * @param {Number} adjHeight The box-adjusted height that was set
8434              * @param {Number} rawWidth The width that was originally specified
8435              * @param {Number} rawHeight The height that was originally specified
8436              */
8437         resize : true,
8438         /**
8439          * @event move
8440          * Fires after the component is moved.
8441              * @param {Roo.Component} this
8442              * @param {Number} x The new x position
8443              * @param {Number} y The new y position
8444              */
8445         move : true
8446     });
8447 };
8448
8449 Roo.extend(Roo.BoxComponent, Roo.Component, {
8450     // private, set in afterRender to signify that the component has been rendered
8451     boxReady : false,
8452     // private, used to defer height settings to subclasses
8453     deferHeight: false,
8454     /** @cfg {Number} width
8455      * width (optional) size of component
8456      */
8457      /** @cfg {Number} height
8458      * height (optional) size of component
8459      */
8460      
8461     /**
8462      * Sets the width and height of the component.  This method fires the resize event.  This method can accept
8463      * either width and height as separate numeric arguments, or you can pass a size object like {width:10, height:20}.
8464      * @param {Number/Object} width The new width to set, or a size object in the format {width, height}
8465      * @param {Number} height The new height to set (not required if a size object is passed as the first arg)
8466      * @return {Roo.BoxComponent} this
8467      */
8468     setSize : function(w, h){
8469         // support for standard size objects
8470         if(typeof w == 'object'){
8471             h = w.height;
8472             w = w.width;
8473         }
8474         // not rendered
8475         if(!this.boxReady){
8476             this.width = w;
8477             this.height = h;
8478             return this;
8479         }
8480
8481         // prevent recalcs when not needed
8482         if(this.lastSize && this.lastSize.width == w && this.lastSize.height == h){
8483             return this;
8484         }
8485         this.lastSize = {width: w, height: h};
8486
8487         var adj = this.adjustSize(w, h);
8488         var aw = adj.width, ah = adj.height;
8489         if(aw !== undefined || ah !== undefined){ // this code is nasty but performs better with floaters
8490             var rz = this.getResizeEl();
8491             if(!this.deferHeight && aw !== undefined && ah !== undefined){
8492                 rz.setSize(aw, ah);
8493             }else if(!this.deferHeight && ah !== undefined){
8494                 rz.setHeight(ah);
8495             }else if(aw !== undefined){
8496                 rz.setWidth(aw);
8497             }
8498             this.onResize(aw, ah, w, h);
8499             this.fireEvent('resize', this, aw, ah, w, h);
8500         }
8501         return this;
8502     },
8503
8504     /**
8505      * Gets the current size of the component's underlying element.
8506      * @return {Object} An object containing the element's size {width: (element width), height: (element height)}
8507      */
8508     getSize : function(){
8509         return this.el.getSize();
8510     },
8511
8512     /**
8513      * Gets the current XY position of the component's underlying element.
8514      * @param {Boolean} local (optional) If true the element's left and top are returned instead of page XY (defaults to false)
8515      * @return {Array} The XY position of the element (e.g., [100, 200])
8516      */
8517     getPosition : function(local){
8518         if(local === true){
8519             return [this.el.getLeft(true), this.el.getTop(true)];
8520         }
8521         return this.xy || this.el.getXY();
8522     },
8523
8524     /**
8525      * Gets the current box measurements of the component's underlying element.
8526      * @param {Boolean} local (optional) If true the element's left and top are returned instead of page XY (defaults to false)
8527      * @returns {Object} box An object in the format {x, y, width, height}
8528      */
8529     getBox : function(local){
8530         var s = this.el.getSize();
8531         if(local){
8532             s.x = this.el.getLeft(true);
8533             s.y = this.el.getTop(true);
8534         }else{
8535             var xy = this.xy || this.el.getXY();
8536             s.x = xy[0];
8537             s.y = xy[1];
8538         }
8539         return s;
8540     },
8541
8542     /**
8543      * Sets the current box measurements of the component's underlying element.
8544      * @param {Object} box An object in the format {x, y, width, height}
8545      * @returns {Roo.BoxComponent} this
8546      */
8547     updateBox : function(box){
8548         this.setSize(box.width, box.height);
8549         this.setPagePosition(box.x, box.y);
8550         return this;
8551     },
8552
8553     // protected
8554     getResizeEl : function(){
8555         return this.resizeEl || this.el;
8556     },
8557
8558     // protected
8559     getPositionEl : function(){
8560         return this.positionEl || this.el;
8561     },
8562
8563     /**
8564      * Sets the left and top of the component.  To set the page XY position instead, use {@link #setPagePosition}.
8565      * This method fires the move event.
8566      * @param {Number} left The new left
8567      * @param {Number} top The new top
8568      * @returns {Roo.BoxComponent} this
8569      */
8570     setPosition : function(x, y){
8571         this.x = x;
8572         this.y = y;
8573         if(!this.boxReady){
8574             return this;
8575         }
8576         var adj = this.adjustPosition(x, y);
8577         var ax = adj.x, ay = adj.y;
8578
8579         var el = this.getPositionEl();
8580         if(ax !== undefined || ay !== undefined){
8581             if(ax !== undefined && ay !== undefined){
8582                 el.setLeftTop(ax, ay);
8583             }else if(ax !== undefined){
8584                 el.setLeft(ax);
8585             }else if(ay !== undefined){
8586                 el.setTop(ay);
8587             }
8588             this.onPosition(ax, ay);
8589             this.fireEvent('move', this, ax, ay);
8590         }
8591         return this;
8592     },
8593
8594     /**
8595      * Sets the page XY position of the component.  To set the left and top instead, use {@link #setPosition}.
8596      * This method fires the move event.
8597      * @param {Number} x The new x position
8598      * @param {Number} y The new y position
8599      * @returns {Roo.BoxComponent} this
8600      */
8601     setPagePosition : function(x, y){
8602         this.pageX = x;
8603         this.pageY = y;
8604         if(!this.boxReady){
8605             return;
8606         }
8607         if(x === undefined || y === undefined){ // cannot translate undefined points
8608             return;
8609         }
8610         var p = this.el.translatePoints(x, y);
8611         this.setPosition(p.left, p.top);
8612         return this;
8613     },
8614
8615     // private
8616     onRender : function(ct, position){
8617         Roo.BoxComponent.superclass.onRender.call(this, ct, position);
8618         if(this.resizeEl){
8619             this.resizeEl = Roo.get(this.resizeEl);
8620         }
8621         if(this.positionEl){
8622             this.positionEl = Roo.get(this.positionEl);
8623         }
8624     },
8625
8626     // private
8627     afterRender : function(){
8628         Roo.BoxComponent.superclass.afterRender.call(this);
8629         this.boxReady = true;
8630         this.setSize(this.width, this.height);
8631         if(this.x || this.y){
8632             this.setPosition(this.x, this.y);
8633         }
8634         if(this.pageX || this.pageY){
8635             this.setPagePosition(this.pageX, this.pageY);
8636         }
8637     },
8638
8639     /**
8640      * Force the component's size to recalculate based on the underlying element's current height and width.
8641      * @returns {Roo.BoxComponent} this
8642      */
8643     syncSize : function(){
8644         delete this.lastSize;
8645         this.setSize(this.el.getWidth(), this.el.getHeight());
8646         return this;
8647     },
8648
8649     /**
8650      * Called after the component is resized, this method is empty by default but can be implemented by any
8651      * subclass that needs to perform custom logic after a resize occurs.
8652      * @param {Number} adjWidth The box-adjusted width that was set
8653      * @param {Number} adjHeight The box-adjusted height that was set
8654      * @param {Number} rawWidth The width that was originally specified
8655      * @param {Number} rawHeight The height that was originally specified
8656      */
8657     onResize : function(adjWidth, adjHeight, rawWidth, rawHeight){
8658
8659     },
8660
8661     /**
8662      * Called after the component is moved, this method is empty by default but can be implemented by any
8663      * subclass that needs to perform custom logic after a move occurs.
8664      * @param {Number} x The new x position
8665      * @param {Number} y The new y position
8666      */
8667     onPosition : function(x, y){
8668
8669     },
8670
8671     // private
8672     adjustSize : function(w, h){
8673         if(this.autoWidth){
8674             w = 'auto';
8675         }
8676         if(this.autoHeight){
8677             h = 'auto';
8678         }
8679         return {width : w, height: h};
8680     },
8681
8682     // private
8683     adjustPosition : function(x, y){
8684         return {x : x, y: y};
8685     }
8686 });/*
8687  * Based on:
8688  * Ext JS Library 1.1.1
8689  * Copyright(c) 2006-2007, Ext JS, LLC.
8690  *
8691  * Originally Released Under LGPL - original licence link has changed is not relivant.
8692  *
8693  * Fork - LGPL
8694  * <script type="text/javascript">
8695  */
8696
8697
8698 /**
8699  * @class Roo.SplitBar
8700  * @extends Roo.util.Observable
8701  * Creates draggable splitter bar functionality from two elements (element to be dragged and element to be resized).
8702  * <br><br>
8703  * Usage:
8704  * <pre><code>
8705 var split = new Roo.SplitBar("elementToDrag", "elementToSize",
8706                    Roo.SplitBar.HORIZONTAL, Roo.SplitBar.LEFT);
8707 split.setAdapter(new Roo.SplitBar.AbsoluteLayoutAdapter("container"));
8708 split.minSize = 100;
8709 split.maxSize = 600;
8710 split.animate = true;
8711 split.on('moved', splitterMoved);
8712 </code></pre>
8713  * @constructor
8714  * Create a new SplitBar
8715  * @param {String/HTMLElement/Roo.Element} dragElement The element to be dragged and act as the SplitBar. 
8716  * @param {String/HTMLElement/Roo.Element} resizingElement The element to be resized based on where the SplitBar element is dragged 
8717  * @param {Number} orientation (optional) Either Roo.SplitBar.HORIZONTAL or Roo.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
8718  * @param {Number} placement (optional) Either Roo.SplitBar.LEFT or Roo.SplitBar.RIGHT for horizontal or  
8719                         Roo.SplitBar.TOP or Roo.SplitBar.BOTTOM for vertical. (By default, this is determined automatically by the initial
8720                         position of the SplitBar).
8721  */
8722 Roo.SplitBar = function(dragElement, resizingElement, orientation, placement, existingProxy){
8723     
8724     /** @private */
8725     this.el = Roo.get(dragElement, true);
8726     this.el.dom.unselectable = "on";
8727     /** @private */
8728     this.resizingEl = Roo.get(resizingElement, true);
8729
8730     /**
8731      * @private
8732      * The orientation of the split. Either Roo.SplitBar.HORIZONTAL or Roo.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
8733      * Note: If this is changed after creating the SplitBar, the placement property must be manually updated
8734      * @type Number
8735      */
8736     this.orientation = orientation || Roo.SplitBar.HORIZONTAL;
8737     
8738     /**
8739      * The minimum size of the resizing element. (Defaults to 0)
8740      * @type Number
8741      */
8742     this.minSize = 0;
8743     
8744     /**
8745      * The maximum size of the resizing element. (Defaults to 2000)
8746      * @type Number
8747      */
8748     this.maxSize = 2000;
8749     
8750     /**
8751      * Whether to animate the transition to the new size
8752      * @type Boolean
8753      */
8754     this.animate = false;
8755     
8756     /**
8757      * Whether to create a transparent shim that overlays the page when dragging, enables dragging across iframes.
8758      * @type Boolean
8759      */
8760     this.useShim = false;
8761     
8762     /** @private */
8763     this.shim = null;
8764     
8765     if(!existingProxy){
8766         /** @private */
8767         this.proxy = Roo.SplitBar.createProxy(this.orientation);
8768     }else{
8769         this.proxy = Roo.get(existingProxy).dom;
8770     }
8771     /** @private */
8772     this.dd = new Roo.dd.DDProxy(this.el.dom.id, "XSplitBars", {dragElId : this.proxy.id});
8773     
8774     /** @private */
8775     this.dd.b4StartDrag = this.onStartProxyDrag.createDelegate(this);
8776     
8777     /** @private */
8778     this.dd.endDrag = this.onEndProxyDrag.createDelegate(this);
8779     
8780     /** @private */
8781     this.dragSpecs = {};
8782     
8783     /**
8784      * @private The adapter to use to positon and resize elements
8785      */
8786     this.adapter = new Roo.SplitBar.BasicLayoutAdapter();
8787     this.adapter.init(this);
8788     
8789     if(this.orientation == Roo.SplitBar.HORIZONTAL){
8790         /** @private */
8791         this.placement = placement || (this.el.getX() > this.resizingEl.getX() ? Roo.SplitBar.LEFT : Roo.SplitBar.RIGHT);
8792         this.el.addClass("x-splitbar-h");
8793     }else{
8794         /** @private */
8795         this.placement = placement || (this.el.getY() > this.resizingEl.getY() ? Roo.SplitBar.TOP : Roo.SplitBar.BOTTOM);
8796         this.el.addClass("x-splitbar-v");
8797     }
8798     
8799     this.addEvents({
8800         /**
8801          * @event resize
8802          * Fires when the splitter is moved (alias for {@link #event-moved})
8803          * @param {Roo.SplitBar} this
8804          * @param {Number} newSize the new width or height
8805          */
8806         "resize" : true,
8807         /**
8808          * @event moved
8809          * Fires when the splitter is moved
8810          * @param {Roo.SplitBar} this
8811          * @param {Number} newSize the new width or height
8812          */
8813         "moved" : true,
8814         /**
8815          * @event beforeresize
8816          * Fires before the splitter is dragged
8817          * @param {Roo.SplitBar} this
8818          */
8819         "beforeresize" : true,
8820
8821         "beforeapply" : true
8822     });
8823
8824     Roo.util.Observable.call(this);
8825 };
8826
8827 Roo.extend(Roo.SplitBar, Roo.util.Observable, {
8828     onStartProxyDrag : function(x, y){
8829         this.fireEvent("beforeresize", this);
8830         if(!this.overlay){
8831             var o = Roo.DomHelper.insertFirst(document.body,  {cls: "x-drag-overlay", html: "&#160;"}, true);
8832             o.unselectable();
8833             o.enableDisplayMode("block");
8834             // all splitbars share the same overlay
8835             Roo.SplitBar.prototype.overlay = o;
8836         }
8837         this.overlay.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
8838         this.overlay.show();
8839         Roo.get(this.proxy).setDisplayed("block");
8840         var size = this.adapter.getElementSize(this);
8841         this.activeMinSize = this.getMinimumSize();;
8842         this.activeMaxSize = this.getMaximumSize();;
8843         var c1 = size - this.activeMinSize;
8844         var c2 = Math.max(this.activeMaxSize - size, 0);
8845         if(this.orientation == Roo.SplitBar.HORIZONTAL){
8846             this.dd.resetConstraints();
8847             this.dd.setXConstraint(
8848                 this.placement == Roo.SplitBar.LEFT ? c1 : c2, 
8849                 this.placement == Roo.SplitBar.LEFT ? c2 : c1
8850             );
8851             this.dd.setYConstraint(0, 0);
8852         }else{
8853             this.dd.resetConstraints();
8854             this.dd.setXConstraint(0, 0);
8855             this.dd.setYConstraint(
8856                 this.placement == Roo.SplitBar.TOP ? c1 : c2, 
8857                 this.placement == Roo.SplitBar.TOP ? c2 : c1
8858             );
8859          }
8860         this.dragSpecs.startSize = size;
8861         this.dragSpecs.startPoint = [x, y];
8862         Roo.dd.DDProxy.prototype.b4StartDrag.call(this.dd, x, y);
8863     },
8864     
8865     /** 
8866      * @private Called after the drag operation by the DDProxy
8867      */
8868     onEndProxyDrag : function(e){
8869         Roo.get(this.proxy).setDisplayed(false);
8870         var endPoint = Roo.lib.Event.getXY(e);
8871         if(this.overlay){
8872             this.overlay.hide();
8873         }
8874         var newSize;
8875         if(this.orientation == Roo.SplitBar.HORIZONTAL){
8876             newSize = this.dragSpecs.startSize + 
8877                 (this.placement == Roo.SplitBar.LEFT ?
8878                     endPoint[0] - this.dragSpecs.startPoint[0] :
8879                     this.dragSpecs.startPoint[0] - endPoint[0]
8880                 );
8881         }else{
8882             newSize = this.dragSpecs.startSize + 
8883                 (this.placement == Roo.SplitBar.TOP ?
8884                     endPoint[1] - this.dragSpecs.startPoint[1] :
8885                     this.dragSpecs.startPoint[1] - endPoint[1]
8886                 );
8887         }
8888         newSize = Math.min(Math.max(newSize, this.activeMinSize), this.activeMaxSize);
8889         if(newSize != this.dragSpecs.startSize){
8890             if(this.fireEvent('beforeapply', this, newSize) !== false){
8891                 this.adapter.setElementSize(this, newSize);
8892                 this.fireEvent("moved", this, newSize);
8893                 this.fireEvent("resize", this, newSize);
8894             }
8895         }
8896     },
8897     
8898     /**
8899      * Get the adapter this SplitBar uses
8900      * @return The adapter object
8901      */
8902     getAdapter : function(){
8903         return this.adapter;
8904     },
8905     
8906     /**
8907      * Set the adapter this SplitBar uses
8908      * @param {Object} adapter A SplitBar adapter object
8909      */
8910     setAdapter : function(adapter){
8911         this.adapter = adapter;
8912         this.adapter.init(this);
8913     },
8914     
8915     /**
8916      * Gets the minimum size for the resizing element
8917      * @return {Number} The minimum size
8918      */
8919     getMinimumSize : function(){
8920         return this.minSize;
8921     },
8922     
8923     /**
8924      * Sets the minimum size for the resizing element
8925      * @param {Number} minSize The minimum size
8926      */
8927     setMinimumSize : function(minSize){
8928         this.minSize = minSize;
8929     },
8930     
8931     /**
8932      * Gets the maximum size for the resizing element
8933      * @return {Number} The maximum size
8934      */
8935     getMaximumSize : function(){
8936         return this.maxSize;
8937     },
8938     
8939     /**
8940      * Sets the maximum size for the resizing element
8941      * @param {Number} maxSize The maximum size
8942      */
8943     setMaximumSize : function(maxSize){
8944         this.maxSize = maxSize;
8945     },
8946     
8947     /**
8948      * Sets the initialize size for the resizing element
8949      * @param {Number} size The initial size
8950      */
8951     setCurrentSize : function(size){
8952         var oldAnimate = this.animate;
8953         this.animate = false;
8954         this.adapter.setElementSize(this, size);
8955         this.animate = oldAnimate;
8956     },
8957     
8958     /**
8959      * Destroy this splitbar. 
8960      * @param {Boolean} removeEl True to remove the element
8961      */
8962     destroy : function(removeEl){
8963         if(this.shim){
8964             this.shim.remove();
8965         }
8966         this.dd.unreg();
8967         this.proxy.parentNode.removeChild(this.proxy);
8968         if(removeEl){
8969             this.el.remove();
8970         }
8971     }
8972 });
8973
8974 /**
8975  * @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.
8976  */
8977 Roo.SplitBar.createProxy = function(dir){
8978     var proxy = new Roo.Element(document.createElement("div"));
8979     proxy.unselectable();
8980     var cls = 'x-splitbar-proxy';
8981     proxy.addClass(cls + ' ' + (dir == Roo.SplitBar.HORIZONTAL ? cls +'-h' : cls + '-v'));
8982     document.body.appendChild(proxy.dom);
8983     return proxy.dom;
8984 };
8985
8986 /** 
8987  * @class Roo.SplitBar.BasicLayoutAdapter
8988  * Default Adapter. It assumes the splitter and resizing element are not positioned
8989  * elements and only gets/sets the width of the element. Generally used for table based layouts.
8990  */
8991 Roo.SplitBar.BasicLayoutAdapter = function(){
8992 };
8993
8994 Roo.SplitBar.BasicLayoutAdapter.prototype = {
8995     // do nothing for now
8996     init : function(s){
8997     
8998     },
8999     /**
9000      * Called before drag operations to get the current size of the resizing element. 
9001      * @param {Roo.SplitBar} s The SplitBar using this adapter
9002      */
9003      getElementSize : function(s){
9004         if(s.orientation == Roo.SplitBar.HORIZONTAL){
9005             return s.resizingEl.getWidth();
9006         }else{
9007             return s.resizingEl.getHeight();
9008         }
9009     },
9010     
9011     /**
9012      * Called after drag operations to set the size of the resizing element.
9013      * @param {Roo.SplitBar} s The SplitBar using this adapter
9014      * @param {Number} newSize The new size to set
9015      * @param {Function} onComplete A function to be invoked when resizing is complete
9016      */
9017     setElementSize : function(s, newSize, onComplete){
9018         if(s.orientation == Roo.SplitBar.HORIZONTAL){
9019             if(!s.animate){
9020                 s.resizingEl.setWidth(newSize);
9021                 if(onComplete){
9022                     onComplete(s, newSize);
9023                 }
9024             }else{
9025                 s.resizingEl.setWidth(newSize, true, .1, onComplete, 'easeOut');
9026             }
9027         }else{
9028             
9029             if(!s.animate){
9030                 s.resizingEl.setHeight(newSize);
9031                 if(onComplete){
9032                     onComplete(s, newSize);
9033                 }
9034             }else{
9035                 s.resizingEl.setHeight(newSize, true, .1, onComplete, 'easeOut');
9036             }
9037         }
9038     }
9039 };
9040
9041 /** 
9042  *@class Roo.SplitBar.AbsoluteLayoutAdapter
9043  * @extends Roo.SplitBar.BasicLayoutAdapter
9044  * Adapter that  moves the splitter element to align with the resized sizing element. 
9045  * Used with an absolute positioned SplitBar.
9046  * @param {String/HTMLElement/Roo.Element} container The container that wraps around the absolute positioned content. If it's
9047  * document.body, make sure you assign an id to the body element.
9048  */
9049 Roo.SplitBar.AbsoluteLayoutAdapter = function(container){
9050     this.basic = new Roo.SplitBar.BasicLayoutAdapter();
9051     this.container = Roo.get(container);
9052 };
9053
9054 Roo.SplitBar.AbsoluteLayoutAdapter.prototype = {
9055     init : function(s){
9056         this.basic.init(s);
9057     },
9058     
9059     getElementSize : function(s){
9060         return this.basic.getElementSize(s);
9061     },
9062     
9063     setElementSize : function(s, newSize, onComplete){
9064         this.basic.setElementSize(s, newSize, this.moveSplitter.createDelegate(this, [s]));
9065     },
9066     
9067     moveSplitter : function(s){
9068         var yes = Roo.SplitBar;
9069         switch(s.placement){
9070             case yes.LEFT:
9071                 s.el.setX(s.resizingEl.getRight());
9072                 break;
9073             case yes.RIGHT:
9074                 s.el.setStyle("right", (this.container.getWidth() - s.resizingEl.getLeft()) + "px");
9075                 break;
9076             case yes.TOP:
9077                 s.el.setY(s.resizingEl.getBottom());
9078                 break;
9079             case yes.BOTTOM:
9080                 s.el.setY(s.resizingEl.getTop() - s.el.getHeight());
9081                 break;
9082         }
9083     }
9084 };
9085
9086 /**
9087  * Orientation constant - Create a vertical SplitBar
9088  * @static
9089  * @type Number
9090  */
9091 Roo.SplitBar.VERTICAL = 1;
9092
9093 /**
9094  * Orientation constant - Create a horizontal SplitBar
9095  * @static
9096  * @type Number
9097  */
9098 Roo.SplitBar.HORIZONTAL = 2;
9099
9100 /**
9101  * Placement constant - The resizing element is to the left of the splitter element
9102  * @static
9103  * @type Number
9104  */
9105 Roo.SplitBar.LEFT = 1;
9106
9107 /**
9108  * Placement constant - The resizing element is to the right of the splitter element
9109  * @static
9110  * @type Number
9111  */
9112 Roo.SplitBar.RIGHT = 2;
9113
9114 /**
9115  * Placement constant - The resizing element is positioned above the splitter element
9116  * @static
9117  * @type Number
9118  */
9119 Roo.SplitBar.TOP = 3;
9120
9121 /**
9122  * Placement constant - The resizing element is positioned under splitter element
9123  * @static
9124  * @type Number
9125  */
9126 Roo.SplitBar.BOTTOM = 4;
9127 /*
9128  * Based on:
9129  * Ext JS Library 1.1.1
9130  * Copyright(c) 2006-2007, Ext JS, LLC.
9131  *
9132  * Originally Released Under LGPL - original licence link has changed is not relivant.
9133  *
9134  * Fork - LGPL
9135  * <script type="text/javascript">
9136  */
9137
9138 /**
9139  * @class Roo.View
9140  * @extends Roo.util.Observable
9141  * Create a "View" for an element based on a data model or UpdateManager and the supplied DomHelper template. 
9142  * This class also supports single and multi selection modes. <br>
9143  * Create a data model bound view:
9144  <pre><code>
9145  var store = new Roo.data.Store(...);
9146
9147  var view = new Roo.View({
9148     el : "my-element",
9149     tpl : '&lt;div id="{0}"&gt;{2} - {1}&lt;/div&gt;', // auto create template
9150  
9151     singleSelect: true,
9152     selectedClass: "ydataview-selected",
9153     store: store
9154  });
9155
9156  // listen for node click?
9157  view.on("click", function(vw, index, node, e){
9158  alert('Node "' + node.id + '" at index: ' + index + " was clicked.");
9159  });
9160
9161  // load XML data
9162  dataModel.load("foobar.xml");
9163  </code></pre>
9164  For an example of creating a JSON/UpdateManager view, see {@link Roo.JsonView}.
9165  * <br><br>
9166  * <b>Note: The root of your template must be a single node. Table/row implementations may work but are not supported due to
9167  * IE"s limited insertion support with tables and Opera"s faulty event bubbling.</b>
9168  * 
9169  * Note: old style constructor is still suported (container, template, config)
9170  * 
9171  * @constructor
9172  * Create a new View
9173  * @param {Object} config The config object
9174  * 
9175  */
9176 Roo.View = function(config, depreciated_tpl, depreciated_config){
9177     
9178     if (typeof(depreciated_tpl) == 'undefined') {
9179         // new way.. - universal constructor.
9180         Roo.apply(this, config);
9181         this.el  = Roo.get(this.el);
9182     } else {
9183         // old format..
9184         this.el  = Roo.get(config);
9185         this.tpl = depreciated_tpl;
9186         Roo.apply(this, depreciated_config);
9187     }
9188     this.wrapEl  = this.el.wrap().wrap();
9189     ///this.el = this.wrapEla.appendChild(document.createElement("div"));
9190     
9191     
9192     if(typeof(this.tpl) == "string"){
9193         this.tpl = new Roo.Template(this.tpl);
9194     } else {
9195         // support xtype ctors..
9196         this.tpl = new Roo.factory(this.tpl, Roo);
9197     }
9198     
9199     
9200     this.tpl.compile();
9201    
9202   
9203     
9204      
9205     /** @private */
9206     this.addEvents({
9207         /**
9208          * @event beforeclick
9209          * Fires before a click is processed. Returns false to cancel the default action.
9210          * @param {Roo.View} this
9211          * @param {Number} index The index of the target node
9212          * @param {HTMLElement} node The target node
9213          * @param {Roo.EventObject} e The raw event object
9214          */
9215             "beforeclick" : true,
9216         /**
9217          * @event click
9218          * Fires when a template node is clicked.
9219          * @param {Roo.View} this
9220          * @param {Number} index The index of the target node
9221          * @param {HTMLElement} node The target node
9222          * @param {Roo.EventObject} e The raw event object
9223          */
9224             "click" : true,
9225         /**
9226          * @event dblclick
9227          * Fires when a template node is double clicked.
9228          * @param {Roo.View} this
9229          * @param {Number} index The index of the target node
9230          * @param {HTMLElement} node The target node
9231          * @param {Roo.EventObject} e The raw event object
9232          */
9233             "dblclick" : true,
9234         /**
9235          * @event contextmenu
9236          * Fires when a template node is right clicked.
9237          * @param {Roo.View} this
9238          * @param {Number} index The index of the target node
9239          * @param {HTMLElement} node The target node
9240          * @param {Roo.EventObject} e The raw event object
9241          */
9242             "contextmenu" : true,
9243         /**
9244          * @event selectionchange
9245          * Fires when the selected nodes change.
9246          * @param {Roo.View} this
9247          * @param {Array} selections Array of the selected nodes
9248          */
9249             "selectionchange" : true,
9250     
9251         /**
9252          * @event beforeselect
9253          * Fires before a selection is made. If any handlers return false, the selection is cancelled.
9254          * @param {Roo.View} this
9255          * @param {HTMLElement} node The node to be selected
9256          * @param {Array} selections Array of currently selected nodes
9257          */
9258             "beforeselect" : true,
9259         /**
9260          * @event preparedata
9261          * Fires on every row to render, to allow you to change the data.
9262          * @param {Roo.View} this
9263          * @param {Object} data to be rendered (change this)
9264          */
9265           "preparedata" : true
9266           
9267           
9268         });
9269
9270
9271
9272     this.el.on({
9273         "click": this.onClick,
9274         "dblclick": this.onDblClick,
9275         "contextmenu": this.onContextMenu,
9276         scope:this
9277     });
9278
9279     this.selections = [];
9280     this.nodes = [];
9281     this.cmp = new Roo.CompositeElementLite([]);
9282     if(this.store){
9283         this.store = Roo.factory(this.store, Roo.data);
9284         this.setStore(this.store, true);
9285     }
9286     
9287     if ( this.footer && this.footer.xtype) {
9288            
9289          var fctr = this.wrapEl.appendChild(document.createElement("div"));
9290         
9291         this.footer.dataSource = this.store
9292         this.footer.container = fctr;
9293         this.footer = Roo.factory(this.footer, Roo);
9294         fctr.insertFirst(this.el);
9295         
9296         // this is a bit insane - as the paging toolbar seems to detach the el..
9297 //        dom.parentNode.parentNode.parentNode
9298          // they get detached?
9299     }
9300     
9301     
9302     Roo.View.superclass.constructor.call(this);
9303     
9304     
9305 };
9306
9307 Roo.extend(Roo.View, Roo.util.Observable, {
9308     
9309      /**
9310      * @cfg {Roo.data.Store} store Data store to load data from.
9311      */
9312     store : false,
9313     
9314     /**
9315      * @cfg {String|Roo.Element} el The container element.
9316      */
9317     el : '',
9318     
9319     /**
9320      * @cfg {String|Roo.Template} tpl The template used by this View 
9321      */
9322     tpl : false,
9323     /**
9324      * @cfg {String} dataName the named area of the template to use as the data area
9325      *                          Works with domtemplates roo-name="name"
9326      */
9327     dataName: false,
9328     /**
9329      * @cfg {String} selectedClass The css class to add to selected nodes
9330      */
9331     selectedClass : "x-view-selected",
9332      /**
9333      * @cfg {String} emptyText The empty text to show when nothing is loaded.
9334      */
9335     emptyText : "",
9336     
9337     /**
9338      * @cfg {String} text to display on mask (default Loading)
9339      */
9340     mask : false,
9341     /**
9342      * @cfg {Boolean} multiSelect Allow multiple selection
9343      */
9344     multiSelect : false,
9345     /**
9346      * @cfg {Boolean} singleSelect Allow single selection
9347      */
9348     singleSelect:  false,
9349     
9350     /**
9351      * @cfg {Boolean} toggleSelect - selecting 
9352      */
9353     toggleSelect : false,
9354     
9355     /**
9356      * Returns the element this view is bound to.
9357      * @return {Roo.Element}
9358      */
9359     getEl : function(){
9360         return this.wrapEl;
9361     },
9362     
9363     
9364
9365     /**
9366      * Refreshes the view. - called by datachanged on the store. - do not call directly.
9367      */
9368     refresh : function(){
9369         var t = this.tpl;
9370         
9371         // if we are using something like 'domtemplate', then
9372         // the what gets used is:
9373         // t.applySubtemplate(NAME, data, wrapping data..)
9374         // the outer template then get' applied with
9375         //     the store 'extra data'
9376         // and the body get's added to the
9377         //      roo-name="data" node?
9378         //      <span class='roo-tpl-{name}'></span> ?????
9379         
9380         
9381         
9382         this.clearSelections();
9383         this.el.update("");
9384         var html = [];
9385         var records = this.store.getRange();
9386         if(records.length < 1) {
9387             
9388             // is this valid??  = should it render a template??
9389             
9390             this.el.update(this.emptyText);
9391             return;
9392         }
9393         var el = this.el;
9394         if (this.dataName) {
9395             this.el.update(t.apply(this.store.meta)); //????
9396             el = this.el.child('.roo-tpl-' + this.dataName);
9397         }
9398         
9399         for(var i = 0, len = records.length; i < len; i++){
9400             var data = this.prepareData(records[i].data, i, records[i]);
9401             this.fireEvent("preparedata", this, data, i, records[i]);
9402             html[html.length] = Roo.util.Format.trim(
9403                 this.dataName ?
9404                     t.applySubtemplate(this.dataName, data, this.store.meta) :
9405                     t.apply(data)
9406             );
9407         }
9408         
9409         
9410         
9411         el.update(html.join(""));
9412         this.nodes = el.dom.childNodes;
9413         this.updateIndexes(0);
9414     },
9415
9416     /**
9417      * Function to override to reformat the data that is sent to
9418      * the template for each node.
9419      * DEPRICATED - use the preparedata event handler.
9420      * @param {Array/Object} data The raw data (array of colData for a data model bound view or
9421      * a JSON object for an UpdateManager bound view).
9422      */
9423     prepareData : function(data, index, record)
9424     {
9425         this.fireEvent("preparedata", this, data, index, record);
9426         return data;
9427     },
9428
9429     onUpdate : function(ds, record){
9430         this.clearSelections();
9431         var index = this.store.indexOf(record);
9432         var n = this.nodes[index];
9433         this.tpl.insertBefore(n, this.prepareData(record.data, index, record));
9434         n.parentNode.removeChild(n);
9435         this.updateIndexes(index, index);
9436     },
9437
9438     
9439     
9440 // --------- FIXME     
9441     onAdd : function(ds, records, index)
9442     {
9443         this.clearSelections();
9444         if(this.nodes.length == 0){
9445             this.refresh();
9446             return;
9447         }
9448         var n = this.nodes[index];
9449         for(var i = 0, len = records.length; i < len; i++){
9450             var d = this.prepareData(records[i].data, i, records[i]);
9451             if(n){
9452                 this.tpl.insertBefore(n, d);
9453             }else{
9454                 
9455                 this.tpl.append(this.el, d);
9456             }
9457         }
9458         this.updateIndexes(index);
9459     },
9460
9461     onRemove : function(ds, record, index){
9462         this.clearSelections();
9463         var el = this.dataName  ?
9464             this.el.child('.roo-tpl-' + this.dataName) :
9465             this.el; 
9466         el.dom.removeChild(this.nodes[index]);
9467         this.updateIndexes(index);
9468     },
9469
9470     /**
9471      * Refresh an individual node.
9472      * @param {Number} index
9473      */
9474     refreshNode : function(index){
9475         this.onUpdate(this.store, this.store.getAt(index));
9476     },
9477
9478     updateIndexes : function(startIndex, endIndex){
9479         var ns = this.nodes;
9480         startIndex = startIndex || 0;
9481         endIndex = endIndex || ns.length - 1;
9482         for(var i = startIndex; i <= endIndex; i++){
9483             ns[i].nodeIndex = i;
9484         }
9485     },
9486
9487     /**
9488      * Changes the data store this view uses and refresh the view.
9489      * @param {Store} store
9490      */
9491     setStore : function(store, initial){
9492         if(!initial && this.store){
9493             this.store.un("datachanged", this.refresh);
9494             this.store.un("add", this.onAdd);
9495             this.store.un("remove", this.onRemove);
9496             this.store.un("update", this.onUpdate);
9497             this.store.un("clear", this.refresh);
9498             this.store.un("beforeload", this.onBeforeLoad);
9499             this.store.un("load", this.onLoad);
9500             this.store.un("loadexception", this.onLoad);
9501         }
9502         if(store){
9503           
9504             store.on("datachanged", this.refresh, this);
9505             store.on("add", this.onAdd, this);
9506             store.on("remove", this.onRemove, this);
9507             store.on("update", this.onUpdate, this);
9508             store.on("clear", this.refresh, this);
9509             store.on("beforeload", this.onBeforeLoad, this);
9510             store.on("load", this.onLoad, this);
9511             store.on("loadexception", this.onLoad, this);
9512         }
9513         
9514         if(store){
9515             this.refresh();
9516         }
9517     },
9518     /**
9519      * onbeforeLoad - masks the loading area.
9520      *
9521      */
9522     onBeforeLoad : function()
9523     {
9524         this.el.update("");
9525         this.el.mask(this.mask ? this.mask : "Loading" ); 
9526     },
9527     onLoad : function ()
9528     {
9529         this.el.unmask();
9530     },
9531     
9532
9533     /**
9534      * Returns the template node the passed child belongs to or null if it doesn't belong to one.
9535      * @param {HTMLElement} node
9536      * @return {HTMLElement} The template node
9537      */
9538     findItemFromChild : function(node){
9539         var el = this.dataName  ?
9540             this.el.child('.roo-tpl-' + this.dataName,true) :
9541             this.el.dom; 
9542         
9543         if(!node || node.parentNode == el){
9544                     return node;
9545             }
9546             var p = node.parentNode;
9547             while(p && p != el){
9548             if(p.parentNode == el){
9549                 return p;
9550             }
9551             p = p.parentNode;
9552         }
9553             return null;
9554     },
9555
9556     /** @ignore */
9557     onClick : function(e){
9558         var item = this.findItemFromChild(e.getTarget());
9559         if(item){
9560             var index = this.indexOf(item);
9561             if(this.onItemClick(item, index, e) !== false){
9562                 this.fireEvent("click", this, index, item, e);
9563             }
9564         }else{
9565             this.clearSelections();
9566         }
9567     },
9568
9569     /** @ignore */
9570     onContextMenu : function(e){
9571         var item = this.findItemFromChild(e.getTarget());
9572         if(item){
9573             this.fireEvent("contextmenu", this, this.indexOf(item), item, e);
9574         }
9575     },
9576
9577     /** @ignore */
9578     onDblClick : function(e){
9579         var item = this.findItemFromChild(e.getTarget());
9580         if(item){
9581             this.fireEvent("dblclick", this, this.indexOf(item), item, e);
9582         }
9583     },
9584
9585     onItemClick : function(item, index, e)
9586     {
9587         if(this.fireEvent("beforeclick", this, index, item, e) === false){
9588             return false;
9589         }
9590         if (this.toggleSelect) {
9591             var m = this.isSelected(item) ? 'unselect' : 'select';
9592             Roo.log(m);
9593             var _t = this;
9594             _t[m](item, true, false);
9595             return true;
9596         }
9597         if(this.multiSelect || this.singleSelect){
9598             if(this.multiSelect && e.shiftKey && this.lastSelection){
9599                 this.select(this.getNodes(this.indexOf(this.lastSelection), index), false);
9600             }else{
9601                 this.select(item, this.multiSelect && e.ctrlKey);
9602                 this.lastSelection = item;
9603             }
9604             e.preventDefault();
9605         }
9606         return true;
9607     },
9608
9609     /**
9610      * Get the number of selected nodes.
9611      * @return {Number}
9612      */
9613     getSelectionCount : function(){
9614         return this.selections.length;
9615     },
9616
9617     /**
9618      * Get the currently selected nodes.
9619      * @return {Array} An array of HTMLElements
9620      */
9621     getSelectedNodes : function(){
9622         return this.selections;
9623     },
9624
9625     /**
9626      * Get the indexes of the selected nodes.
9627      * @return {Array}
9628      */
9629     getSelectedIndexes : function(){
9630         var indexes = [], s = this.selections;
9631         for(var i = 0, len = s.length; i < len; i++){
9632             indexes.push(s[i].nodeIndex);
9633         }
9634         return indexes;
9635     },
9636
9637     /**
9638      * Clear all selections
9639      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange event
9640      */
9641     clearSelections : function(suppressEvent){
9642         if(this.nodes && (this.multiSelect || this.singleSelect) && this.selections.length > 0){
9643             this.cmp.elements = this.selections;
9644             this.cmp.removeClass(this.selectedClass);
9645             this.selections = [];
9646             if(!suppressEvent){
9647                 this.fireEvent("selectionchange", this, this.selections);
9648             }
9649         }
9650     },
9651
9652     /**
9653      * Returns true if the passed node is selected
9654      * @param {HTMLElement/Number} node The node or node index
9655      * @return {Boolean}
9656      */
9657     isSelected : function(node){
9658         var s = this.selections;
9659         if(s.length < 1){
9660             return false;
9661         }
9662         node = this.getNode(node);
9663         return s.indexOf(node) !== -1;
9664     },
9665
9666     /**
9667      * Selects nodes.
9668      * @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
9669      * @param {Boolean} keepExisting (optional) true to keep existing selections
9670      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
9671      */
9672     select : function(nodeInfo, keepExisting, suppressEvent){
9673         if(nodeInfo instanceof Array){
9674             if(!keepExisting){
9675                 this.clearSelections(true);
9676             }
9677             for(var i = 0, len = nodeInfo.length; i < len; i++){
9678                 this.select(nodeInfo[i], true, true);
9679             }
9680             return;
9681         } 
9682         var node = this.getNode(nodeInfo);
9683         if(!node || this.isSelected(node)){
9684             return; // already selected.
9685         }
9686         if(!keepExisting){
9687             this.clearSelections(true);
9688         }
9689         if(this.fireEvent("beforeselect", this, node, this.selections) !== false){
9690             Roo.fly(node).addClass(this.selectedClass);
9691             this.selections.push(node);
9692             if(!suppressEvent){
9693                 this.fireEvent("selectionchange", this, this.selections);
9694             }
9695         }
9696         
9697         
9698     },
9699       /**
9700      * Unselects nodes.
9701      * @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
9702      * @param {Boolean} keepExisting (optional) true IGNORED (for campatibility with select)
9703      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
9704      */
9705     unselect : function(nodeInfo, keepExisting, suppressEvent)
9706     {
9707         if(nodeInfo instanceof Array){
9708             Roo.each(this.selections, function(s) {
9709                 this.unselect(s, nodeInfo);
9710             }, this);
9711             return;
9712         }
9713         var node = this.getNode(nodeInfo);
9714         if(!node || !this.isSelected(node)){
9715             Roo.log("not selected");
9716             return; // not selected.
9717         }
9718         // fireevent???
9719         var ns = [];
9720         Roo.each(this.selections, function(s) {
9721             if (s == node ) {
9722                 Roo.fly(node).removeClass(this.selectedClass);
9723
9724                 return;
9725             }
9726             ns.push(s);
9727         },this);
9728         
9729         this.selections= ns;
9730         this.fireEvent("selectionchange", this, this.selections);
9731     },
9732
9733     /**
9734      * Gets a template node.
9735      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
9736      * @return {HTMLElement} The node or null if it wasn't found
9737      */
9738     getNode : function(nodeInfo){
9739         if(typeof nodeInfo == "string"){
9740             return document.getElementById(nodeInfo);
9741         }else if(typeof nodeInfo == "number"){
9742             return this.nodes[nodeInfo];
9743         }
9744         return nodeInfo;
9745     },
9746
9747     /**
9748      * Gets a range template nodes.
9749      * @param {Number} startIndex
9750      * @param {Number} endIndex
9751      * @return {Array} An array of nodes
9752      */
9753     getNodes : function(start, end){
9754         var ns = this.nodes;
9755         start = start || 0;
9756         end = typeof end == "undefined" ? ns.length - 1 : end;
9757         var nodes = [];
9758         if(start <= end){
9759             for(var i = start; i <= end; i++){
9760                 nodes.push(ns[i]);
9761             }
9762         } else{
9763             for(var i = start; i >= end; i--){
9764                 nodes.push(ns[i]);
9765             }
9766         }
9767         return nodes;
9768     },
9769
9770     /**
9771      * Finds the index of the passed node
9772      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
9773      * @return {Number} The index of the node or -1
9774      */
9775     indexOf : function(node){
9776         node = this.getNode(node);
9777         if(typeof node.nodeIndex == "number"){
9778             return node.nodeIndex;
9779         }
9780         var ns = this.nodes;
9781         for(var i = 0, len = ns.length; i < len; i++){
9782             if(ns[i] == node){
9783                 return i;
9784             }
9785         }
9786         return -1;
9787     }
9788 });
9789 /*
9790  * Based on:
9791  * Ext JS Library 1.1.1
9792  * Copyright(c) 2006-2007, Ext JS, LLC.
9793  *
9794  * Originally Released Under LGPL - original licence link has changed is not relivant.
9795  *
9796  * Fork - LGPL
9797  * <script type="text/javascript">
9798  */
9799
9800 /**
9801  * @class Roo.JsonView
9802  * @extends Roo.View
9803  * Shortcut class to create a JSON + {@link Roo.UpdateManager} template view. Usage:
9804 <pre><code>
9805 var view = new Roo.JsonView({
9806     container: "my-element",
9807     tpl: '&lt;div id="{id}"&gt;{foo} - {bar}&lt;/div&gt;', // auto create template
9808     multiSelect: true, 
9809     jsonRoot: "data" 
9810 });
9811
9812 // listen for node click?
9813 view.on("click", function(vw, index, node, e){
9814     alert('Node "' + node.id + '" at index: ' + index + " was clicked.");
9815 });
9816
9817 // direct load of JSON data
9818 view.load("foobar.php");
9819
9820 // Example from my blog list
9821 var tpl = new Roo.Template(
9822     '&lt;div class="entry"&gt;' +
9823     '&lt;a class="entry-title" href="{link}"&gt;{title}&lt;/a&gt;' +
9824     "&lt;h4&gt;{date} by {author} | {comments} Comments&lt;/h4&gt;{description}" +
9825     "&lt;/div&gt;&lt;hr /&gt;"
9826 );
9827
9828 var moreView = new Roo.JsonView({
9829     container :  "entry-list", 
9830     template : tpl,
9831     jsonRoot: "posts"
9832 });
9833 moreView.on("beforerender", this.sortEntries, this);
9834 moreView.load({
9835     url: "/blog/get-posts.php",
9836     params: "allposts=true",
9837     text: "Loading Blog Entries..."
9838 });
9839 </code></pre>
9840
9841 * Note: old code is supported with arguments : (container, template, config)
9842
9843
9844  * @constructor
9845  * Create a new JsonView
9846  * 
9847  * @param {Object} config The config object
9848  * 
9849  */
9850 Roo.JsonView = function(config, depreciated_tpl, depreciated_config){
9851     
9852     
9853     Roo.JsonView.superclass.constructor.call(this, config, depreciated_tpl, depreciated_config);
9854
9855     var um = this.el.getUpdateManager();
9856     um.setRenderer(this);
9857     um.on("update", this.onLoad, this);
9858     um.on("failure", this.onLoadException, this);
9859
9860     /**
9861      * @event beforerender
9862      * Fires before rendering of the downloaded JSON data.
9863      * @param {Roo.JsonView} this
9864      * @param {Object} data The JSON data loaded
9865      */
9866     /**
9867      * @event load
9868      * Fires when data is loaded.
9869      * @param {Roo.JsonView} this
9870      * @param {Object} data The JSON data loaded
9871      * @param {Object} response The raw Connect response object
9872      */
9873     /**
9874      * @event loadexception
9875      * Fires when loading fails.
9876      * @param {Roo.JsonView} this
9877      * @param {Object} response The raw Connect response object
9878      */
9879     this.addEvents({
9880         'beforerender' : true,
9881         'load' : true,
9882         'loadexception' : true
9883     });
9884 };
9885 Roo.extend(Roo.JsonView, Roo.View, {
9886     /**
9887      * @type {String} The root property in the loaded JSON object that contains the data
9888      */
9889     jsonRoot : "",
9890
9891     /**
9892      * Refreshes the view.
9893      */
9894     refresh : function(){
9895         this.clearSelections();
9896         this.el.update("");
9897         var html = [];
9898         var o = this.jsonData;
9899         if(o && o.length > 0){
9900             for(var i = 0, len = o.length; i < len; i++){
9901                 var data = this.prepareData(o[i], i, o);
9902                 html[html.length] = this.tpl.apply(data);
9903             }
9904         }else{
9905             html.push(this.emptyText);
9906         }
9907         this.el.update(html.join(""));
9908         this.nodes = this.el.dom.childNodes;
9909         this.updateIndexes(0);
9910     },
9911
9912     /**
9913      * 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.
9914      * @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:
9915      <pre><code>
9916      view.load({
9917          url: "your-url.php",
9918          params: {param1: "foo", param2: "bar"}, // or a URL encoded string
9919          callback: yourFunction,
9920          scope: yourObject, //(optional scope)
9921          discardUrl: false,
9922          nocache: false,
9923          text: "Loading...",
9924          timeout: 30,
9925          scripts: false
9926      });
9927      </code></pre>
9928      * The only required property is <i>url</i>. The optional properties <i>nocache</i>, <i>text</i> and <i>scripts</i>
9929      * 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.
9930      * @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}
9931      * @param {Function} callback (optional) Callback when transaction is complete - called with signature (oElement, bSuccess)
9932      * @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.
9933      */
9934     load : function(){
9935         var um = this.el.getUpdateManager();
9936         um.update.apply(um, arguments);
9937     },
9938
9939     render : function(el, response){
9940         this.clearSelections();
9941         this.el.update("");
9942         var o;
9943         try{
9944             o = Roo.util.JSON.decode(response.responseText);
9945             if(this.jsonRoot){
9946                 
9947                 o = o[this.jsonRoot];
9948             }
9949         } catch(e){
9950         }
9951         /**
9952          * The current JSON data or null
9953          */
9954         this.jsonData = o;
9955         this.beforeRender();
9956         this.refresh();
9957     },
9958
9959 /**
9960  * Get the number of records in the current JSON dataset
9961  * @return {Number}
9962  */
9963     getCount : function(){
9964         return this.jsonData ? this.jsonData.length : 0;
9965     },
9966
9967 /**
9968  * Returns the JSON object for the specified node(s)
9969  * @param {HTMLElement/Array} node The node or an array of nodes
9970  * @return {Object/Array} If you pass in an array, you get an array back, otherwise
9971  * you get the JSON object for the node
9972  */
9973     getNodeData : function(node){
9974         if(node instanceof Array){
9975             var data = [];
9976             for(var i = 0, len = node.length; i < len; i++){
9977                 data.push(this.getNodeData(node[i]));
9978             }
9979             return data;
9980         }
9981         return this.jsonData[this.indexOf(node)] || null;
9982     },
9983
9984     beforeRender : function(){
9985         this.snapshot = this.jsonData;
9986         if(this.sortInfo){
9987             this.sort.apply(this, this.sortInfo);
9988         }
9989         this.fireEvent("beforerender", this, this.jsonData);
9990     },
9991
9992     onLoad : function(el, o){
9993         this.fireEvent("load", this, this.jsonData, o);
9994     },
9995
9996     onLoadException : function(el, o){
9997         this.fireEvent("loadexception", this, o);
9998     },
9999
10000 /**
10001  * Filter the data by a specific property.
10002  * @param {String} property A property on your JSON objects
10003  * @param {String/RegExp} value Either string that the property values
10004  * should start with, or a RegExp to test against the property
10005  */
10006     filter : function(property, value){
10007         if(this.jsonData){
10008             var data = [];
10009             var ss = this.snapshot;
10010             if(typeof value == "string"){
10011                 var vlen = value.length;
10012                 if(vlen == 0){
10013                     this.clearFilter();
10014                     return;
10015                 }
10016                 value = value.toLowerCase();
10017                 for(var i = 0, len = ss.length; i < len; i++){
10018                     var o = ss[i];
10019                     if(o[property].substr(0, vlen).toLowerCase() == value){
10020                         data.push(o);
10021                     }
10022                 }
10023             } else if(value.exec){ // regex?
10024                 for(var i = 0, len = ss.length; i < len; i++){
10025                     var o = ss[i];
10026                     if(value.test(o[property])){
10027                         data.push(o);
10028                     }
10029                 }
10030             } else{
10031                 return;
10032             }
10033             this.jsonData = data;
10034             this.refresh();
10035         }
10036     },
10037
10038 /**
10039  * Filter by a function. The passed function will be called with each
10040  * object in the current dataset. If the function returns true the value is kept,
10041  * otherwise it is filtered.
10042  * @param {Function} fn
10043  * @param {Object} scope (optional) The scope of the function (defaults to this JsonView)
10044  */
10045     filterBy : function(fn, scope){
10046         if(this.jsonData){
10047             var data = [];
10048             var ss = this.snapshot;
10049             for(var i = 0, len = ss.length; i < len; i++){
10050                 var o = ss[i];
10051                 if(fn.call(scope || this, o)){
10052                     data.push(o);
10053                 }
10054             }
10055             this.jsonData = data;
10056             this.refresh();
10057         }
10058     },
10059
10060 /**
10061  * Clears the current filter.
10062  */
10063     clearFilter : function(){
10064         if(this.snapshot && this.jsonData != this.snapshot){
10065             this.jsonData = this.snapshot;
10066             this.refresh();
10067         }
10068     },
10069
10070
10071 /**
10072  * Sorts the data for this view and refreshes it.
10073  * @param {String} property A property on your JSON objects to sort on
10074  * @param {String} direction (optional) "desc" or "asc" (defaults to "asc")
10075  * @param {Function} sortType (optional) A function to call to convert the data to a sortable value.
10076  */
10077     sort : function(property, dir, sortType){
10078         this.sortInfo = Array.prototype.slice.call(arguments, 0);
10079         if(this.jsonData){
10080             var p = property;
10081             var dsc = dir && dir.toLowerCase() == "desc";
10082             var f = function(o1, o2){
10083                 var v1 = sortType ? sortType(o1[p]) : o1[p];
10084                 var v2 = sortType ? sortType(o2[p]) : o2[p];
10085                 ;
10086                 if(v1 < v2){
10087                     return dsc ? +1 : -1;
10088                 } else if(v1 > v2){
10089                     return dsc ? -1 : +1;
10090                 } else{
10091                     return 0;
10092                 }
10093             };
10094             this.jsonData.sort(f);
10095             this.refresh();
10096             if(this.jsonData != this.snapshot){
10097                 this.snapshot.sort(f);
10098             }
10099         }
10100     }
10101 });/*
10102  * Based on:
10103  * Ext JS Library 1.1.1
10104  * Copyright(c) 2006-2007, Ext JS, LLC.
10105  *
10106  * Originally Released Under LGPL - original licence link has changed is not relivant.
10107  *
10108  * Fork - LGPL
10109  * <script type="text/javascript">
10110  */
10111  
10112
10113 /**
10114  * @class Roo.ColorPalette
10115  * @extends Roo.Component
10116  * Simple color palette class for choosing colors.  The palette can be rendered to any container.<br />
10117  * Here's an example of typical usage:
10118  * <pre><code>
10119 var cp = new Roo.ColorPalette({value:'993300'});  // initial selected color
10120 cp.render('my-div');
10121
10122 cp.on('select', function(palette, selColor){
10123     // do something with selColor
10124 });
10125 </code></pre>
10126  * @constructor
10127  * Create a new ColorPalette
10128  * @param {Object} config The config object
10129  */
10130 Roo.ColorPalette = function(config){
10131     Roo.ColorPalette.superclass.constructor.call(this, config);
10132     this.addEvents({
10133         /**
10134              * @event select
10135              * Fires when a color is selected
10136              * @param {ColorPalette} this
10137              * @param {String} color The 6-digit color hex code (without the # symbol)
10138              */
10139         select: true
10140     });
10141
10142     if(this.handler){
10143         this.on("select", this.handler, this.scope, true);
10144     }
10145 };
10146 Roo.extend(Roo.ColorPalette, Roo.Component, {
10147     /**
10148      * @cfg {String} itemCls
10149      * The CSS class to apply to the containing element (defaults to "x-color-palette")
10150      */
10151     itemCls : "x-color-palette",
10152     /**
10153      * @cfg {String} value
10154      * The initial color to highlight (should be a valid 6-digit color hex code without the # symbol).  Note that
10155      * the hex codes are case-sensitive.
10156      */
10157     value : null,
10158     clickEvent:'click',
10159     // private
10160     ctype: "Roo.ColorPalette",
10161
10162     /**
10163      * @cfg {Boolean} allowReselect If set to true then reselecting a color that is already selected fires the selection event
10164      */
10165     allowReselect : false,
10166
10167     /**
10168      * <p>An array of 6-digit color hex code strings (without the # symbol).  This array can contain any number
10169      * of colors, and each hex code should be unique.  The width of the palette is controlled via CSS by adjusting
10170      * the width property of the 'x-color-palette' class (or assigning a custom class), so you can balance the number
10171      * of colors with the width setting until the box is symmetrical.</p>
10172      * <p>You can override individual colors if needed:</p>
10173      * <pre><code>
10174 var cp = new Roo.ColorPalette();
10175 cp.colors[0] = "FF0000";  // change the first box to red
10176 </code></pre>
10177
10178 Or you can provide a custom array of your own for complete control:
10179 <pre><code>
10180 var cp = new Roo.ColorPalette();
10181 cp.colors = ["000000", "993300", "333300"];
10182 </code></pre>
10183      * @type Array
10184      */
10185     colors : [
10186         "000000", "993300", "333300", "003300", "003366", "000080", "333399", "333333",
10187         "800000", "FF6600", "808000", "008000", "008080", "0000FF", "666699", "808080",
10188         "FF0000", "FF9900", "99CC00", "339966", "33CCCC", "3366FF", "800080", "969696",
10189         "FF00FF", "FFCC00", "FFFF00", "00FF00", "00FFFF", "00CCFF", "993366", "C0C0C0",
10190         "FF99CC", "FFCC99", "FFFF99", "CCFFCC", "CCFFFF", "99CCFF", "CC99FF", "FFFFFF"
10191     ],
10192
10193     // private
10194     onRender : function(container, position){
10195         var t = new Roo.MasterTemplate(
10196             '<tpl><a href="#" class="color-{0}" hidefocus="on"><em><span style="background:#{0}" unselectable="on">&#160;</span></em></a></tpl>'
10197         );
10198         var c = this.colors;
10199         for(var i = 0, len = c.length; i < len; i++){
10200             t.add([c[i]]);
10201         }
10202         var el = document.createElement("div");
10203         el.className = this.itemCls;
10204         t.overwrite(el);
10205         container.dom.insertBefore(el, position);
10206         this.el = Roo.get(el);
10207         this.el.on(this.clickEvent, this.handleClick,  this, {delegate: "a"});
10208         if(this.clickEvent != 'click'){
10209             this.el.on('click', Roo.emptyFn,  this, {delegate: "a", preventDefault:true});
10210         }
10211     },
10212
10213     // private
10214     afterRender : function(){
10215         Roo.ColorPalette.superclass.afterRender.call(this);
10216         if(this.value){
10217             var s = this.value;
10218             this.value = null;
10219             this.select(s);
10220         }
10221     },
10222
10223     // private
10224     handleClick : function(e, t){
10225         e.preventDefault();
10226         if(!this.disabled){
10227             var c = t.className.match(/(?:^|\s)color-(.{6})(?:\s|$)/)[1];
10228             this.select(c.toUpperCase());
10229         }
10230     },
10231
10232     /**
10233      * Selects the specified color in the palette (fires the select event)
10234      * @param {String} color A valid 6-digit color hex code (# will be stripped if included)
10235      */
10236     select : function(color){
10237         color = color.replace("#", "");
10238         if(color != this.value || this.allowReselect){
10239             var el = this.el;
10240             if(this.value){
10241                 el.child("a.color-"+this.value).removeClass("x-color-palette-sel");
10242             }
10243             el.child("a.color-"+color).addClass("x-color-palette-sel");
10244             this.value = color;
10245             this.fireEvent("select", this, color);
10246         }
10247     }
10248 });/*
10249  * Based on:
10250  * Ext JS Library 1.1.1
10251  * Copyright(c) 2006-2007, Ext JS, LLC.
10252  *
10253  * Originally Released Under LGPL - original licence link has changed is not relivant.
10254  *
10255  * Fork - LGPL
10256  * <script type="text/javascript">
10257  */
10258  
10259 /**
10260  * @class Roo.DatePicker
10261  * @extends Roo.Component
10262  * Simple date picker class.
10263  * @constructor
10264  * Create a new DatePicker
10265  * @param {Object} config The config object
10266  */
10267 Roo.DatePicker = function(config){
10268     Roo.DatePicker.superclass.constructor.call(this, config);
10269
10270     this.value = config && config.value ?
10271                  config.value.clearTime() : new Date().clearTime();
10272
10273     this.addEvents({
10274         /**
10275              * @event select
10276              * Fires when a date is selected
10277              * @param {DatePicker} this
10278              * @param {Date} date The selected date
10279              */
10280         'select': true,
10281         /**
10282              * @event monthchange
10283              * Fires when the displayed month changes 
10284              * @param {DatePicker} this
10285              * @param {Date} date The selected month
10286              */
10287         'monthchange': true
10288     });
10289
10290     if(this.handler){
10291         this.on("select", this.handler,  this.scope || this);
10292     }
10293     // build the disabledDatesRE
10294     if(!this.disabledDatesRE && this.disabledDates){
10295         var dd = this.disabledDates;
10296         var re = "(?:";
10297         for(var i = 0; i < dd.length; i++){
10298             re += dd[i];
10299             if(i != dd.length-1) re += "|";
10300         }
10301         this.disabledDatesRE = new RegExp(re + ")");
10302     }
10303 };
10304
10305 Roo.extend(Roo.DatePicker, Roo.Component, {
10306     /**
10307      * @cfg {String} todayText
10308      * The text to display on the button that selects the current date (defaults to "Today")
10309      */
10310     todayText : "Today",
10311     /**
10312      * @cfg {String} okText
10313      * The text to display on the ok button
10314      */
10315     okText : "&#160;OK&#160;", // &#160; to give the user extra clicking room
10316     /**
10317      * @cfg {String} cancelText
10318      * The text to display on the cancel button
10319      */
10320     cancelText : "Cancel",
10321     /**
10322      * @cfg {String} todayTip
10323      * The tooltip to display for the button that selects the current date (defaults to "{current date} (Spacebar)")
10324      */
10325     todayTip : "{0} (Spacebar)",
10326     /**
10327      * @cfg {Date} minDate
10328      * Minimum allowable date (JavaScript date object, defaults to null)
10329      */
10330     minDate : null,
10331     /**
10332      * @cfg {Date} maxDate
10333      * Maximum allowable date (JavaScript date object, defaults to null)
10334      */
10335     maxDate : null,
10336     /**
10337      * @cfg {String} minText
10338      * The error text to display if the minDate validation fails (defaults to "This date is before the minimum date")
10339      */
10340     minText : "This date is before the minimum date",
10341     /**
10342      * @cfg {String} maxText
10343      * The error text to display if the maxDate validation fails (defaults to "This date is after the maximum date")
10344      */
10345     maxText : "This date is after the maximum date",
10346     /**
10347      * @cfg {String} format
10348      * The default date format string which can be overriden for localization support.  The format must be
10349      * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
10350      */
10351     format : "m/d/y",
10352     /**
10353      * @cfg {Array} disabledDays
10354      * An array of days to disable, 0-based. For example, [0, 6] disables Sunday and Saturday (defaults to null).
10355      */
10356     disabledDays : null,
10357     /**
10358      * @cfg {String} disabledDaysText
10359      * The tooltip to display when the date falls on a disabled day (defaults to "")
10360      */
10361     disabledDaysText : "",
10362     /**
10363      * @cfg {RegExp} disabledDatesRE
10364      * JavaScript regular expression used to disable a pattern of dates (defaults to null)
10365      */
10366     disabledDatesRE : null,
10367     /**
10368      * @cfg {String} disabledDatesText
10369      * The tooltip text to display when the date falls on a disabled date (defaults to "")
10370      */
10371     disabledDatesText : "",
10372     /**
10373      * @cfg {Boolean} constrainToViewport
10374      * True to constrain the date picker to the viewport (defaults to true)
10375      */
10376     constrainToViewport : true,
10377     /**
10378      * @cfg {Array} monthNames
10379      * An array of textual month names which can be overriden for localization support (defaults to Date.monthNames)
10380      */
10381     monthNames : Date.monthNames,
10382     /**
10383      * @cfg {Array} dayNames
10384      * An array of textual day names which can be overriden for localization support (defaults to Date.dayNames)
10385      */
10386     dayNames : Date.dayNames,
10387     /**
10388      * @cfg {String} nextText
10389      * The next month navigation button tooltip (defaults to 'Next Month (Control+Right)')
10390      */
10391     nextText: 'Next Month (Control+Right)',
10392     /**
10393      * @cfg {String} prevText
10394      * The previous month navigation button tooltip (defaults to 'Previous Month (Control+Left)')
10395      */
10396     prevText: 'Previous Month (Control+Left)',
10397     /**
10398      * @cfg {String} monthYearText
10399      * The header month selector tooltip (defaults to 'Choose a month (Control+Up/Down to move years)')
10400      */
10401     monthYearText: 'Choose a month (Control+Up/Down to move years)',
10402     /**
10403      * @cfg {Number} startDay
10404      * Day index at which the week should begin, 0-based (defaults to 0, which is Sunday)
10405      */
10406     startDay : 0,
10407     /**
10408      * @cfg {Bool} showClear
10409      * Show a clear button (usefull for date form elements that can be blank.)
10410      */
10411     
10412     showClear: false,
10413     
10414     /**
10415      * Sets the value of the date field
10416      * @param {Date} value The date to set
10417      */
10418     setValue : function(value){
10419         var old = this.value;
10420         
10421         if (typeof(value) == 'string') {
10422          
10423             value = Date.parseDate(value, this.format);
10424         }
10425         if (!value) {
10426             value = new Date();
10427         }
10428         
10429         this.value = value.clearTime(true);
10430         if(this.el){
10431             this.update(this.value);
10432         }
10433     },
10434
10435     /**
10436      * Gets the current selected value of the date field
10437      * @return {Date} The selected date
10438      */
10439     getValue : function(){
10440         return this.value;
10441     },
10442
10443     // private
10444     focus : function(){
10445         if(this.el){
10446             this.update(this.activeDate);
10447         }
10448     },
10449
10450     // privateval
10451     onRender : function(container, position){
10452         
10453         var m = [
10454              '<table cellspacing="0">',
10455                 '<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>',
10456                 '<tr><td colspan="3"><table class="x-date-inner" cellspacing="0"><thead><tr>'];
10457         var dn = this.dayNames;
10458         for(var i = 0; i < 7; i++){
10459             var d = this.startDay+i;
10460             if(d > 6){
10461                 d = d-7;
10462             }
10463             m.push("<th><span>", dn[d].substr(0,1), "</span></th>");
10464         }
10465         m[m.length] = "</tr></thead><tbody><tr>";
10466         for(var i = 0; i < 42; i++) {
10467             if(i % 7 == 0 && i != 0){
10468                 m[m.length] = "</tr><tr>";
10469             }
10470             m[m.length] = '<td><a href="#" hidefocus="on" class="x-date-date" tabIndex="1"><em><span></span></em></a></td>';
10471         }
10472         m[m.length] = '</tr></tbody></table></td></tr><tr>'+
10473             '<td colspan="3" class="x-date-bottom" align="center"></td></tr></table><div class="x-date-mp"></div>';
10474
10475         var el = document.createElement("div");
10476         el.className = "x-date-picker";
10477         el.innerHTML = m.join("");
10478
10479         container.dom.insertBefore(el, position);
10480
10481         this.el = Roo.get(el);
10482         this.eventEl = Roo.get(el.firstChild);
10483
10484         new Roo.util.ClickRepeater(this.el.child("td.x-date-left a"), {
10485             handler: this.showPrevMonth,
10486             scope: this,
10487             preventDefault:true,
10488             stopDefault:true
10489         });
10490
10491         new Roo.util.ClickRepeater(this.el.child("td.x-date-right a"), {
10492             handler: this.showNextMonth,
10493             scope: this,
10494             preventDefault:true,
10495             stopDefault:true
10496         });
10497
10498         this.eventEl.on("mousewheel", this.handleMouseWheel,  this);
10499
10500         this.monthPicker = this.el.down('div.x-date-mp');
10501         this.monthPicker.enableDisplayMode('block');
10502         
10503         var kn = new Roo.KeyNav(this.eventEl, {
10504             "left" : function(e){
10505                 e.ctrlKey ?
10506                     this.showPrevMonth() :
10507                     this.update(this.activeDate.add("d", -1));
10508             },
10509
10510             "right" : function(e){
10511                 e.ctrlKey ?
10512                     this.showNextMonth() :
10513                     this.update(this.activeDate.add("d", 1));
10514             },
10515
10516             "up" : function(e){
10517                 e.ctrlKey ?
10518                     this.showNextYear() :
10519                     this.update(this.activeDate.add("d", -7));
10520             },
10521
10522             "down" : function(e){
10523                 e.ctrlKey ?
10524                     this.showPrevYear() :
10525                     this.update(this.activeDate.add("d", 7));
10526             },
10527
10528             "pageUp" : function(e){
10529                 this.showNextMonth();
10530             },
10531
10532             "pageDown" : function(e){
10533                 this.showPrevMonth();
10534             },
10535
10536             "enter" : function(e){
10537                 e.stopPropagation();
10538                 return true;
10539             },
10540
10541             scope : this
10542         });
10543
10544         this.eventEl.on("click", this.handleDateClick,  this, {delegate: "a.x-date-date"});
10545
10546         this.eventEl.addKeyListener(Roo.EventObject.SPACE, this.selectToday,  this);
10547
10548         this.el.unselectable();
10549         
10550         this.cells = this.el.select("table.x-date-inner tbody td");
10551         this.textNodes = this.el.query("table.x-date-inner tbody span");
10552
10553         this.mbtn = new Roo.Button(this.el.child("td.x-date-middle", true), {
10554             text: "&#160;",
10555             tooltip: this.monthYearText
10556         });
10557
10558         this.mbtn.on('click', this.showMonthPicker, this);
10559         this.mbtn.el.child(this.mbtn.menuClassTarget).addClass("x-btn-with-menu");
10560
10561
10562         var today = (new Date()).dateFormat(this.format);
10563         
10564         var baseTb = new Roo.Toolbar(this.el.child("td.x-date-bottom", true));
10565         if (this.showClear) {
10566             baseTb.add( new Roo.Toolbar.Fill());
10567         }
10568         baseTb.add({
10569             text: String.format(this.todayText, today),
10570             tooltip: String.format(this.todayTip, today),
10571             handler: this.selectToday,
10572             scope: this
10573         });
10574         
10575         //var todayBtn = new Roo.Button(this.el.child("td.x-date-bottom", true), {
10576             
10577         //});
10578         if (this.showClear) {
10579             
10580             baseTb.add( new Roo.Toolbar.Fill());
10581             baseTb.add({
10582                 text: '&#160;',
10583                 cls: 'x-btn-icon x-btn-clear',
10584                 handler: function() {
10585                     //this.value = '';
10586                     this.fireEvent("select", this, '');
10587                 },
10588                 scope: this
10589             });
10590         }
10591         
10592         
10593         if(Roo.isIE){
10594             this.el.repaint();
10595         }
10596         this.update(this.value);
10597     },
10598
10599     createMonthPicker : function(){
10600         if(!this.monthPicker.dom.firstChild){
10601             var buf = ['<table border="0" cellspacing="0">'];
10602             for(var i = 0; i < 6; i++){
10603                 buf.push(
10604                     '<tr><td class="x-date-mp-month"><a href="#">', this.monthNames[i].substr(0, 3), '</a></td>',
10605                     '<td class="x-date-mp-month x-date-mp-sep"><a href="#">', this.monthNames[i+6].substr(0, 3), '</a></td>',
10606                     i == 0 ?
10607                     '<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>' :
10608                     '<td class="x-date-mp-year"><a href="#"></a></td><td class="x-date-mp-year"><a href="#"></a></td></tr>'
10609                 );
10610             }
10611             buf.push(
10612                 '<tr class="x-date-mp-btns"><td colspan="4"><button type="button" class="x-date-mp-ok">',
10613                     this.okText,
10614                     '</button><button type="button" class="x-date-mp-cancel">',
10615                     this.cancelText,
10616                     '</button></td></tr>',
10617                 '</table>'
10618             );
10619             this.monthPicker.update(buf.join(''));
10620             this.monthPicker.on('click', this.onMonthClick, this);
10621             this.monthPicker.on('dblclick', this.onMonthDblClick, this);
10622
10623             this.mpMonths = this.monthPicker.select('td.x-date-mp-month');
10624             this.mpYears = this.monthPicker.select('td.x-date-mp-year');
10625
10626             this.mpMonths.each(function(m, a, i){
10627                 i += 1;
10628                 if((i%2) == 0){
10629                     m.dom.xmonth = 5 + Math.round(i * .5);
10630                 }else{
10631                     m.dom.xmonth = Math.round((i-1) * .5);
10632                 }
10633             });
10634         }
10635     },
10636
10637     showMonthPicker : function(){
10638         this.createMonthPicker();
10639         var size = this.el.getSize();
10640         this.monthPicker.setSize(size);
10641         this.monthPicker.child('table').setSize(size);
10642
10643         this.mpSelMonth = (this.activeDate || this.value).getMonth();
10644         this.updateMPMonth(this.mpSelMonth);
10645         this.mpSelYear = (this.activeDate || this.value).getFullYear();
10646         this.updateMPYear(this.mpSelYear);
10647
10648         this.monthPicker.slideIn('t', {duration:.2});
10649     },
10650
10651     updateMPYear : function(y){
10652         this.mpyear = y;
10653         var ys = this.mpYears.elements;
10654         for(var i = 1; i <= 10; i++){
10655             var td = ys[i-1], y2;
10656             if((i%2) == 0){
10657                 y2 = y + Math.round(i * .5);
10658                 td.firstChild.innerHTML = y2;
10659                 td.xyear = y2;
10660             }else{
10661                 y2 = y - (5-Math.round(i * .5));
10662                 td.firstChild.innerHTML = y2;
10663                 td.xyear = y2;
10664             }
10665             this.mpYears.item(i-1)[y2 == this.mpSelYear ? 'addClass' : 'removeClass']('x-date-mp-sel');
10666         }
10667     },
10668
10669     updateMPMonth : function(sm){
10670         this.mpMonths.each(function(m, a, i){
10671             m[m.dom.xmonth == sm ? 'addClass' : 'removeClass']('x-date-mp-sel');
10672         });
10673     },
10674
10675     selectMPMonth: function(m){
10676         
10677     },
10678
10679     onMonthClick : function(e, t){
10680         e.stopEvent();
10681         var el = new Roo.Element(t), pn;
10682         if(el.is('button.x-date-mp-cancel')){
10683             this.hideMonthPicker();
10684         }
10685         else if(el.is('button.x-date-mp-ok')){
10686             this.update(new Date(this.mpSelYear, this.mpSelMonth, (this.activeDate || this.value).getDate()));
10687             this.hideMonthPicker();
10688         }
10689         else if(pn = el.up('td.x-date-mp-month', 2)){
10690             this.mpMonths.removeClass('x-date-mp-sel');
10691             pn.addClass('x-date-mp-sel');
10692             this.mpSelMonth = pn.dom.xmonth;
10693         }
10694         else if(pn = el.up('td.x-date-mp-year', 2)){
10695             this.mpYears.removeClass('x-date-mp-sel');
10696             pn.addClass('x-date-mp-sel');
10697             this.mpSelYear = pn.dom.xyear;
10698         }
10699         else if(el.is('a.x-date-mp-prev')){
10700             this.updateMPYear(this.mpyear-10);
10701         }
10702         else if(el.is('a.x-date-mp-next')){
10703             this.updateMPYear(this.mpyear+10);
10704         }
10705     },
10706
10707     onMonthDblClick : function(e, t){
10708         e.stopEvent();
10709         var el = new Roo.Element(t), pn;
10710         if(pn = el.up('td.x-date-mp-month', 2)){
10711             this.update(new Date(this.mpSelYear, pn.dom.xmonth, (this.activeDate || this.value).getDate()));
10712             this.hideMonthPicker();
10713         }
10714         else if(pn = el.up('td.x-date-mp-year', 2)){
10715             this.update(new Date(pn.dom.xyear, this.mpSelMonth, (this.activeDate || this.value).getDate()));
10716             this.hideMonthPicker();
10717         }
10718     },
10719
10720     hideMonthPicker : function(disableAnim){
10721         if(this.monthPicker){
10722             if(disableAnim === true){
10723                 this.monthPicker.hide();
10724             }else{
10725                 this.monthPicker.slideOut('t', {duration:.2});
10726             }
10727         }
10728     },
10729
10730     // private
10731     showPrevMonth : function(e){
10732         this.update(this.activeDate.add("mo", -1));
10733     },
10734
10735     // private
10736     showNextMonth : function(e){
10737         this.update(this.activeDate.add("mo", 1));
10738     },
10739
10740     // private
10741     showPrevYear : function(){
10742         this.update(this.activeDate.add("y", -1));
10743     },
10744
10745     // private
10746     showNextYear : function(){
10747         this.update(this.activeDate.add("y", 1));
10748     },
10749
10750     // private
10751     handleMouseWheel : function(e){
10752         var delta = e.getWheelDelta();
10753         if(delta > 0){
10754             this.showPrevMonth();
10755             e.stopEvent();
10756         } else if(delta < 0){
10757             this.showNextMonth();
10758             e.stopEvent();
10759         }
10760     },
10761
10762     // private
10763     handleDateClick : function(e, t){
10764         e.stopEvent();
10765         if(t.dateValue && !Roo.fly(t.parentNode).hasClass("x-date-disabled")){
10766             this.setValue(new Date(t.dateValue));
10767             this.fireEvent("select", this, this.value);
10768         }
10769     },
10770
10771     // private
10772     selectToday : function(){
10773         this.setValue(new Date().clearTime());
10774         this.fireEvent("select", this, this.value);
10775     },
10776
10777     // private
10778     update : function(date)
10779     {
10780         var vd = this.activeDate;
10781         this.activeDate = date;
10782         if(vd && this.el){
10783             var t = date.getTime();
10784             if(vd.getMonth() == date.getMonth() && vd.getFullYear() == date.getFullYear()){
10785                 this.cells.removeClass("x-date-selected");
10786                 this.cells.each(function(c){
10787                    if(c.dom.firstChild.dateValue == t){
10788                        c.addClass("x-date-selected");
10789                        setTimeout(function(){
10790                             try{c.dom.firstChild.focus();}catch(e){}
10791                        }, 50);
10792                        return false;
10793                    }
10794                 });
10795                 return;
10796             }
10797         }
10798         
10799         var days = date.getDaysInMonth();
10800         var firstOfMonth = date.getFirstDateOfMonth();
10801         var startingPos = firstOfMonth.getDay()-this.startDay;
10802
10803         if(startingPos <= this.startDay){
10804             startingPos += 7;
10805         }
10806
10807         var pm = date.add("mo", -1);
10808         var prevStart = pm.getDaysInMonth()-startingPos;
10809
10810         var cells = this.cells.elements;
10811         var textEls = this.textNodes;
10812         days += startingPos;
10813
10814         // convert everything to numbers so it's fast
10815         var day = 86400000;
10816         var d = (new Date(pm.getFullYear(), pm.getMonth(), prevStart)).clearTime();
10817         var today = new Date().clearTime().getTime();
10818         var sel = date.clearTime().getTime();
10819         var min = this.minDate ? this.minDate.clearTime() : Number.NEGATIVE_INFINITY;
10820         var max = this.maxDate ? this.maxDate.clearTime() : Number.POSITIVE_INFINITY;
10821         var ddMatch = this.disabledDatesRE;
10822         var ddText = this.disabledDatesText;
10823         var ddays = this.disabledDays ? this.disabledDays.join("") : false;
10824         var ddaysText = this.disabledDaysText;
10825         var format = this.format;
10826
10827         var setCellClass = function(cal, cell){
10828             cell.title = "";
10829             var t = d.getTime();
10830             cell.firstChild.dateValue = t;
10831             if(t == today){
10832                 cell.className += " x-date-today";
10833                 cell.title = cal.todayText;
10834             }
10835             if(t == sel){
10836                 cell.className += " x-date-selected";
10837                 setTimeout(function(){
10838                     try{cell.firstChild.focus();}catch(e){}
10839                 }, 50);
10840             }
10841             // disabling
10842             if(t < min) {
10843                 cell.className = " x-date-disabled";
10844                 cell.title = cal.minText;
10845                 return;
10846             }
10847             if(t > max) {
10848                 cell.className = " x-date-disabled";
10849                 cell.title = cal.maxText;
10850                 return;
10851             }
10852             if(ddays){
10853                 if(ddays.indexOf(d.getDay()) != -1){
10854                     cell.title = ddaysText;
10855                     cell.className = " x-date-disabled";
10856                 }
10857             }
10858             if(ddMatch && format){
10859                 var fvalue = d.dateFormat(format);
10860                 if(ddMatch.test(fvalue)){
10861                     cell.title = ddText.replace("%0", fvalue);
10862                     cell.className = " x-date-disabled";
10863                 }
10864             }
10865         };
10866
10867         var i = 0;
10868         for(; i < startingPos; i++) {
10869             textEls[i].innerHTML = (++prevStart);
10870             d.setDate(d.getDate()+1);
10871             cells[i].className = "x-date-prevday";
10872             setCellClass(this, cells[i]);
10873         }
10874         for(; i < days; i++){
10875             intDay = i - startingPos + 1;
10876             textEls[i].innerHTML = (intDay);
10877             d.setDate(d.getDate()+1);
10878             cells[i].className = "x-date-active";
10879             setCellClass(this, cells[i]);
10880         }
10881         var extraDays = 0;
10882         for(; i < 42; i++) {
10883              textEls[i].innerHTML = (++extraDays);
10884              d.setDate(d.getDate()+1);
10885              cells[i].className = "x-date-nextday";
10886              setCellClass(this, cells[i]);
10887         }
10888
10889         this.mbtn.setText(this.monthNames[date.getMonth()] + " " + date.getFullYear());
10890         this.fireEvent('monthchange', this, date);
10891         
10892         if(!this.internalRender){
10893             var main = this.el.dom.firstChild;
10894             var w = main.offsetWidth;
10895             this.el.setWidth(w + this.el.getBorderWidth("lr"));
10896             Roo.fly(main).setWidth(w);
10897             this.internalRender = true;
10898             // opera does not respect the auto grow header center column
10899             // then, after it gets a width opera refuses to recalculate
10900             // without a second pass
10901             if(Roo.isOpera && !this.secondPass){
10902                 main.rows[0].cells[1].style.width = (w - (main.rows[0].cells[0].offsetWidth+main.rows[0].cells[2].offsetWidth)) + "px";
10903                 this.secondPass = true;
10904                 this.update.defer(10, this, [date]);
10905             }
10906         }
10907         
10908         
10909     }
10910 });        /*
10911  * Based on:
10912  * Ext JS Library 1.1.1
10913  * Copyright(c) 2006-2007, Ext JS, LLC.
10914  *
10915  * Originally Released Under LGPL - original licence link has changed is not relivant.
10916  *
10917  * Fork - LGPL
10918  * <script type="text/javascript">
10919  */
10920 /**
10921  * @class Roo.TabPanel
10922  * @extends Roo.util.Observable
10923  * A lightweight tab container.
10924  * <br><br>
10925  * Usage:
10926  * <pre><code>
10927 // basic tabs 1, built from existing content
10928 var tabs = new Roo.TabPanel("tabs1");
10929 tabs.addTab("script", "View Script");
10930 tabs.addTab("markup", "View Markup");
10931 tabs.activate("script");
10932
10933 // more advanced tabs, built from javascript
10934 var jtabs = new Roo.TabPanel("jtabs");
10935 jtabs.addTab("jtabs-1", "Normal Tab", "My content was added during construction.");
10936
10937 // set up the UpdateManager
10938 var tab2 = jtabs.addTab("jtabs-2", "Ajax Tab 1");
10939 var updater = tab2.getUpdateManager();
10940 updater.setDefaultUrl("ajax1.htm");
10941 tab2.on('activate', updater.refresh, updater, true);
10942
10943 // Use setUrl for Ajax loading
10944 var tab3 = jtabs.addTab("jtabs-3", "Ajax Tab 2");
10945 tab3.setUrl("ajax2.htm", null, true);
10946
10947 // Disabled tab
10948 var tab4 = jtabs.addTab("tabs1-5", "Disabled Tab", "Can't see me cause I'm disabled");
10949 tab4.disable();
10950
10951 jtabs.activate("jtabs-1");
10952  * </code></pre>
10953  * @constructor
10954  * Create a new TabPanel.
10955  * @param {String/HTMLElement/Roo.Element} container The id, DOM element or Roo.Element container where this TabPanel is to be rendered.
10956  * @param {Object/Boolean} config Config object to set any properties for this TabPanel, or true to render the tabs on the bottom.
10957  */
10958 Roo.TabPanel = function(container, config){
10959     /**
10960     * The container element for this TabPanel.
10961     * @type Roo.Element
10962     */
10963     this.el = Roo.get(container, true);
10964     if(config){
10965         if(typeof config == "boolean"){
10966             this.tabPosition = config ? "bottom" : "top";
10967         }else{
10968             Roo.apply(this, config);
10969         }
10970     }
10971     if(this.tabPosition == "bottom"){
10972         this.bodyEl = Roo.get(this.createBody(this.el.dom));
10973         this.el.addClass("x-tabs-bottom");
10974     }
10975     this.stripWrap = Roo.get(this.createStrip(this.el.dom), true);
10976     this.stripEl = Roo.get(this.createStripList(this.stripWrap.dom), true);
10977     this.stripBody = Roo.get(this.stripWrap.dom.firstChild.firstChild, true);
10978     if(Roo.isIE){
10979         Roo.fly(this.stripWrap.dom.firstChild).setStyle("overflow-x", "hidden");
10980     }
10981     if(this.tabPosition != "bottom"){
10982         /** The body element that contains {@link Roo.TabPanelItem} bodies. +
10983          * @type Roo.Element
10984          */
10985         this.bodyEl = Roo.get(this.createBody(this.el.dom));
10986         this.el.addClass("x-tabs-top");
10987     }
10988     this.items = [];
10989
10990     this.bodyEl.setStyle("position", "relative");
10991
10992     this.active = null;
10993     this.activateDelegate = this.activate.createDelegate(this);
10994
10995     this.addEvents({
10996         /**
10997          * @event tabchange
10998          * Fires when the active tab changes
10999          * @param {Roo.TabPanel} this
11000          * @param {Roo.TabPanelItem} activePanel The new active tab
11001          */
11002         "tabchange": true,
11003         /**
11004          * @event beforetabchange
11005          * Fires before the active tab changes, set cancel to true on the "e" parameter to cancel the change
11006          * @param {Roo.TabPanel} this
11007          * @param {Object} e Set cancel to true on this object to cancel the tab change
11008          * @param {Roo.TabPanelItem} tab The tab being changed to
11009          */
11010         "beforetabchange" : true
11011     });
11012
11013     Roo.EventManager.onWindowResize(this.onResize, this);
11014     this.cpad = this.el.getPadding("lr");
11015     this.hiddenCount = 0;
11016
11017
11018     // toolbar on the tabbar support...
11019     if (this.toolbar) {
11020         var tcfg = this.toolbar;
11021         tcfg.container = this.stripEl.child('td.x-tab-strip-toolbar');  
11022         this.toolbar = new Roo.Toolbar(tcfg);
11023         if (Roo.isSafari) {
11024             var tbl = tcfg.container.child('table', true);
11025             tbl.setAttribute('width', '100%');
11026         }
11027         
11028     }
11029    
11030
11031
11032     Roo.TabPanel.superclass.constructor.call(this);
11033 };
11034
11035 Roo.extend(Roo.TabPanel, Roo.util.Observable, {
11036     /*
11037      *@cfg {String} tabPosition "top" or "bottom" (defaults to "top")
11038      */
11039     tabPosition : "top",
11040     /*
11041      *@cfg {Number} currentTabWidth The width of the current tab (defaults to 0)
11042      */
11043     currentTabWidth : 0,
11044     /*
11045      *@cfg {Number} minTabWidth The minimum width of a tab (defaults to 40) (ignored if {@link #resizeTabs} is not true)
11046      */
11047     minTabWidth : 40,
11048     /*
11049      *@cfg {Number} maxTabWidth The maximum width of a tab (defaults to 250) (ignored if {@link #resizeTabs} is not true)
11050      */
11051     maxTabWidth : 250,
11052     /*
11053      *@cfg {Number} preferredTabWidth The preferred (default) width of a tab (defaults to 175) (ignored if {@link #resizeTabs} is not true)
11054      */
11055     preferredTabWidth : 175,
11056     /*
11057      *@cfg {Boolean} resizeTabs True to enable dynamic tab resizing (defaults to false)
11058      */
11059     resizeTabs : false,
11060     /*
11061      *@cfg {Boolean} monitorResize Set this to true to turn on window resize monitoring (ignored if {@link #resizeTabs} is not true) (defaults to true)
11062      */
11063     monitorResize : true,
11064     /*
11065      *@cfg {Object} toolbar xtype description of toolbar to show at the right of the tab bar. 
11066      */
11067     toolbar : false,
11068
11069     /**
11070      * Creates a new {@link Roo.TabPanelItem} by looking for an existing element with the provided id -- if it's not found it creates one.
11071      * @param {String} id The id of the div to use <b>or create</b>
11072      * @param {String} text The text for the tab
11073      * @param {String} content (optional) Content to put in the TabPanelItem body
11074      * @param {Boolean} closable (optional) True to create a close icon on the tab
11075      * @return {Roo.TabPanelItem} The created TabPanelItem
11076      */
11077     addTab : function(id, text, content, closable){
11078         var item = new Roo.TabPanelItem(this, id, text, closable);
11079         this.addTabItem(item);
11080         if(content){
11081             item.setContent(content);
11082         }
11083         return item;
11084     },
11085
11086     /**
11087      * Returns the {@link Roo.TabPanelItem} with the specified id/index
11088      * @param {String/Number} id The id or index of the TabPanelItem to fetch.
11089      * @return {Roo.TabPanelItem}
11090      */
11091     getTab : function(id){
11092         return this.items[id];
11093     },
11094
11095     /**
11096      * Hides the {@link Roo.TabPanelItem} with the specified id/index
11097      * @param {String/Number} id The id or index of the TabPanelItem to hide.
11098      */
11099     hideTab : function(id){
11100         var t = this.items[id];
11101         if(!t.isHidden()){
11102            t.setHidden(true);
11103            this.hiddenCount++;
11104            this.autoSizeTabs();
11105         }
11106     },
11107
11108     /**
11109      * "Unhides" the {@link Roo.TabPanelItem} with the specified id/index.
11110      * @param {String/Number} id The id or index of the TabPanelItem to unhide.
11111      */
11112     unhideTab : function(id){
11113         var t = this.items[id];
11114         if(t.isHidden()){
11115            t.setHidden(false);
11116            this.hiddenCount--;
11117            this.autoSizeTabs();
11118         }
11119     },
11120
11121     /**
11122      * Adds an existing {@link Roo.TabPanelItem}.
11123      * @param {Roo.TabPanelItem} item The TabPanelItem to add
11124      */
11125     addTabItem : function(item){
11126         this.items[item.id] = item;
11127         this.items.push(item);
11128         if(this.resizeTabs){
11129            item.setWidth(this.currentTabWidth || this.preferredTabWidth);
11130            this.autoSizeTabs();
11131         }else{
11132             item.autoSize();
11133         }
11134     },
11135
11136     /**
11137      * Removes a {@link Roo.TabPanelItem}.
11138      * @param {String/Number} id The id or index of the TabPanelItem to remove.
11139      */
11140     removeTab : function(id){
11141         var items = this.items;
11142         var tab = items[id];
11143         if(!tab) { return; }
11144         var index = items.indexOf(tab);
11145         if(this.active == tab && items.length > 1){
11146             var newTab = this.getNextAvailable(index);
11147             if(newTab) {
11148                 newTab.activate();
11149             }
11150         }
11151         this.stripEl.dom.removeChild(tab.pnode.dom);
11152         if(tab.bodyEl.dom.parentNode == this.bodyEl.dom){ // if it was moved already prevent error
11153             this.bodyEl.dom.removeChild(tab.bodyEl.dom);
11154         }
11155         items.splice(index, 1);
11156         delete this.items[tab.id];
11157         tab.fireEvent("close", tab);
11158         tab.purgeListeners();
11159         this.autoSizeTabs();
11160     },
11161
11162     getNextAvailable : function(start){
11163         var items = this.items;
11164         var index = start;
11165         // look for a next tab that will slide over to
11166         // replace the one being removed
11167         while(index < items.length){
11168             var item = items[++index];
11169             if(item && !item.isHidden()){
11170                 return item;
11171             }
11172         }
11173         // if one isn't found select the previous tab (on the left)
11174         index = start;
11175         while(index >= 0){
11176             var item = items[--index];
11177             if(item && !item.isHidden()){
11178                 return item;
11179             }
11180         }
11181         return null;
11182     },
11183
11184     /**
11185      * Disables a {@link Roo.TabPanelItem}. It cannot be the active tab, if it is this call is ignored.
11186      * @param {String/Number} id The id or index of the TabPanelItem to disable.
11187      */
11188     disableTab : function(id){
11189         var tab = this.items[id];
11190         if(tab && this.active != tab){
11191             tab.disable();
11192         }
11193     },
11194
11195     /**
11196      * Enables a {@link Roo.TabPanelItem} that is disabled.
11197      * @param {String/Number} id The id or index of the TabPanelItem to enable.
11198      */
11199     enableTab : function(id){
11200         var tab = this.items[id];
11201         tab.enable();
11202     },
11203
11204     /**
11205      * Activates a {@link Roo.TabPanelItem}. The currently active one will be deactivated.
11206      * @param {String/Number} id The id or index of the TabPanelItem to activate.
11207      * @return {Roo.TabPanelItem} The TabPanelItem.
11208      */
11209     activate : function(id){
11210         var tab = this.items[id];
11211         if(!tab){
11212             return null;
11213         }
11214         if(tab == this.active || tab.disabled){
11215             return tab;
11216         }
11217         var e = {};
11218         this.fireEvent("beforetabchange", this, e, tab);
11219         if(e.cancel !== true && !tab.disabled){
11220             if(this.active){
11221                 this.active.hide();
11222             }
11223             this.active = this.items[id];
11224             this.active.show();
11225             this.fireEvent("tabchange", this, this.active);
11226         }
11227         return tab;
11228     },
11229
11230     /**
11231      * Gets the active {@link Roo.TabPanelItem}.
11232      * @return {Roo.TabPanelItem} The active TabPanelItem or null if none are active.
11233      */
11234     getActiveTab : function(){
11235         return this.active;
11236     },
11237
11238     /**
11239      * Updates the tab body element to fit the height of the container element
11240      * for overflow scrolling
11241      * @param {Number} targetHeight (optional) Override the starting height from the elements height
11242      */
11243     syncHeight : function(targetHeight){
11244         var height = (targetHeight || this.el.getHeight())-this.el.getBorderWidth("tb")-this.el.getPadding("tb");
11245         var bm = this.bodyEl.getMargins();
11246         var newHeight = height-(this.stripWrap.getHeight()||0)-(bm.top+bm.bottom);
11247         this.bodyEl.setHeight(newHeight);
11248         return newHeight;
11249     },
11250
11251     onResize : function(){
11252         if(this.monitorResize){
11253             this.autoSizeTabs();
11254         }
11255     },
11256
11257     /**
11258      * Disables tab resizing while tabs are being added (if {@link #resizeTabs} is false this does nothing)
11259      */
11260     beginUpdate : function(){
11261         this.updating = true;
11262     },
11263
11264     /**
11265      * Stops an update and resizes the tabs (if {@link #resizeTabs} is false this does nothing)
11266      */
11267     endUpdate : function(){
11268         this.updating = false;
11269         this.autoSizeTabs();
11270     },
11271
11272     /**
11273      * Manual call to resize the tabs (if {@link #resizeTabs} is false this does nothing)
11274      */
11275     autoSizeTabs : function(){
11276         var count = this.items.length;
11277         var vcount = count - this.hiddenCount;
11278         if(!this.resizeTabs || count < 1 || vcount < 1 || this.updating) return;
11279         var w = Math.max(this.el.getWidth() - this.cpad, 10);
11280         var availWidth = Math.floor(w / vcount);
11281         var b = this.stripBody;
11282         if(b.getWidth() > w){
11283             var tabs = this.items;
11284             this.setTabWidth(Math.max(availWidth, this.minTabWidth)-2);
11285             if(availWidth < this.minTabWidth){
11286                 /*if(!this.sleft){    // incomplete scrolling code
11287                     this.createScrollButtons();
11288                 }
11289                 this.showScroll();
11290                 this.stripClip.setWidth(w - (this.sleft.getWidth()+this.sright.getWidth()));*/
11291             }
11292         }else{
11293             if(this.currentTabWidth < this.preferredTabWidth){
11294                 this.setTabWidth(Math.min(availWidth, this.preferredTabWidth)-2);
11295             }
11296         }
11297     },
11298
11299     /**
11300      * Returns the number of tabs in this TabPanel.
11301      * @return {Number}
11302      */
11303      getCount : function(){
11304          return this.items.length;
11305      },
11306
11307     /**
11308      * Resizes all the tabs to the passed width
11309      * @param {Number} The new width
11310      */
11311     setTabWidth : function(width){
11312         this.currentTabWidth = width;
11313         for(var i = 0, len = this.items.length; i < len; i++) {
11314                 if(!this.items[i].isHidden())this.items[i].setWidth(width);
11315         }
11316     },
11317
11318     /**
11319      * Destroys this TabPanel
11320      * @param {Boolean} removeEl (optional) True to remove the element from the DOM as well (defaults to undefined)
11321      */
11322     destroy : function(removeEl){
11323         Roo.EventManager.removeResizeListener(this.onResize, this);
11324         for(var i = 0, len = this.items.length; i < len; i++){
11325             this.items[i].purgeListeners();
11326         }
11327         if(removeEl === true){
11328             this.el.update("");
11329             this.el.remove();
11330         }
11331     }
11332 });
11333
11334 /**
11335  * @class Roo.TabPanelItem
11336  * @extends Roo.util.Observable
11337  * Represents an individual item (tab plus body) in a TabPanel.
11338  * @param {Roo.TabPanel} tabPanel The {@link Roo.TabPanel} this TabPanelItem belongs to
11339  * @param {String} id The id of this TabPanelItem
11340  * @param {String} text The text for the tab of this TabPanelItem
11341  * @param {Boolean} closable True to allow this TabPanelItem to be closable (defaults to false)
11342  */
11343 Roo.TabPanelItem = function(tabPanel, id, text, closable){
11344     /**
11345      * The {@link Roo.TabPanel} this TabPanelItem belongs to
11346      * @type Roo.TabPanel
11347      */
11348     this.tabPanel = tabPanel;
11349     /**
11350      * The id for this TabPanelItem
11351      * @type String
11352      */
11353     this.id = id;
11354     /** @private */
11355     this.disabled = false;
11356     /** @private */
11357     this.text = text;
11358     /** @private */
11359     this.loaded = false;
11360     this.closable = closable;
11361
11362     /**
11363      * The body element for this TabPanelItem.
11364      * @type Roo.Element
11365      */
11366     this.bodyEl = Roo.get(tabPanel.createItemBody(tabPanel.bodyEl.dom, id));
11367     this.bodyEl.setVisibilityMode(Roo.Element.VISIBILITY);
11368     this.bodyEl.setStyle("display", "block");
11369     this.bodyEl.setStyle("zoom", "1");
11370     this.hideAction();
11371
11372     var els = tabPanel.createStripElements(tabPanel.stripEl.dom, text, closable);
11373     /** @private */
11374     this.el = Roo.get(els.el, true);
11375     this.inner = Roo.get(els.inner, true);
11376     this.textEl = Roo.get(this.el.dom.firstChild.firstChild.firstChild, true);
11377     this.pnode = Roo.get(els.el.parentNode, true);
11378     this.el.on("mousedown", this.onTabMouseDown, this);
11379     this.el.on("click", this.onTabClick, this);
11380     /** @private */
11381     if(closable){
11382         var c = Roo.get(els.close, true);
11383         c.dom.title = this.closeText;
11384         c.addClassOnOver("close-over");
11385         c.on("click", this.closeClick, this);
11386      }
11387
11388     this.addEvents({
11389          /**
11390          * @event activate
11391          * Fires when this tab becomes the active tab.
11392          * @param {Roo.TabPanel} tabPanel The parent TabPanel
11393          * @param {Roo.TabPanelItem} this
11394          */
11395         "activate": true,
11396         /**
11397          * @event beforeclose
11398          * Fires before this tab is closed. To cancel the close, set cancel to true on e (e.cancel = true).
11399          * @param {Roo.TabPanelItem} this
11400          * @param {Object} e Set cancel to true on this object to cancel the close.
11401          */
11402         "beforeclose": true,
11403         /**
11404          * @event close
11405          * Fires when this tab is closed.
11406          * @param {Roo.TabPanelItem} this
11407          */
11408          "close": true,
11409         /**
11410          * @event deactivate
11411          * Fires when this tab is no longer the active tab.
11412          * @param {Roo.TabPanel} tabPanel The parent TabPanel
11413          * @param {Roo.TabPanelItem} this
11414          */
11415          "deactivate" : true
11416     });
11417     this.hidden = false;
11418
11419     Roo.TabPanelItem.superclass.constructor.call(this);
11420 };
11421
11422 Roo.extend(Roo.TabPanelItem, Roo.util.Observable, {
11423     purgeListeners : function(){
11424        Roo.util.Observable.prototype.purgeListeners.call(this);
11425        this.el.removeAllListeners();
11426     },
11427     /**
11428      * Shows this TabPanelItem -- this <b>does not</b> deactivate the currently active TabPanelItem.
11429      */
11430     show : function(){
11431         this.pnode.addClass("on");
11432         this.showAction();
11433         if(Roo.isOpera){
11434             this.tabPanel.stripWrap.repaint();
11435         }
11436         this.fireEvent("activate", this.tabPanel, this);
11437     },
11438
11439     /**
11440      * Returns true if this tab is the active tab.
11441      * @return {Boolean}
11442      */
11443     isActive : function(){
11444         return this.tabPanel.getActiveTab() == this;
11445     },
11446
11447     /**
11448      * Hides this TabPanelItem -- if you don't activate another TabPanelItem this could look odd.
11449      */
11450     hide : function(){
11451         this.pnode.removeClass("on");
11452         this.hideAction();
11453         this.fireEvent("deactivate", this.tabPanel, this);
11454     },
11455
11456     hideAction : function(){
11457         this.bodyEl.hide();
11458         this.bodyEl.setStyle("position", "absolute");
11459         this.bodyEl.setLeft("-20000px");
11460         this.bodyEl.setTop("-20000px");
11461     },
11462
11463     showAction : function(){
11464         this.bodyEl.setStyle("position", "relative");
11465         this.bodyEl.setTop("");
11466         this.bodyEl.setLeft("");
11467         this.bodyEl.show();
11468     },
11469
11470     /**
11471      * Set the tooltip for the tab.
11472      * @param {String} tooltip The tab's tooltip
11473      */
11474     setTooltip : function(text){
11475         if(Roo.QuickTips && Roo.QuickTips.isEnabled()){
11476             this.textEl.dom.qtip = text;
11477             this.textEl.dom.removeAttribute('title');
11478         }else{
11479             this.textEl.dom.title = text;
11480         }
11481     },
11482
11483     onTabClick : function(e){
11484         e.preventDefault();
11485         this.tabPanel.activate(this.id);
11486     },
11487
11488     onTabMouseDown : function(e){
11489         e.preventDefault();
11490         this.tabPanel.activate(this.id);
11491     },
11492
11493     getWidth : function(){
11494         return this.inner.getWidth();
11495     },
11496
11497     setWidth : function(width){
11498         var iwidth = width - this.pnode.getPadding("lr");
11499         this.inner.setWidth(iwidth);
11500         this.textEl.setWidth(iwidth-this.inner.getPadding("lr"));
11501         this.pnode.setWidth(width);
11502     },
11503
11504     /**
11505      * Show or hide the tab
11506      * @param {Boolean} hidden True to hide or false to show.
11507      */
11508     setHidden : function(hidden){
11509         this.hidden = hidden;
11510         this.pnode.setStyle("display", hidden ? "none" : "");
11511     },
11512
11513     /**
11514      * Returns true if this tab is "hidden"
11515      * @return {Boolean}
11516      */
11517     isHidden : function(){
11518         return this.hidden;
11519     },
11520
11521     /**
11522      * Returns the text for this tab
11523      * @return {String}
11524      */
11525     getText : function(){
11526         return this.text;
11527     },
11528
11529     autoSize : function(){
11530         //this.el.beginMeasure();
11531         this.textEl.setWidth(1);
11532         this.setWidth(this.textEl.dom.scrollWidth+this.pnode.getPadding("lr")+this.inner.getPadding("lr"));
11533         //this.el.endMeasure();
11534     },
11535
11536     /**
11537      * Sets the text for the tab (Note: this also sets the tooltip text)
11538      * @param {String} text The tab's text and tooltip
11539      */
11540     setText : function(text){
11541         this.text = text;
11542         this.textEl.update(text);
11543         this.setTooltip(text);
11544         if(!this.tabPanel.resizeTabs){
11545             this.autoSize();
11546         }
11547     },
11548     /**
11549      * Activates this TabPanelItem -- this <b>does</b> deactivate the currently active TabPanelItem.
11550      */
11551     activate : function(){
11552         this.tabPanel.activate(this.id);
11553     },
11554
11555     /**
11556      * Disables this TabPanelItem -- this does nothing if this is the active TabPanelItem.
11557      */
11558     disable : function(){
11559         if(this.tabPanel.active != this){
11560             this.disabled = true;
11561             this.pnode.addClass("disabled");
11562         }
11563     },
11564
11565     /**
11566      * Enables this TabPanelItem if it was previously disabled.
11567      */
11568     enable : function(){
11569         this.disabled = false;
11570         this.pnode.removeClass("disabled");
11571     },
11572
11573     /**
11574      * Sets the content for this TabPanelItem.
11575      * @param {String} content The content
11576      * @param {Boolean} loadScripts true to look for and load scripts
11577      */
11578     setContent : function(content, loadScripts){
11579         this.bodyEl.update(content, loadScripts);
11580     },
11581
11582     /**
11583      * Gets the {@link Roo.UpdateManager} for the body of this TabPanelItem. Enables you to perform Ajax updates.
11584      * @return {Roo.UpdateManager} The UpdateManager
11585      */
11586     getUpdateManager : function(){
11587         return this.bodyEl.getUpdateManager();
11588     },
11589
11590     /**
11591      * Set a URL to be used to load the content for this TabPanelItem.
11592      * @param {String/Function} url The URL to load the content from, or a function to call to get the URL
11593      * @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)
11594      * @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)
11595      * @return {Roo.UpdateManager} The UpdateManager
11596      */
11597     setUrl : function(url, params, loadOnce){
11598         if(this.refreshDelegate){
11599             this.un('activate', this.refreshDelegate);
11600         }
11601         this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
11602         this.on("activate", this.refreshDelegate);
11603         return this.bodyEl.getUpdateManager();
11604     },
11605
11606     /** @private */
11607     _handleRefresh : function(url, params, loadOnce){
11608         if(!loadOnce || !this.loaded){
11609             var updater = this.bodyEl.getUpdateManager();
11610             updater.update(url, params, this._setLoaded.createDelegate(this));
11611         }
11612     },
11613
11614     /**
11615      *   Forces a content refresh from the URL specified in the {@link #setUrl} method.
11616      *   Will fail silently if the setUrl method has not been called.
11617      *   This does not activate the panel, just updates its content.
11618      */
11619     refresh : function(){
11620         if(this.refreshDelegate){
11621            this.loaded = false;
11622            this.refreshDelegate();
11623         }
11624     },
11625
11626     /** @private */
11627     _setLoaded : function(){
11628         this.loaded = true;
11629     },
11630
11631     /** @private */
11632     closeClick : function(e){
11633         var o = {};
11634         e.stopEvent();
11635         this.fireEvent("beforeclose", this, o);
11636         if(o.cancel !== true){
11637             this.tabPanel.removeTab(this.id);
11638         }
11639     },
11640     /**
11641      * The text displayed in the tooltip for the close icon.
11642      * @type String
11643      */
11644     closeText : "Close this tab"
11645 });
11646
11647 /** @private */
11648 Roo.TabPanel.prototype.createStrip = function(container){
11649     var strip = document.createElement("div");
11650     strip.className = "x-tabs-wrap";
11651     container.appendChild(strip);
11652     return strip;
11653 };
11654 /** @private */
11655 Roo.TabPanel.prototype.createStripList = function(strip){
11656     // div wrapper for retard IE
11657     // returns the "tr" element.
11658     strip.innerHTML = '<div class="x-tabs-strip-wrap">'+
11659         '<table class="x-tabs-strip" cellspacing="0" cellpadding="0" border="0"><tbody><tr>'+
11660         '<td class="x-tab-strip-toolbar"></td></tr></tbody></table></div>';
11661     return strip.firstChild.firstChild.firstChild.firstChild;
11662 };
11663 /** @private */
11664 Roo.TabPanel.prototype.createBody = function(container){
11665     var body = document.createElement("div");
11666     Roo.id(body, "tab-body");
11667     Roo.fly(body).addClass("x-tabs-body");
11668     container.appendChild(body);
11669     return body;
11670 };
11671 /** @private */
11672 Roo.TabPanel.prototype.createItemBody = function(bodyEl, id){
11673     var body = Roo.getDom(id);
11674     if(!body){
11675         body = document.createElement("div");
11676         body.id = id;
11677     }
11678     Roo.fly(body).addClass("x-tabs-item-body");
11679     bodyEl.insertBefore(body, bodyEl.firstChild);
11680     return body;
11681 };
11682 /** @private */
11683 Roo.TabPanel.prototype.createStripElements = function(stripEl, text, closable){
11684     var td = document.createElement("td");
11685     stripEl.insertBefore(td, stripEl.childNodes[stripEl.childNodes.length-1]);
11686     //stripEl.appendChild(td);
11687     if(closable){
11688         td.className = "x-tabs-closable";
11689         if(!this.closeTpl){
11690             this.closeTpl = new Roo.Template(
11691                '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
11692                '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span>' +
11693                '<div unselectable="on" class="close-icon">&#160;</div></em></span></a>'
11694             );
11695         }
11696         var el = this.closeTpl.overwrite(td, {"text": text});
11697         var close = el.getElementsByTagName("div")[0];
11698         var inner = el.getElementsByTagName("em")[0];
11699         return {"el": el, "close": close, "inner": inner};
11700     } else {
11701         if(!this.tabTpl){
11702             this.tabTpl = new Roo.Template(
11703                '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
11704                '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span></em></span></a>'
11705             );
11706         }
11707         var el = this.tabTpl.overwrite(td, {"text": text});
11708         var inner = el.getElementsByTagName("em")[0];
11709         return {"el": el, "inner": inner};
11710     }
11711 };/*
11712  * Based on:
11713  * Ext JS Library 1.1.1
11714  * Copyright(c) 2006-2007, Ext JS, LLC.
11715  *
11716  * Originally Released Under LGPL - original licence link has changed is not relivant.
11717  *
11718  * Fork - LGPL
11719  * <script type="text/javascript">
11720  */
11721
11722 /**
11723  * @class Roo.Button
11724  * @extends Roo.util.Observable
11725  * Simple Button class
11726  * @cfg {String} text The button text
11727  * @cfg {String} icon The path to an image to display in the button (the image will be set as the background-image
11728  * CSS property of the button by default, so if you want a mixed icon/text button, set cls:"x-btn-text-icon")
11729  * @cfg {Function} handler A function called when the button is clicked (can be used instead of click event)
11730  * @cfg {Object} scope The scope of the handler
11731  * @cfg {Number} minWidth The minimum width for this button (used to give a set of buttons a common width)
11732  * @cfg {String/Object} tooltip The tooltip for the button - can be a string or QuickTips config object
11733  * @cfg {Boolean} hidden True to start hidden (defaults to false)
11734  * @cfg {Boolean} disabled True to start disabled (defaults to false)
11735  * @cfg {Boolean} pressed True to start pressed (only if enableToggle = true)
11736  * @cfg {String} toggleGroup The group this toggle button is a member of (only 1 per group can be pressed, only
11737    applies if enableToggle = true)
11738  * @cfg {String/HTMLElement/Element} renderTo The element to append the button to
11739  * @cfg {Boolean/Object} repeat True to repeat fire the click event while the mouse is down. This can also be
11740   an {@link Roo.util.ClickRepeater} config object (defaults to false).
11741  * @constructor
11742  * Create a new button
11743  * @param {Object} config The config object
11744  */
11745 Roo.Button = function(renderTo, config)
11746 {
11747     if (!config) {
11748         config = renderTo;
11749         renderTo = config.renderTo || false;
11750     }
11751     
11752     Roo.apply(this, config);
11753     this.addEvents({
11754         /**
11755              * @event click
11756              * Fires when this button is clicked
11757              * @param {Button} this
11758              * @param {EventObject} e The click event
11759              */
11760             "click" : true,
11761         /**
11762              * @event toggle
11763              * Fires when the "pressed" state of this button changes (only if enableToggle = true)
11764              * @param {Button} this
11765              * @param {Boolean} pressed
11766              */
11767             "toggle" : true,
11768         /**
11769              * @event mouseover
11770              * Fires when the mouse hovers over the button
11771              * @param {Button} this
11772              * @param {Event} e The event object
11773              */
11774         'mouseover' : true,
11775         /**
11776              * @event mouseout
11777              * Fires when the mouse exits the button
11778              * @param {Button} this
11779              * @param {Event} e The event object
11780              */
11781         'mouseout': true,
11782          /**
11783              * @event render
11784              * Fires when the button is rendered
11785              * @param {Button} this
11786              */
11787         'render': true
11788     });
11789     if(this.menu){
11790         this.menu = Roo.menu.MenuMgr.get(this.menu);
11791     }
11792     // register listeners first!!  - so render can be captured..
11793     Roo.util.Observable.call(this);
11794     if(renderTo){
11795         this.render(renderTo);
11796     }
11797     
11798   
11799 };
11800
11801 Roo.extend(Roo.Button, Roo.util.Observable, {
11802     /**
11803      * 
11804      */
11805     
11806     /**
11807      * Read-only. True if this button is hidden
11808      * @type Boolean
11809      */
11810     hidden : false,
11811     /**
11812      * Read-only. True if this button is disabled
11813      * @type Boolean
11814      */
11815     disabled : false,
11816     /**
11817      * Read-only. True if this button is pressed (only if enableToggle = true)
11818      * @type Boolean
11819      */
11820     pressed : false,
11821
11822     /**
11823      * @cfg {Number} tabIndex 
11824      * The DOM tabIndex for this button (defaults to undefined)
11825      */
11826     tabIndex : undefined,
11827
11828     /**
11829      * @cfg {Boolean} enableToggle
11830      * True to enable pressed/not pressed toggling (defaults to false)
11831      */
11832     enableToggle: false,
11833     /**
11834      * @cfg {Mixed} menu
11835      * Standard menu attribute consisting of a reference to a menu object, a menu id or a menu config blob (defaults to undefined).
11836      */
11837     menu : undefined,
11838     /**
11839      * @cfg {String} menuAlign
11840      * The position to align the menu to (see {@link Roo.Element#alignTo} for more details, defaults to 'tl-bl?').
11841      */
11842     menuAlign : "tl-bl?",
11843
11844     /**
11845      * @cfg {String} iconCls
11846      * A css class which sets a background image to be used as the icon for this button (defaults to undefined).
11847      */
11848     iconCls : undefined,
11849     /**
11850      * @cfg {String} type
11851      * The button's type, corresponding to the DOM input element type attribute.  Either "submit," "reset" or "button" (default).
11852      */
11853     type : 'button',
11854
11855     // private
11856     menuClassTarget: 'tr',
11857
11858     /**
11859      * @cfg {String} clickEvent
11860      * The type of event to map to the button's event handler (defaults to 'click')
11861      */
11862     clickEvent : 'click',
11863
11864     /**
11865      * @cfg {Boolean} handleMouseEvents
11866      * False to disable visual cues on mouseover, mouseout and mousedown (defaults to true)
11867      */
11868     handleMouseEvents : true,
11869
11870     /**
11871      * @cfg {String} tooltipType
11872      * The type of tooltip to use. Either "qtip" (default) for QuickTips or "title" for title attribute.
11873      */
11874     tooltipType : 'qtip',
11875
11876     /**
11877      * @cfg {String} cls
11878      * A CSS class to apply to the button's main element.
11879      */
11880     
11881     /**
11882      * @cfg {Roo.Template} template (Optional)
11883      * An {@link Roo.Template} with which to create the Button's main element. This Template must
11884      * contain numeric substitution parameter 0 if it is to display the tRoo property. Changing the template could
11885      * require code modifications if required elements (e.g. a button) aren't present.
11886      */
11887
11888     // private
11889     render : function(renderTo){
11890         var btn;
11891         if(this.hideParent){
11892             this.parentEl = Roo.get(renderTo);
11893         }
11894         if(!this.dhconfig){
11895             if(!this.template){
11896                 if(!Roo.Button.buttonTemplate){
11897                     // hideous table template
11898                     Roo.Button.buttonTemplate = new Roo.Template(
11899                         '<table border="0" cellpadding="0" cellspacing="0" class="x-btn-wrap"><tbody><tr>',
11900                         '<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>',
11901                         "</tr></tbody></table>");
11902                 }
11903                 this.template = Roo.Button.buttonTemplate;
11904             }
11905             btn = this.template.append(renderTo, [this.text || '&#160;', this.type], true);
11906             var btnEl = btn.child("button:first");
11907             btnEl.on('focus', this.onFocus, this);
11908             btnEl.on('blur', this.onBlur, this);
11909             if(this.cls){
11910                 btn.addClass(this.cls);
11911             }
11912             if(this.icon){
11913                 btnEl.setStyle('background-image', 'url(' +this.icon +')');
11914             }
11915             if(this.iconCls){
11916                 btnEl.addClass(this.iconCls);
11917                 if(!this.cls){
11918                     btn.addClass(this.text ? 'x-btn-text-icon' : 'x-btn-icon');
11919                 }
11920             }
11921             if(this.tabIndex !== undefined){
11922                 btnEl.dom.tabIndex = this.tabIndex;
11923             }
11924             if(this.tooltip){
11925                 if(typeof this.tooltip == 'object'){
11926                     Roo.QuickTips.tips(Roo.apply({
11927                           target: btnEl.id
11928                     }, this.tooltip));
11929                 } else {
11930                     btnEl.dom[this.tooltipType] = this.tooltip;
11931                 }
11932             }
11933         }else{
11934             btn = Roo.DomHelper.append(Roo.get(renderTo).dom, this.dhconfig, true);
11935         }
11936         this.el = btn;
11937         if(this.id){
11938             this.el.dom.id = this.el.id = this.id;
11939         }
11940         if(this.menu){
11941             this.el.child(this.menuClassTarget).addClass("x-btn-with-menu");
11942             this.menu.on("show", this.onMenuShow, this);
11943             this.menu.on("hide", this.onMenuHide, this);
11944         }
11945         btn.addClass("x-btn");
11946         if(Roo.isIE && !Roo.isIE7){
11947             this.autoWidth.defer(1, this);
11948         }else{
11949             this.autoWidth();
11950         }
11951         if(this.handleMouseEvents){
11952             btn.on("mouseover", this.onMouseOver, this);
11953             btn.on("mouseout", this.onMouseOut, this);
11954             btn.on("mousedown", this.onMouseDown, this);
11955         }
11956         btn.on(this.clickEvent, this.onClick, this);
11957         //btn.on("mouseup", this.onMouseUp, this);
11958         if(this.hidden){
11959             this.hide();
11960         }
11961         if(this.disabled){
11962             this.disable();
11963         }
11964         Roo.ButtonToggleMgr.register(this);
11965         if(this.pressed){
11966             this.el.addClass("x-btn-pressed");
11967         }
11968         if(this.repeat){
11969             var repeater = new Roo.util.ClickRepeater(btn,
11970                 typeof this.repeat == "object" ? this.repeat : {}
11971             );
11972             repeater.on("click", this.onClick,  this);
11973         }
11974         
11975         this.fireEvent('render', this);
11976         
11977     },
11978     /**
11979      * Returns the button's underlying element
11980      * @return {Roo.Element} The element
11981      */
11982     getEl : function(){
11983         return this.el;  
11984     },
11985     
11986     /**
11987      * Destroys this Button and removes any listeners.
11988      */
11989     destroy : function(){
11990         Roo.ButtonToggleMgr.unregister(this);
11991         this.el.removeAllListeners();
11992         this.purgeListeners();
11993         this.el.remove();
11994     },
11995
11996     // private
11997     autoWidth : function(){
11998         if(this.el){
11999             this.el.setWidth("auto");
12000             if(Roo.isIE7 && Roo.isStrict){
12001                 var ib = this.el.child('button');
12002                 if(ib && ib.getWidth() > 20){
12003                     ib.clip();
12004                     ib.setWidth(Roo.util.TextMetrics.measure(ib, this.text).width+ib.getFrameWidth('lr'));
12005                 }
12006             }
12007             if(this.minWidth){
12008                 if(this.hidden){
12009                     this.el.beginMeasure();
12010                 }
12011                 if(this.el.getWidth() < this.minWidth){
12012                     this.el.setWidth(this.minWidth);
12013                 }
12014                 if(this.hidden){
12015                     this.el.endMeasure();
12016                 }
12017             }
12018         }
12019     },
12020
12021     /**
12022      * Assigns this button's click handler
12023      * @param {Function} handler The function to call when the button is clicked
12024      * @param {Object} scope (optional) Scope for the function passed in
12025      */
12026     setHandler : function(handler, scope){
12027         this.handler = handler;
12028         this.scope = scope;  
12029     },
12030     
12031     /**
12032      * Sets this button's text
12033      * @param {String} text The button text
12034      */
12035     setText : function(text){
12036         this.text = text;
12037         if(this.el){
12038             this.el.child("td.x-btn-center button.x-btn-text").update(text);
12039         }
12040         this.autoWidth();
12041     },
12042     
12043     /**
12044      * Gets the text for this button
12045      * @return {String} The button text
12046      */
12047     getText : function(){
12048         return this.text;  
12049     },
12050     
12051     /**
12052      * Show this button
12053      */
12054     show: function(){
12055         this.hidden = false;
12056         if(this.el){
12057             this[this.hideParent? 'parentEl' : 'el'].setStyle("display", "");
12058         }
12059     },
12060     
12061     /**
12062      * Hide this button
12063      */
12064     hide: function(){
12065         this.hidden = true;
12066         if(this.el){
12067             this[this.hideParent? 'parentEl' : 'el'].setStyle("display", "none");
12068         }
12069     },
12070     
12071     /**
12072      * Convenience function for boolean show/hide
12073      * @param {Boolean} visible True to show, false to hide
12074      */
12075     setVisible: function(visible){
12076         if(visible) {
12077             this.show();
12078         }else{
12079             this.hide();
12080         }
12081     },
12082     
12083     /**
12084      * If a state it passed, it becomes the pressed state otherwise the current state is toggled.
12085      * @param {Boolean} state (optional) Force a particular state
12086      */
12087     toggle : function(state){
12088         state = state === undefined ? !this.pressed : state;
12089         if(state != this.pressed){
12090             if(state){
12091                 this.el.addClass("x-btn-pressed");
12092                 this.pressed = true;
12093                 this.fireEvent("toggle", this, true);
12094             }else{
12095                 this.el.removeClass("x-btn-pressed");
12096                 this.pressed = false;
12097                 this.fireEvent("toggle", this, false);
12098             }
12099             if(this.toggleHandler){
12100                 this.toggleHandler.call(this.scope || this, this, state);
12101             }
12102         }
12103     },
12104     
12105     /**
12106      * Focus the button
12107      */
12108     focus : function(){
12109         this.el.child('button:first').focus();
12110     },
12111     
12112     /**
12113      * Disable this button
12114      */
12115     disable : function(){
12116         if(this.el){
12117             this.el.addClass("x-btn-disabled");
12118         }
12119         this.disabled = true;
12120     },
12121     
12122     /**
12123      * Enable this button
12124      */
12125     enable : function(){
12126         if(this.el){
12127             this.el.removeClass("x-btn-disabled");
12128         }
12129         this.disabled = false;
12130     },
12131
12132     /**
12133      * Convenience function for boolean enable/disable
12134      * @param {Boolean} enabled True to enable, false to disable
12135      */
12136     setDisabled : function(v){
12137         this[v !== true ? "enable" : "disable"]();
12138     },
12139
12140     // private
12141     onClick : function(e){
12142         if(e){
12143             e.preventDefault();
12144         }
12145         if(e.button != 0){
12146             return;
12147         }
12148         if(!this.disabled){
12149             if(this.enableToggle){
12150                 this.toggle();
12151             }
12152             if(this.menu && !this.menu.isVisible()){
12153                 this.menu.show(this.el, this.menuAlign);
12154             }
12155             this.fireEvent("click", this, e);
12156             if(this.handler){
12157                 this.el.removeClass("x-btn-over");
12158                 this.handler.call(this.scope || this, this, e);
12159             }
12160         }
12161     },
12162     // private
12163     onMouseOver : function(e){
12164         if(!this.disabled){
12165             this.el.addClass("x-btn-over");
12166             this.fireEvent('mouseover', this, e);
12167         }
12168     },
12169     // private
12170     onMouseOut : function(e){
12171         if(!e.within(this.el,  true)){
12172             this.el.removeClass("x-btn-over");
12173             this.fireEvent('mouseout', this, e);
12174         }
12175     },
12176     // private
12177     onFocus : function(e){
12178         if(!this.disabled){
12179             this.el.addClass("x-btn-focus");
12180         }
12181     },
12182     // private
12183     onBlur : function(e){
12184         this.el.removeClass("x-btn-focus");
12185     },
12186     // private
12187     onMouseDown : function(e){
12188         if(!this.disabled && e.button == 0){
12189             this.el.addClass("x-btn-click");
12190             Roo.get(document).on('mouseup', this.onMouseUp, this);
12191         }
12192     },
12193     // private
12194     onMouseUp : function(e){
12195         if(e.button == 0){
12196             this.el.removeClass("x-btn-click");
12197             Roo.get(document).un('mouseup', this.onMouseUp, this);
12198         }
12199     },
12200     // private
12201     onMenuShow : function(e){
12202         this.el.addClass("x-btn-menu-active");
12203     },
12204     // private
12205     onMenuHide : function(e){
12206         this.el.removeClass("x-btn-menu-active");
12207     }   
12208 });
12209
12210 // Private utility class used by Button
12211 Roo.ButtonToggleMgr = function(){
12212    var groups = {};
12213    
12214    function toggleGroup(btn, state){
12215        if(state){
12216            var g = groups[btn.toggleGroup];
12217            for(var i = 0, l = g.length; i < l; i++){
12218                if(g[i] != btn){
12219                    g[i].toggle(false);
12220                }
12221            }
12222        }
12223    }
12224    
12225    return {
12226        register : function(btn){
12227            if(!btn.toggleGroup){
12228                return;
12229            }
12230            var g = groups[btn.toggleGroup];
12231            if(!g){
12232                g = groups[btn.toggleGroup] = [];
12233            }
12234            g.push(btn);
12235            btn.on("toggle", toggleGroup);
12236        },
12237        
12238        unregister : function(btn){
12239            if(!btn.toggleGroup){
12240                return;
12241            }
12242            var g = groups[btn.toggleGroup];
12243            if(g){
12244                g.remove(btn);
12245                btn.un("toggle", toggleGroup);
12246            }
12247        }
12248    };
12249 }();/*
12250  * Based on:
12251  * Ext JS Library 1.1.1
12252  * Copyright(c) 2006-2007, Ext JS, LLC.
12253  *
12254  * Originally Released Under LGPL - original licence link has changed is not relivant.
12255  *
12256  * Fork - LGPL
12257  * <script type="text/javascript">
12258  */
12259  
12260 /**
12261  * @class Roo.SplitButton
12262  * @extends Roo.Button
12263  * A split button that provides a built-in dropdown arrow that can fire an event separately from the default
12264  * click event of the button.  Typically this would be used to display a dropdown menu that provides additional
12265  * options to the primary button action, but any custom handler can provide the arrowclick implementation.
12266  * @cfg {Function} arrowHandler A function called when the arrow button is clicked (can be used instead of click event)
12267  * @cfg {String} arrowTooltip The title attribute of the arrow
12268  * @constructor
12269  * Create a new menu button
12270  * @param {String/HTMLElement/Element} renderTo The element to append the button to
12271  * @param {Object} config The config object
12272  */
12273 Roo.SplitButton = function(renderTo, config){
12274     Roo.SplitButton.superclass.constructor.call(this, renderTo, config);
12275     /**
12276      * @event arrowclick
12277      * Fires when this button's arrow is clicked
12278      * @param {SplitButton} this
12279      * @param {EventObject} e The click event
12280      */
12281     this.addEvents({"arrowclick":true});
12282 };
12283
12284 Roo.extend(Roo.SplitButton, Roo.Button, {
12285     render : function(renderTo){
12286         // this is one sweet looking template!
12287         var tpl = new Roo.Template(
12288             '<table cellspacing="0" class="x-btn-menu-wrap x-btn"><tr><td>',
12289             '<table cellspacing="0" class="x-btn-wrap x-btn-menu-text-wrap"><tbody>',
12290             '<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>',
12291             "</tbody></table></td><td>",
12292             '<table cellspacing="0" class="x-btn-wrap x-btn-menu-arrow-wrap"><tbody>',
12293             '<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>',
12294             "</tbody></table></td></tr></table>"
12295         );
12296         var btn = tpl.append(renderTo, [this.text, this.type], true);
12297         var btnEl = btn.child("button");
12298         if(this.cls){
12299             btn.addClass(this.cls);
12300         }
12301         if(this.icon){
12302             btnEl.setStyle('background-image', 'url(' +this.icon +')');
12303         }
12304         if(this.iconCls){
12305             btnEl.addClass(this.iconCls);
12306             if(!this.cls){
12307                 btn.addClass(this.text ? 'x-btn-text-icon' : 'x-btn-icon');
12308             }
12309         }
12310         this.el = btn;
12311         if(this.handleMouseEvents){
12312             btn.on("mouseover", this.onMouseOver, this);
12313             btn.on("mouseout", this.onMouseOut, this);
12314             btn.on("mousedown", this.onMouseDown, this);
12315             btn.on("mouseup", this.onMouseUp, this);
12316         }
12317         btn.on(this.clickEvent, this.onClick, this);
12318         if(this.tooltip){
12319             if(typeof this.tooltip == 'object'){
12320                 Roo.QuickTips.tips(Roo.apply({
12321                       target: btnEl.id
12322                 }, this.tooltip));
12323             } else {
12324                 btnEl.dom[this.tooltipType] = this.tooltip;
12325             }
12326         }
12327         if(this.arrowTooltip){
12328             btn.child("button:nth(2)").dom[this.tooltipType] = this.arrowTooltip;
12329         }
12330         if(this.hidden){
12331             this.hide();
12332         }
12333         if(this.disabled){
12334             this.disable();
12335         }
12336         if(this.pressed){
12337             this.el.addClass("x-btn-pressed");
12338         }
12339         if(Roo.isIE && !Roo.isIE7){
12340             this.autoWidth.defer(1, this);
12341         }else{
12342             this.autoWidth();
12343         }
12344         if(this.menu){
12345             this.menu.on("show", this.onMenuShow, this);
12346             this.menu.on("hide", this.onMenuHide, this);
12347         }
12348         this.fireEvent('render', this);
12349     },
12350
12351     // private
12352     autoWidth : function(){
12353         if(this.el){
12354             var tbl = this.el.child("table:first");
12355             var tbl2 = this.el.child("table:last");
12356             this.el.setWidth("auto");
12357             tbl.setWidth("auto");
12358             if(Roo.isIE7 && Roo.isStrict){
12359                 var ib = this.el.child('button:first');
12360                 if(ib && ib.getWidth() > 20){
12361                     ib.clip();
12362                     ib.setWidth(Roo.util.TextMetrics.measure(ib, this.text).width+ib.getFrameWidth('lr'));
12363                 }
12364             }
12365             if(this.minWidth){
12366                 if(this.hidden){
12367                     this.el.beginMeasure();
12368                 }
12369                 if((tbl.getWidth()+tbl2.getWidth()) < this.minWidth){
12370                     tbl.setWidth(this.minWidth-tbl2.getWidth());
12371                 }
12372                 if(this.hidden){
12373                     this.el.endMeasure();
12374                 }
12375             }
12376             this.el.setWidth(tbl.getWidth()+tbl2.getWidth());
12377         } 
12378     },
12379     /**
12380      * Sets this button's click handler
12381      * @param {Function} handler The function to call when the button is clicked
12382      * @param {Object} scope (optional) Scope for the function passed above
12383      */
12384     setHandler : function(handler, scope){
12385         this.handler = handler;
12386         this.scope = scope;  
12387     },
12388     
12389     /**
12390      * Sets this button's arrow click handler
12391      * @param {Function} handler The function to call when the arrow is clicked
12392      * @param {Object} scope (optional) Scope for the function passed above
12393      */
12394     setArrowHandler : function(handler, scope){
12395         this.arrowHandler = handler;
12396         this.scope = scope;  
12397     },
12398     
12399     /**
12400      * Focus the button
12401      */
12402     focus : function(){
12403         if(this.el){
12404             this.el.child("button:first").focus();
12405         }
12406     },
12407
12408     // private
12409     onClick : function(e){
12410         e.preventDefault();
12411         if(!this.disabled){
12412             if(e.getTarget(".x-btn-menu-arrow-wrap")){
12413                 if(this.menu && !this.menu.isVisible()){
12414                     this.menu.show(this.el, this.menuAlign);
12415                 }
12416                 this.fireEvent("arrowclick", this, e);
12417                 if(this.arrowHandler){
12418                     this.arrowHandler.call(this.scope || this, this, e);
12419                 }
12420             }else{
12421                 this.fireEvent("click", this, e);
12422                 if(this.handler){
12423                     this.handler.call(this.scope || this, this, e);
12424                 }
12425             }
12426         }
12427     },
12428     // private
12429     onMouseDown : function(e){
12430         if(!this.disabled){
12431             Roo.fly(e.getTarget("table")).addClass("x-btn-click");
12432         }
12433     },
12434     // private
12435     onMouseUp : function(e){
12436         Roo.fly(e.getTarget("table")).removeClass("x-btn-click");
12437     }   
12438 });
12439
12440
12441 // backwards compat
12442 Roo.MenuButton = Roo.SplitButton;/*
12443  * Based on:
12444  * Ext JS Library 1.1.1
12445  * Copyright(c) 2006-2007, Ext JS, LLC.
12446  *
12447  * Originally Released Under LGPL - original licence link has changed is not relivant.
12448  *
12449  * Fork - LGPL
12450  * <script type="text/javascript">
12451  */
12452
12453 /**
12454  * @class Roo.Toolbar
12455  * Basic Toolbar class.
12456  * @constructor
12457  * Creates a new Toolbar
12458  * @param {Object} container The config object
12459  */ 
12460 Roo.Toolbar = function(container, buttons, config)
12461 {
12462     /// old consturctor format still supported..
12463     if(container instanceof Array){ // omit the container for later rendering
12464         buttons = container;
12465         config = buttons;
12466         container = null;
12467     }
12468     if (typeof(container) == 'object' && container.xtype) {
12469         config = container;
12470         container = config.container;
12471         buttons = config.buttons || []; // not really - use items!!
12472     }
12473     var xitems = [];
12474     if (config && config.items) {
12475         xitems = config.items;
12476         delete config.items;
12477     }
12478     Roo.apply(this, config);
12479     this.buttons = buttons;
12480     
12481     if(container){
12482         this.render(container);
12483     }
12484     this.xitems = xitems;
12485     Roo.each(xitems, function(b) {
12486         this.add(b);
12487     }, this);
12488     
12489 };
12490
12491 Roo.Toolbar.prototype = {
12492     /**
12493      * @cfg {Array} items
12494      * array of button configs or elements to add (will be converted to a MixedCollection)
12495      */
12496     
12497     /**
12498      * @cfg {String/HTMLElement/Element} container
12499      * The id or element that will contain the toolbar
12500      */
12501     // private
12502     render : function(ct){
12503         this.el = Roo.get(ct);
12504         if(this.cls){
12505             this.el.addClass(this.cls);
12506         }
12507         // using a table allows for vertical alignment
12508         // 100% width is needed by Safari...
12509         this.el.update('<div class="x-toolbar x-small-editor"><table cellspacing="0"><tr></tr></table></div>');
12510         this.tr = this.el.child("tr", true);
12511         var autoId = 0;
12512         this.items = new Roo.util.MixedCollection(false, function(o){
12513             return o.id || ("item" + (++autoId));
12514         });
12515         if(this.buttons){
12516             this.add.apply(this, this.buttons);
12517             delete this.buttons;
12518         }
12519     },
12520
12521     /**
12522      * Adds element(s) to the toolbar -- this function takes a variable number of 
12523      * arguments of mixed type and adds them to the toolbar.
12524      * @param {Mixed} arg1 The following types of arguments are all valid:<br />
12525      * <ul>
12526      * <li>{@link Roo.Toolbar.Button} config: A valid button config object (equivalent to {@link #addButton})</li>
12527      * <li>HtmlElement: Any standard HTML element (equivalent to {@link #addElement})</li>
12528      * <li>Field: Any form field (equivalent to {@link #addField})</li>
12529      * <li>Item: Any subclass of {@link Roo.Toolbar.Item} (equivalent to {@link #addItem})</li>
12530      * <li>String: Any generic string (gets wrapped in a {@link Roo.Toolbar.TextItem}, equivalent to {@link #addText}).
12531      * Note that there are a few special strings that are treated differently as explained nRoo.</li>
12532      * <li>'separator' or '-': Creates a separator element (equivalent to {@link #addSeparator})</li>
12533      * <li>' ': Creates a spacer element (equivalent to {@link #addSpacer})</li>
12534      * <li>'->': Creates a fill element (equivalent to {@link #addFill})</li>
12535      * </ul>
12536      * @param {Mixed} arg2
12537      * @param {Mixed} etc.
12538      */
12539     add : function(){
12540         var a = arguments, l = a.length;
12541         for(var i = 0; i < l; i++){
12542             this._add(a[i]);
12543         }
12544     },
12545     // private..
12546     _add : function(el) {
12547         
12548         if (el.xtype) {
12549             el = Roo.factory(el, typeof(Roo.Toolbar[el.xtype]) == 'undefined' ? Roo.form : Roo.Toolbar);
12550         }
12551         
12552         if (el.applyTo){ // some kind of form field
12553             return this.addField(el);
12554         } 
12555         if (el.render){ // some kind of Toolbar.Item
12556             return this.addItem(el);
12557         }
12558         if (typeof el == "string"){ // string
12559             if(el == "separator" || el == "-"){
12560                 return this.addSeparator();
12561             }
12562             if (el == " "){
12563                 return this.addSpacer();
12564             }
12565             if(el == "->"){
12566                 return this.addFill();
12567             }
12568             return this.addText(el);
12569             
12570         }
12571         if(el.tagName){ // element
12572             return this.addElement(el);
12573         }
12574         if(typeof el == "object"){ // must be button config?
12575             return this.addButton(el);
12576         }
12577         // and now what?!?!
12578         return false;
12579         
12580     },
12581     
12582     /**
12583      * Add an Xtype element
12584      * @param {Object} xtype Xtype Object
12585      * @return {Object} created Object
12586      */
12587     addxtype : function(e){
12588         return this.add(e);  
12589     },
12590     
12591     /**
12592      * Returns the Element for this toolbar.
12593      * @return {Roo.Element}
12594      */
12595     getEl : function(){
12596         return this.el;  
12597     },
12598     
12599     /**
12600      * Adds a separator
12601      * @return {Roo.Toolbar.Item} The separator item
12602      */
12603     addSeparator : function(){
12604         return this.addItem(new Roo.Toolbar.Separator());
12605     },
12606
12607     /**
12608      * Adds a spacer element
12609      * @return {Roo.Toolbar.Spacer} The spacer item
12610      */
12611     addSpacer : function(){
12612         return this.addItem(new Roo.Toolbar.Spacer());
12613     },
12614
12615     /**
12616      * Adds a fill element that forces subsequent additions to the right side of the toolbar
12617      * @return {Roo.Toolbar.Fill} The fill item
12618      */
12619     addFill : function(){
12620         return this.addItem(new Roo.Toolbar.Fill());
12621     },
12622
12623     /**
12624      * Adds any standard HTML element to the toolbar
12625      * @param {String/HTMLElement/Element} el The element or id of the element to add
12626      * @return {Roo.Toolbar.Item} The element's item
12627      */
12628     addElement : function(el){
12629         return this.addItem(new Roo.Toolbar.Item(el));
12630     },
12631     /**
12632      * Collection of items on the toolbar.. (only Toolbar Items, so use fields to retrieve fields)
12633      * @type Roo.util.MixedCollection  
12634      */
12635     items : false,
12636      
12637     /**
12638      * Adds any Toolbar.Item or subclass
12639      * @param {Roo.Toolbar.Item} item
12640      * @return {Roo.Toolbar.Item} The item
12641      */
12642     addItem : function(item){
12643         var td = this.nextBlock();
12644         item.render(td);
12645         this.items.add(item);
12646         return item;
12647     },
12648     
12649     /**
12650      * Adds a button (or buttons). See {@link Roo.Toolbar.Button} for more info on the config.
12651      * @param {Object/Array} config A button config or array of configs
12652      * @return {Roo.Toolbar.Button/Array}
12653      */
12654     addButton : function(config){
12655         if(config instanceof Array){
12656             var buttons = [];
12657             for(var i = 0, len = config.length; i < len; i++) {
12658                 buttons.push(this.addButton(config[i]));
12659             }
12660             return buttons;
12661         }
12662         var b = config;
12663         if(!(config instanceof Roo.Toolbar.Button)){
12664             b = config.split ?
12665                 new Roo.Toolbar.SplitButton(config) :
12666                 new Roo.Toolbar.Button(config);
12667         }
12668         var td = this.nextBlock();
12669         b.render(td);
12670         this.items.add(b);
12671         return b;
12672     },
12673     
12674     /**
12675      * Adds text to the toolbar
12676      * @param {String} text The text to add
12677      * @return {Roo.Toolbar.Item} The element's item
12678      */
12679     addText : function(text){
12680         return this.addItem(new Roo.Toolbar.TextItem(text));
12681     },
12682     
12683     /**
12684      * Inserts any {@link Roo.Toolbar.Item}/{@link Roo.Toolbar.Button} at the specified index.
12685      * @param {Number} index The index where the item is to be inserted
12686      * @param {Object/Roo.Toolbar.Item/Roo.Toolbar.Button (may be Array)} item The button, or button config object to be inserted.
12687      * @return {Roo.Toolbar.Button/Item}
12688      */
12689     insertButton : function(index, item){
12690         if(item instanceof Array){
12691             var buttons = [];
12692             for(var i = 0, len = item.length; i < len; i++) {
12693                buttons.push(this.insertButton(index + i, item[i]));
12694             }
12695             return buttons;
12696         }
12697         if (!(item instanceof Roo.Toolbar.Button)){
12698            item = new Roo.Toolbar.Button(item);
12699         }
12700         var td = document.createElement("td");
12701         this.tr.insertBefore(td, this.tr.childNodes[index]);
12702         item.render(td);
12703         this.items.insert(index, item);
12704         return item;
12705     },
12706     
12707     /**
12708      * Adds a new element to the toolbar from the passed {@link Roo.DomHelper} config.
12709      * @param {Object} config
12710      * @return {Roo.Toolbar.Item} The element's item
12711      */
12712     addDom : function(config, returnEl){
12713         var td = this.nextBlock();
12714         Roo.DomHelper.overwrite(td, config);
12715         var ti = new Roo.Toolbar.Item(td.firstChild);
12716         ti.render(td);
12717         this.items.add(ti);
12718         return ti;
12719     },
12720
12721     /**
12722      * Collection of fields on the toolbar.. usefull for quering (value is false if there are no fields)
12723      * @type Roo.util.MixedCollection  
12724      */
12725     fields : false,
12726     
12727     /**
12728      * Adds a dynamically rendered Roo.form field (TextField, ComboBox, etc).
12729      * Note: the field should not have been rendered yet. For a field that has already been
12730      * rendered, use {@link #addElement}.
12731      * @param {Roo.form.Field} field
12732      * @return {Roo.ToolbarItem}
12733      */
12734      
12735       
12736     addField : function(field) {
12737         if (!this.fields) {
12738             var autoId = 0;
12739             this.fields = new Roo.util.MixedCollection(false, function(o){
12740                 return o.id || ("item" + (++autoId));
12741             });
12742
12743         }
12744         
12745         var td = this.nextBlock();
12746         field.render(td);
12747         var ti = new Roo.Toolbar.Item(td.firstChild);
12748         ti.render(td);
12749         this.items.add(ti);
12750         this.fields.add(field);
12751         return ti;
12752     },
12753     /**
12754      * Hide the toolbar
12755      * @method hide
12756      */
12757      
12758       
12759     hide : function()
12760     {
12761         this.el.child('div').setVisibilityMode(Roo.Element.DISPLAY);
12762         this.el.child('div').hide();
12763     },
12764     /**
12765      * Show the toolbar
12766      * @method show
12767      */
12768     show : function()
12769     {
12770         this.el.child('div').show();
12771     },
12772       
12773     // private
12774     nextBlock : function(){
12775         var td = document.createElement("td");
12776         this.tr.appendChild(td);
12777         return td;
12778     },
12779
12780     // private
12781     destroy : function(){
12782         if(this.items){ // rendered?
12783             Roo.destroy.apply(Roo, this.items.items);
12784         }
12785         if(this.fields){ // rendered?
12786             Roo.destroy.apply(Roo, this.fields.items);
12787         }
12788         Roo.Element.uncache(this.el, this.tr);
12789     }
12790 };
12791
12792 /**
12793  * @class Roo.Toolbar.Item
12794  * The base class that other classes should extend in order to get some basic common toolbar item functionality.
12795  * @constructor
12796  * Creates a new Item
12797  * @param {HTMLElement} el 
12798  */
12799 Roo.Toolbar.Item = function(el){
12800     this.el = Roo.getDom(el);
12801     this.id = Roo.id(this.el);
12802     this.hidden = false;
12803 };
12804
12805 Roo.Toolbar.Item.prototype = {
12806     
12807     /**
12808      * Get this item's HTML Element
12809      * @return {HTMLElement}
12810      */
12811     getEl : function(){
12812        return this.el;  
12813     },
12814
12815     // private
12816     render : function(td){
12817         this.td = td;
12818         td.appendChild(this.el);
12819     },
12820     
12821     /**
12822      * Removes and destroys this item.
12823      */
12824     destroy : function(){
12825         this.td.parentNode.removeChild(this.td);
12826     },
12827     
12828     /**
12829      * Shows this item.
12830      */
12831     show: function(){
12832         this.hidden = false;
12833         this.td.style.display = "";
12834     },
12835     
12836     /**
12837      * Hides this item.
12838      */
12839     hide: function(){
12840         this.hidden = true;
12841         this.td.style.display = "none";
12842     },
12843     
12844     /**
12845      * Convenience function for boolean show/hide.
12846      * @param {Boolean} visible true to show/false to hide
12847      */
12848     setVisible: function(visible){
12849         if(visible) {
12850             this.show();
12851         }else{
12852             this.hide();
12853         }
12854     },
12855     
12856     /**
12857      * Try to focus this item.
12858      */
12859     focus : function(){
12860         Roo.fly(this.el).focus();
12861     },
12862     
12863     /**
12864      * Disables this item.
12865      */
12866     disable : function(){
12867         Roo.fly(this.td).addClass("x-item-disabled");
12868         this.disabled = true;
12869         this.el.disabled = true;
12870     },
12871     
12872     /**
12873      * Enables this item.
12874      */
12875     enable : function(){
12876         Roo.fly(this.td).removeClass("x-item-disabled");
12877         this.disabled = false;
12878         this.el.disabled = false;
12879     }
12880 };
12881
12882
12883 /**
12884  * @class Roo.Toolbar.Separator
12885  * @extends Roo.Toolbar.Item
12886  * A simple toolbar separator class
12887  * @constructor
12888  * Creates a new Separator
12889  */
12890 Roo.Toolbar.Separator = function(){
12891     var s = document.createElement("span");
12892     s.className = "ytb-sep";
12893     Roo.Toolbar.Separator.superclass.constructor.call(this, s);
12894 };
12895 Roo.extend(Roo.Toolbar.Separator, Roo.Toolbar.Item, {
12896     enable:Roo.emptyFn,
12897     disable:Roo.emptyFn,
12898     focus:Roo.emptyFn
12899 });
12900
12901 /**
12902  * @class Roo.Toolbar.Spacer
12903  * @extends Roo.Toolbar.Item
12904  * A simple element that adds extra horizontal space to a toolbar.
12905  * @constructor
12906  * Creates a new Spacer
12907  */
12908 Roo.Toolbar.Spacer = function(){
12909     var s = document.createElement("div");
12910     s.className = "ytb-spacer";
12911     Roo.Toolbar.Spacer.superclass.constructor.call(this, s);
12912 };
12913 Roo.extend(Roo.Toolbar.Spacer, Roo.Toolbar.Item, {
12914     enable:Roo.emptyFn,
12915     disable:Roo.emptyFn,
12916     focus:Roo.emptyFn
12917 });
12918
12919 /**
12920  * @class Roo.Toolbar.Fill
12921  * @extends Roo.Toolbar.Spacer
12922  * A simple element that adds a greedy (100% width) horizontal space to a toolbar.
12923  * @constructor
12924  * Creates a new Spacer
12925  */
12926 Roo.Toolbar.Fill = Roo.extend(Roo.Toolbar.Spacer, {
12927     // private
12928     render : function(td){
12929         td.style.width = '100%';
12930         Roo.Toolbar.Fill.superclass.render.call(this, td);
12931     }
12932 });
12933
12934 /**
12935  * @class Roo.Toolbar.TextItem
12936  * @extends Roo.Toolbar.Item
12937  * A simple class that renders text directly into a toolbar.
12938  * @constructor
12939  * Creates a new TextItem
12940  * @param {String} text
12941  */
12942 Roo.Toolbar.TextItem = function(text){
12943     if (typeof(text) == 'object') {
12944         text = text.text;
12945     }
12946     var s = document.createElement("span");
12947     s.className = "ytb-text";
12948     s.innerHTML = text;
12949     Roo.Toolbar.TextItem.superclass.constructor.call(this, s);
12950 };
12951 Roo.extend(Roo.Toolbar.TextItem, Roo.Toolbar.Item, {
12952     enable:Roo.emptyFn,
12953     disable:Roo.emptyFn,
12954     focus:Roo.emptyFn
12955 });
12956
12957 /**
12958  * @class Roo.Toolbar.Button
12959  * @extends Roo.Button
12960  * A button that renders into a toolbar.
12961  * @constructor
12962  * Creates a new Button
12963  * @param {Object} config A standard {@link Roo.Button} config object
12964  */
12965 Roo.Toolbar.Button = function(config){
12966     Roo.Toolbar.Button.superclass.constructor.call(this, null, config);
12967 };
12968 Roo.extend(Roo.Toolbar.Button, Roo.Button, {
12969     render : function(td){
12970         this.td = td;
12971         Roo.Toolbar.Button.superclass.render.call(this, td);
12972     },
12973     
12974     /**
12975      * Removes and destroys this button
12976      */
12977     destroy : function(){
12978         Roo.Toolbar.Button.superclass.destroy.call(this);
12979         this.td.parentNode.removeChild(this.td);
12980     },
12981     
12982     /**
12983      * Shows this button
12984      */
12985     show: function(){
12986         this.hidden = false;
12987         this.td.style.display = "";
12988     },
12989     
12990     /**
12991      * Hides this button
12992      */
12993     hide: function(){
12994         this.hidden = true;
12995         this.td.style.display = "none";
12996     },
12997
12998     /**
12999      * Disables this item
13000      */
13001     disable : function(){
13002         Roo.fly(this.td).addClass("x-item-disabled");
13003         this.disabled = true;
13004     },
13005
13006     /**
13007      * Enables this item
13008      */
13009     enable : function(){
13010         Roo.fly(this.td).removeClass("x-item-disabled");
13011         this.disabled = false;
13012     }
13013 });
13014 // backwards compat
13015 Roo.ToolbarButton = Roo.Toolbar.Button;
13016
13017 /**
13018  * @class Roo.Toolbar.SplitButton
13019  * @extends Roo.SplitButton
13020  * A menu button that renders into a toolbar.
13021  * @constructor
13022  * Creates a new SplitButton
13023  * @param {Object} config A standard {@link Roo.SplitButton} config object
13024  */
13025 Roo.Toolbar.SplitButton = function(config){
13026     Roo.Toolbar.SplitButton.superclass.constructor.call(this, null, config);
13027 };
13028 Roo.extend(Roo.Toolbar.SplitButton, Roo.SplitButton, {
13029     render : function(td){
13030         this.td = td;
13031         Roo.Toolbar.SplitButton.superclass.render.call(this, td);
13032     },
13033     
13034     /**
13035      * Removes and destroys this button
13036      */
13037     destroy : function(){
13038         Roo.Toolbar.SplitButton.superclass.destroy.call(this);
13039         this.td.parentNode.removeChild(this.td);
13040     },
13041     
13042     /**
13043      * Shows this button
13044      */
13045     show: function(){
13046         this.hidden = false;
13047         this.td.style.display = "";
13048     },
13049     
13050     /**
13051      * Hides this button
13052      */
13053     hide: function(){
13054         this.hidden = true;
13055         this.td.style.display = "none";
13056     }
13057 });
13058
13059 // backwards compat
13060 Roo.Toolbar.MenuButton = Roo.Toolbar.SplitButton;/*
13061  * Based on:
13062  * Ext JS Library 1.1.1
13063  * Copyright(c) 2006-2007, Ext JS, LLC.
13064  *
13065  * Originally Released Under LGPL - original licence link has changed is not relivant.
13066  *
13067  * Fork - LGPL
13068  * <script type="text/javascript">
13069  */
13070  
13071 /**
13072  * @class Roo.PagingToolbar
13073  * @extends Roo.Toolbar
13074  * A specialized toolbar that is bound to a {@link Roo.data.Store} and provides automatic paging controls.
13075  * @constructor
13076  * Create a new PagingToolbar
13077  * @param {Object} config The config object
13078  */
13079 Roo.PagingToolbar = function(el, ds, config)
13080 {
13081     // old args format still supported... - xtype is prefered..
13082     if (typeof(el) == 'object' && el.xtype) {
13083         // created from xtype...
13084         config = el;
13085         ds = el.dataSource;
13086         el = config.container;
13087     }
13088     var items = [];
13089     if (config.items) {
13090         items = config.items;
13091         config.items = [];
13092     }
13093     
13094     Roo.PagingToolbar.superclass.constructor.call(this, el, null, config);
13095     this.ds = ds;
13096     this.cursor = 0;
13097     this.renderButtons(this.el);
13098     this.bind(ds);
13099     
13100     // supprot items array.
13101    
13102     Roo.each(items, function(e) {
13103         this.add(Roo.factory(e));
13104     },this);
13105     
13106 };
13107
13108 Roo.extend(Roo.PagingToolbar, Roo.Toolbar, {
13109     /**
13110      * @cfg {Roo.data.Store} dataSource
13111      * The underlying data store providing the paged data
13112      */
13113     /**
13114      * @cfg {String/HTMLElement/Element} container
13115      * container The id or element that will contain the toolbar
13116      */
13117     /**
13118      * @cfg {Boolean} displayInfo
13119      * True to display the displayMsg (defaults to false)
13120      */
13121     /**
13122      * @cfg {Number} pageSize
13123      * The number of records to display per page (defaults to 20)
13124      */
13125     pageSize: 20,
13126     /**
13127      * @cfg {String} displayMsg
13128      * The paging status message to display (defaults to "Displaying {start} - {end} of {total}")
13129      */
13130     displayMsg : 'Displaying {0} - {1} of {2}',
13131     /**
13132      * @cfg {String} emptyMsg
13133      * The message to display when no records are found (defaults to "No data to display")
13134      */
13135     emptyMsg : 'No data to display',
13136     /**
13137      * Customizable piece of the default paging text (defaults to "Page")
13138      * @type String
13139      */
13140     beforePageText : "Page",
13141     /**
13142      * Customizable piece of the default paging text (defaults to "of %0")
13143      * @type String
13144      */
13145     afterPageText : "of {0}",
13146     /**
13147      * Customizable piece of the default paging text (defaults to "First Page")
13148      * @type String
13149      */
13150     firstText : "First Page",
13151     /**
13152      * Customizable piece of the default paging text (defaults to "Previous Page")
13153      * @type String
13154      */
13155     prevText : "Previous Page",
13156     /**
13157      * Customizable piece of the default paging text (defaults to "Next Page")
13158      * @type String
13159      */
13160     nextText : "Next Page",
13161     /**
13162      * Customizable piece of the default paging text (defaults to "Last Page")
13163      * @type String
13164      */
13165     lastText : "Last Page",
13166     /**
13167      * Customizable piece of the default paging text (defaults to "Refresh")
13168      * @type String
13169      */
13170     refreshText : "Refresh",
13171
13172     // private
13173     renderButtons : function(el){
13174         Roo.PagingToolbar.superclass.render.call(this, el);
13175         this.first = this.addButton({
13176             tooltip: this.firstText,
13177             cls: "x-btn-icon x-grid-page-first",
13178             disabled: true,
13179             handler: this.onClick.createDelegate(this, ["first"])
13180         });
13181         this.prev = this.addButton({
13182             tooltip: this.prevText,
13183             cls: "x-btn-icon x-grid-page-prev",
13184             disabled: true,
13185             handler: this.onClick.createDelegate(this, ["prev"])
13186         });
13187         //this.addSeparator();
13188         this.add(this.beforePageText);
13189         this.field = Roo.get(this.addDom({
13190            tag: "input",
13191            type: "text",
13192            size: "3",
13193            value: "1",
13194            cls: "x-grid-page-number"
13195         }).el);
13196         this.field.on("keydown", this.onPagingKeydown, this);
13197         this.field.on("focus", function(){this.dom.select();});
13198         this.afterTextEl = this.addText(String.format(this.afterPageText, 1));
13199         this.field.setHeight(18);
13200         //this.addSeparator();
13201         this.next = this.addButton({
13202             tooltip: this.nextText,
13203             cls: "x-btn-icon x-grid-page-next",
13204             disabled: true,
13205             handler: this.onClick.createDelegate(this, ["next"])
13206         });
13207         this.last = this.addButton({
13208             tooltip: this.lastText,
13209             cls: "x-btn-icon x-grid-page-last",
13210             disabled: true,
13211             handler: this.onClick.createDelegate(this, ["last"])
13212         });
13213         //this.addSeparator();
13214         this.loading = this.addButton({
13215             tooltip: this.refreshText,
13216             cls: "x-btn-icon x-grid-loading",
13217             handler: this.onClick.createDelegate(this, ["refresh"])
13218         });
13219
13220         if(this.displayInfo){
13221             this.displayEl = Roo.fly(this.el.dom.firstChild).createChild({cls:'x-paging-info'});
13222         }
13223     },
13224
13225     // private
13226     updateInfo : function(){
13227         if(this.displayEl){
13228             var count = this.ds.getCount();
13229             var msg = count == 0 ?
13230                 this.emptyMsg :
13231                 String.format(
13232                     this.displayMsg,
13233                     this.cursor+1, this.cursor+count, this.ds.getTotalCount()    
13234                 );
13235             this.displayEl.update(msg);
13236         }
13237     },
13238
13239     // private
13240     onLoad : function(ds, r, o){
13241        this.cursor = o.params ? o.params.start : 0;
13242        var d = this.getPageData(), ap = d.activePage, ps = d.pages;
13243
13244        this.afterTextEl.el.innerHTML = String.format(this.afterPageText, d.pages);
13245        this.field.dom.value = ap;
13246        this.first.setDisabled(ap == 1);
13247        this.prev.setDisabled(ap == 1);
13248        this.next.setDisabled(ap == ps);
13249        this.last.setDisabled(ap == ps);
13250        this.loading.enable();
13251        this.updateInfo();
13252     },
13253
13254     // private
13255     getPageData : function(){
13256         var total = this.ds.getTotalCount();
13257         return {
13258             total : total,
13259             activePage : Math.ceil((this.cursor+this.pageSize)/this.pageSize),
13260             pages :  total < this.pageSize ? 1 : Math.ceil(total/this.pageSize)
13261         };
13262     },
13263
13264     // private
13265     onLoadError : function(){
13266         this.loading.enable();
13267     },
13268
13269     // private
13270     onPagingKeydown : function(e){
13271         var k = e.getKey();
13272         var d = this.getPageData();
13273         if(k == e.RETURN){
13274             var v = this.field.dom.value, pageNum;
13275             if(!v || isNaN(pageNum = parseInt(v, 10))){
13276                 this.field.dom.value = d.activePage;
13277                 return;
13278             }
13279             pageNum = Math.min(Math.max(1, pageNum), d.pages) - 1;
13280             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
13281             e.stopEvent();
13282         }
13283         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))
13284         {
13285           var pageNum = (k == e.HOME || (k == e.DOWN && e.ctrlKey) || (k == e.LEFT && e.ctrlKey) || (k == e.PAGEDOWN && e.ctrlKey)) ? 1 : d.pages;
13286           this.field.dom.value = pageNum;
13287           this.ds.load({params:{start: (pageNum - 1) * this.pageSize, limit: this.pageSize}});
13288           e.stopEvent();
13289         }
13290         else if(k == e.UP || k == e.RIGHT || k == e.PAGEUP || k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN)
13291         {
13292           var v = this.field.dom.value, pageNum; 
13293           var increment = (e.shiftKey) ? 10 : 1;
13294           if(k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN)
13295             increment *= -1;
13296           if(!v || isNaN(pageNum = parseInt(v, 10))) {
13297             this.field.dom.value = d.activePage;
13298             return;
13299           }
13300           else if(parseInt(v, 10) + increment >= 1 & parseInt(v, 10) + increment <= d.pages)
13301           {
13302             this.field.dom.value = parseInt(v, 10) + increment;
13303             pageNum = Math.min(Math.max(1, pageNum + increment), d.pages) - 1;
13304             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
13305           }
13306           e.stopEvent();
13307         }
13308     },
13309
13310     // private
13311     beforeLoad : function(){
13312         if(this.loading){
13313             this.loading.disable();
13314         }
13315     },
13316
13317     // private
13318     onClick : function(which){
13319         var ds = this.ds;
13320         switch(which){
13321             case "first":
13322                 ds.load({params:{start: 0, limit: this.pageSize}});
13323             break;
13324             case "prev":
13325                 ds.load({params:{start: Math.max(0, this.cursor-this.pageSize), limit: this.pageSize}});
13326             break;
13327             case "next":
13328                 ds.load({params:{start: this.cursor+this.pageSize, limit: this.pageSize}});
13329             break;
13330             case "last":
13331                 var total = ds.getTotalCount();
13332                 var extra = total % this.pageSize;
13333                 var lastStart = extra ? (total - extra) : total-this.pageSize;
13334                 ds.load({params:{start: lastStart, limit: this.pageSize}});
13335             break;
13336             case "refresh":
13337                 ds.load({params:{start: this.cursor, limit: this.pageSize}});
13338             break;
13339         }
13340     },
13341
13342     /**
13343      * Unbinds the paging toolbar from the specified {@link Roo.data.Store}
13344      * @param {Roo.data.Store} store The data store to unbind
13345      */
13346     unbind : function(ds){
13347         ds.un("beforeload", this.beforeLoad, this);
13348         ds.un("load", this.onLoad, this);
13349         ds.un("loadexception", this.onLoadError, this);
13350         ds.un("remove", this.updateInfo, this);
13351         ds.un("add", this.updateInfo, this);
13352         this.ds = undefined;
13353     },
13354
13355     /**
13356      * Binds the paging toolbar to the specified {@link Roo.data.Store}
13357      * @param {Roo.data.Store} store The data store to bind
13358      */
13359     bind : function(ds){
13360         ds.on("beforeload", this.beforeLoad, this);
13361         ds.on("load", this.onLoad, this);
13362         ds.on("loadexception", this.onLoadError, this);
13363         ds.on("remove", this.updateInfo, this);
13364         ds.on("add", this.updateInfo, this);
13365         this.ds = ds;
13366     }
13367 });/*
13368  * Based on:
13369  * Ext JS Library 1.1.1
13370  * Copyright(c) 2006-2007, Ext JS, LLC.
13371  *
13372  * Originally Released Under LGPL - original licence link has changed is not relivant.
13373  *
13374  * Fork - LGPL
13375  * <script type="text/javascript">
13376  */
13377
13378 /**
13379  * @class Roo.Resizable
13380  * @extends Roo.util.Observable
13381  * <p>Applies drag handles to an element to make it resizable. The drag handles are inserted into the element
13382  * and positioned absolute. Some elements, such as a textarea or image, don't support this. To overcome that, you can wrap
13383  * 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
13384  * the element will be wrapped for you automatically.</p>
13385  * <p>Here is the list of valid resize handles:</p>
13386  * <pre>
13387 Value   Description
13388 ------  -------------------
13389  'n'     north
13390  's'     south
13391  'e'     east
13392  'w'     west
13393  'nw'    northwest
13394  'sw'    southwest
13395  'se'    southeast
13396  'ne'    northeast
13397  'hd'    horizontal drag
13398  'all'   all
13399 </pre>
13400  * <p>Here's an example showing the creation of a typical Resizable:</p>
13401  * <pre><code>
13402 var resizer = new Roo.Resizable("element-id", {
13403     handles: 'all',
13404     minWidth: 200,
13405     minHeight: 100,
13406     maxWidth: 500,
13407     maxHeight: 400,
13408     pinned: true
13409 });
13410 resizer.on("resize", myHandler);
13411 </code></pre>
13412  * <p>To hide a particular handle, set its display to none in CSS, or through script:<br>
13413  * resizer.east.setDisplayed(false);</p>
13414  * @cfg {Boolean/String/Element} resizeChild True to resize the first child, or id/element to resize (defaults to false)
13415  * @cfg {Array/String} adjustments String "auto" or an array [width, height] with values to be <b>added</b> to the
13416  * resize operation's new size (defaults to [0, 0])
13417  * @cfg {Number} minWidth The minimum width for the element (defaults to 5)
13418  * @cfg {Number} minHeight The minimum height for the element (defaults to 5)
13419  * @cfg {Number} maxWidth The maximum width for the element (defaults to 10000)
13420  * @cfg {Number} maxHeight The maximum height for the element (defaults to 10000)
13421  * @cfg {Boolean} enabled False to disable resizing (defaults to true)
13422  * @cfg {Boolean} wrap True to wrap an element with a div if needed (required for textareas and images, defaults to false)
13423  * @cfg {Number} width The width of the element in pixels (defaults to null)
13424  * @cfg {Number} height The height of the element in pixels (defaults to null)
13425  * @cfg {Boolean} animate True to animate the resize (not compatible with dynamic sizing, defaults to false)
13426  * @cfg {Number} duration Animation duration if animate = true (defaults to .35)
13427  * @cfg {Boolean} dynamic True to resize the element while dragging instead of using a proxy (defaults to false)
13428  * @cfg {String} handles String consisting of the resize handles to display (defaults to undefined)
13429  * @cfg {Boolean} multiDirectional <b>Deprecated</b>.  The old style of adding multi-direction resize handles, deprecated
13430  * in favor of the handles config option (defaults to false)
13431  * @cfg {Boolean} disableTrackOver True to disable mouse tracking. This is only applied at config time. (defaults to false)
13432  * @cfg {String} easing Animation easing if animate = true (defaults to 'easingOutStrong')
13433  * @cfg {Number} widthIncrement The increment to snap the width resize in pixels (dynamic must be true, defaults to 0)
13434  * @cfg {Number} heightIncrement The increment to snap the height resize in pixels (dynamic must be true, defaults to 0)
13435  * @cfg {Boolean} pinned True to ensure that the resize handles are always visible, false to display them only when the
13436  * user mouses over the resizable borders. This is only applied at config time. (defaults to false)
13437  * @cfg {Boolean} preserveRatio True to preserve the original ratio between height and width during resize (defaults to false)
13438  * @cfg {Boolean} transparent True for transparent handles. This is only applied at config time. (defaults to false)
13439  * @cfg {Number} minX The minimum allowed page X for the element (only used for west resizing, defaults to 0)
13440  * @cfg {Number} minY The minimum allowed page Y for the element (only used for north resizing, defaults to 0)
13441  * @cfg {Boolean} draggable Convenience to initialize drag drop (defaults to false)
13442  * @constructor
13443  * Create a new resizable component
13444  * @param {String/HTMLElement/Roo.Element} el The id or element to resize
13445  * @param {Object} config configuration options
13446   */
13447 Roo.Resizable = function(el, config)
13448 {
13449     this.el = Roo.get(el);
13450
13451     if(config && config.wrap){
13452         config.resizeChild = this.el;
13453         this.el = this.el.wrap(typeof config.wrap == "object" ? config.wrap : {cls:"xresizable-wrap"});
13454         this.el.id = this.el.dom.id = config.resizeChild.id + "-rzwrap";
13455         this.el.setStyle("overflow", "hidden");
13456         this.el.setPositioning(config.resizeChild.getPositioning());
13457         config.resizeChild.clearPositioning();
13458         if(!config.width || !config.height){
13459             var csize = config.resizeChild.getSize();
13460             this.el.setSize(csize.width, csize.height);
13461         }
13462         if(config.pinned && !config.adjustments){
13463             config.adjustments = "auto";
13464         }
13465     }
13466
13467     this.proxy = this.el.createProxy({tag: "div", cls: "x-resizable-proxy", id: this.el.id + "-rzproxy"});
13468     this.proxy.unselectable();
13469     this.proxy.enableDisplayMode('block');
13470
13471     Roo.apply(this, config);
13472
13473     if(this.pinned){
13474         this.disableTrackOver = true;
13475         this.el.addClass("x-resizable-pinned");
13476     }
13477     // if the element isn't positioned, make it relative
13478     var position = this.el.getStyle("position");
13479     if(position != "absolute" && position != "fixed"){
13480         this.el.setStyle("position", "relative");
13481     }
13482     if(!this.handles){ // no handles passed, must be legacy style
13483         this.handles = 's,e,se';
13484         if(this.multiDirectional){
13485             this.handles += ',n,w';
13486         }
13487     }
13488     if(this.handles == "all"){
13489         this.handles = "n s e w ne nw se sw";
13490     }
13491     var hs = this.handles.split(/\s*?[,;]\s*?| /);
13492     var ps = Roo.Resizable.positions;
13493     for(var i = 0, len = hs.length; i < len; i++){
13494         if(hs[i] && ps[hs[i]]){
13495             var pos = ps[hs[i]];
13496             this[pos] = new Roo.Resizable.Handle(this, pos, this.disableTrackOver, this.transparent);
13497         }
13498     }
13499     // legacy
13500     this.corner = this.southeast;
13501     
13502     // updateBox = the box can move..
13503     if(this.handles.indexOf("n") != -1 || this.handles.indexOf("w") != -1 || this.handles.indexOf("hd") != -1) {
13504         this.updateBox = true;
13505     }
13506
13507     this.activeHandle = null;
13508
13509     if(this.resizeChild){
13510         if(typeof this.resizeChild == "boolean"){
13511             this.resizeChild = Roo.get(this.el.dom.firstChild, true);
13512         }else{
13513             this.resizeChild = Roo.get(this.resizeChild, true);
13514         }
13515     }
13516     
13517     if(this.adjustments == "auto"){
13518         var rc = this.resizeChild;
13519         var hw = this.west, he = this.east, hn = this.north, hs = this.south;
13520         if(rc && (hw || hn)){
13521             rc.position("relative");
13522             rc.setLeft(hw ? hw.el.getWidth() : 0);
13523             rc.setTop(hn ? hn.el.getHeight() : 0);
13524         }
13525         this.adjustments = [
13526             (he ? -he.el.getWidth() : 0) + (hw ? -hw.el.getWidth() : 0),
13527             (hn ? -hn.el.getHeight() : 0) + (hs ? -hs.el.getHeight() : 0) -1
13528         ];
13529     }
13530
13531     if(this.draggable){
13532         this.dd = this.dynamic ?
13533             this.el.initDD(null) : this.el.initDDProxy(null, {dragElId: this.proxy.id});
13534         this.dd.setHandleElId(this.resizeChild ? this.resizeChild.id : this.el.id);
13535     }
13536
13537     // public events
13538     this.addEvents({
13539         /**
13540          * @event beforeresize
13541          * Fired before resize is allowed. Set enabled to false to cancel resize.
13542          * @param {Roo.Resizable} this
13543          * @param {Roo.EventObject} e The mousedown event
13544          */
13545         "beforeresize" : true,
13546         /**
13547          * @event resizing
13548          * Fired a resizing.
13549          * @param {Roo.Resizable} this
13550          * @param {Number} x The new x position
13551          * @param {Number} y The new y position
13552          * @param {Number} w The new w width
13553          * @param {Number} h The new h hight
13554          * @param {Roo.EventObject} e The mouseup event
13555          */
13556         "resizing" : true,
13557         /**
13558          * @event resize
13559          * Fired after a resize.
13560          * @param {Roo.Resizable} this
13561          * @param {Number} width The new width
13562          * @param {Number} height The new height
13563          * @param {Roo.EventObject} e The mouseup event
13564          */
13565         "resize" : true
13566     });
13567
13568     if(this.width !== null && this.height !== null){
13569         this.resizeTo(this.width, this.height);
13570     }else{
13571         this.updateChildSize();
13572     }
13573     if(Roo.isIE){
13574         this.el.dom.style.zoom = 1;
13575     }
13576     Roo.Resizable.superclass.constructor.call(this);
13577 };
13578
13579 Roo.extend(Roo.Resizable, Roo.util.Observable, {
13580         resizeChild : false,
13581         adjustments : [0, 0],
13582         minWidth : 5,
13583         minHeight : 5,
13584         maxWidth : 10000,
13585         maxHeight : 10000,
13586         enabled : true,
13587         animate : false,
13588         duration : .35,
13589         dynamic : false,
13590         handles : false,
13591         multiDirectional : false,
13592         disableTrackOver : false,
13593         easing : 'easeOutStrong',
13594         widthIncrement : 0,
13595         heightIncrement : 0,
13596         pinned : false,
13597         width : null,
13598         height : null,
13599         preserveRatio : false,
13600         transparent: false,
13601         minX: 0,
13602         minY: 0,
13603         draggable: false,
13604
13605         /**
13606          * @cfg {String/HTMLElement/Element} constrainTo Constrain the resize to a particular element
13607          */
13608         constrainTo: undefined,
13609         /**
13610          * @cfg {Roo.lib.Region} resizeRegion Constrain the resize to a particular region
13611          */
13612         resizeRegion: undefined,
13613
13614
13615     /**
13616      * Perform a manual resize
13617      * @param {Number} width
13618      * @param {Number} height
13619      */
13620     resizeTo : function(width, height){
13621         this.el.setSize(width, height);
13622         this.updateChildSize();
13623         this.fireEvent("resize", this, width, height, null);
13624     },
13625
13626     // private
13627     startSizing : function(e, handle){
13628         this.fireEvent("beforeresize", this, e);
13629         if(this.enabled){ // 2nd enabled check in case disabled before beforeresize handler
13630
13631             if(!this.overlay){
13632                 this.overlay = this.el.createProxy({tag: "div", cls: "x-resizable-overlay", html: "&#160;"});
13633                 this.overlay.unselectable();
13634                 this.overlay.enableDisplayMode("block");
13635                 this.overlay.on("mousemove", this.onMouseMove, this);
13636                 this.overlay.on("mouseup", this.onMouseUp, this);
13637             }
13638             this.overlay.setStyle("cursor", handle.el.getStyle("cursor"));
13639
13640             this.resizing = true;
13641             this.startBox = this.el.getBox();
13642             this.startPoint = e.getXY();
13643             this.offsets = [(this.startBox.x + this.startBox.width) - this.startPoint[0],
13644                             (this.startBox.y + this.startBox.height) - this.startPoint[1]];
13645
13646             this.overlay.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
13647             this.overlay.show();
13648
13649             if(this.constrainTo) {
13650                 var ct = Roo.get(this.constrainTo);
13651                 this.resizeRegion = ct.getRegion().adjust(
13652                     ct.getFrameWidth('t'),
13653                     ct.getFrameWidth('l'),
13654                     -ct.getFrameWidth('b'),
13655                     -ct.getFrameWidth('r')
13656                 );
13657             }
13658
13659             this.proxy.setStyle('visibility', 'hidden'); // workaround display none
13660             this.proxy.show();
13661             this.proxy.setBox(this.startBox);
13662             if(!this.dynamic){
13663                 this.proxy.setStyle('visibility', 'visible');
13664             }
13665         }
13666     },
13667
13668     // private
13669     onMouseDown : function(handle, e){
13670         if(this.enabled){
13671             e.stopEvent();
13672             this.activeHandle = handle;
13673             this.startSizing(e, handle);
13674         }
13675     },
13676
13677     // private
13678     onMouseUp : function(e){
13679         var size = this.resizeElement();
13680         this.resizing = false;
13681         this.handleOut();
13682         this.overlay.hide();
13683         this.proxy.hide();
13684         this.fireEvent("resize", this, size.width, size.height, e);
13685     },
13686
13687     // private
13688     updateChildSize : function(){
13689         
13690         if(this.resizeChild){
13691             var el = this.el;
13692             var child = this.resizeChild;
13693             var adj = this.adjustments;
13694             if(el.dom.offsetWidth){
13695                 var b = el.getSize(true);
13696                 child.setSize(b.width+adj[0], b.height+adj[1]);
13697             }
13698             // Second call here for IE
13699             // The first call enables instant resizing and
13700             // the second call corrects scroll bars if they
13701             // exist
13702             if(Roo.isIE){
13703                 setTimeout(function(){
13704                     if(el.dom.offsetWidth){
13705                         var b = el.getSize(true);
13706                         child.setSize(b.width+adj[0], b.height+adj[1]);
13707                     }
13708                 }, 10);
13709             }
13710         }
13711     },
13712
13713     // private
13714     snap : function(value, inc, min){
13715         if(!inc || !value) return value;
13716         var newValue = value;
13717         var m = value % inc;
13718         if(m > 0){
13719             if(m > (inc/2)){
13720                 newValue = value + (inc-m);
13721             }else{
13722                 newValue = value - m;
13723             }
13724         }
13725         return Math.max(min, newValue);
13726     },
13727
13728     // private
13729     resizeElement : function(){
13730         var box = this.proxy.getBox();
13731         if(this.updateBox){
13732             this.el.setBox(box, false, this.animate, this.duration, null, this.easing);
13733         }else{
13734             this.el.setSize(box.width, box.height, this.animate, this.duration, null, this.easing);
13735         }
13736         this.updateChildSize();
13737         if(!this.dynamic){
13738             this.proxy.hide();
13739         }
13740         return box;
13741     },
13742
13743     // private
13744     constrain : function(v, diff, m, mx){
13745         if(v - diff < m){
13746             diff = v - m;
13747         }else if(v - diff > mx){
13748             diff = mx - v;
13749         }
13750         return diff;
13751     },
13752
13753     // private
13754     onMouseMove : function(e){
13755         
13756         if(this.enabled){
13757             try{// try catch so if something goes wrong the user doesn't get hung
13758
13759             if(this.resizeRegion && !this.resizeRegion.contains(e.getPoint())) {
13760                 return;
13761             }
13762
13763             //var curXY = this.startPoint;
13764             var curSize = this.curSize || this.startBox;
13765             var x = this.startBox.x, y = this.startBox.y;
13766             var ox = x, oy = y;
13767             var w = curSize.width, h = curSize.height;
13768             var ow = w, oh = h;
13769             var mw = this.minWidth, mh = this.minHeight;
13770             var mxw = this.maxWidth, mxh = this.maxHeight;
13771             var wi = this.widthIncrement;
13772             var hi = this.heightIncrement;
13773
13774             var eventXY = e.getXY();
13775             var diffX = -(this.startPoint[0] - Math.max(this.minX, eventXY[0]));
13776             var diffY = -(this.startPoint[1] - Math.max(this.minY, eventXY[1]));
13777
13778             var pos = this.activeHandle.position;
13779
13780             switch(pos){
13781                 case "east":
13782                     w += diffX;
13783                     w = Math.min(Math.max(mw, w), mxw);
13784                     break;
13785              
13786                 case "south":
13787                     h += diffY;
13788                     h = Math.min(Math.max(mh, h), mxh);
13789                     break;
13790                 case "southeast":
13791                     w += diffX;
13792                     h += diffY;
13793                     w = Math.min(Math.max(mw, w), mxw);
13794                     h = Math.min(Math.max(mh, h), mxh);
13795                     break;
13796                 case "north":
13797                     diffY = this.constrain(h, diffY, mh, mxh);
13798                     y += diffY;
13799                     h -= diffY;
13800                     break;
13801                 case "hdrag":
13802                     
13803                     if (wi) {
13804                         var adiffX = Math.abs(diffX);
13805                         var sub = (adiffX % wi); // how much 
13806                         if (sub > (wi/2)) { // far enough to snap
13807                             diffX = (diffX > 0) ? diffX-sub + wi : diffX+sub - wi;
13808                         } else {
13809                             // remove difference.. 
13810                             diffX = (diffX > 0) ? diffX-sub : diffX+sub;
13811                         }
13812                     }
13813                     x += diffX;
13814                     x = Math.max(this.minX, x);
13815                     break;
13816                 case "west":
13817                     diffX = this.constrain(w, diffX, mw, mxw);
13818                     x += diffX;
13819                     w -= diffX;
13820                     break;
13821                 case "northeast":
13822                     w += diffX;
13823                     w = Math.min(Math.max(mw, w), mxw);
13824                     diffY = this.constrain(h, diffY, mh, mxh);
13825                     y += diffY;
13826                     h -= diffY;
13827                     break;
13828                 case "northwest":
13829                     diffX = this.constrain(w, diffX, mw, mxw);
13830                     diffY = this.constrain(h, diffY, mh, mxh);
13831                     y += diffY;
13832                     h -= diffY;
13833                     x += diffX;
13834                     w -= diffX;
13835                     break;
13836                case "southwest":
13837                     diffX = this.constrain(w, diffX, mw, mxw);
13838                     h += diffY;
13839                     h = Math.min(Math.max(mh, h), mxh);
13840                     x += diffX;
13841                     w -= diffX;
13842                     break;
13843             }
13844
13845             var sw = this.snap(w, wi, mw);
13846             var sh = this.snap(h, hi, mh);
13847             if(sw != w || sh != h){
13848                 switch(pos){
13849                     case "northeast":
13850                         y -= sh - h;
13851                     break;
13852                     case "north":
13853                         y -= sh - h;
13854                         break;
13855                     case "southwest":
13856                         x -= sw - w;
13857                     break;
13858                     case "west":
13859                         x -= sw - w;
13860                         break;
13861                     case "northwest":
13862                         x -= sw - w;
13863                         y -= sh - h;
13864                     break;
13865                 }
13866                 w = sw;
13867                 h = sh;
13868             }
13869
13870             if(this.preserveRatio){
13871                 switch(pos){
13872                     case "southeast":
13873                     case "east":
13874                         h = oh * (w/ow);
13875                         h = Math.min(Math.max(mh, h), mxh);
13876                         w = ow * (h/oh);
13877                        break;
13878                     case "south":
13879                         w = ow * (h/oh);
13880                         w = Math.min(Math.max(mw, w), mxw);
13881                         h = oh * (w/ow);
13882                         break;
13883                     case "northeast":
13884                         w = ow * (h/oh);
13885                         w = Math.min(Math.max(mw, w), mxw);
13886                         h = oh * (w/ow);
13887                     break;
13888                     case "north":
13889                         var tw = w;
13890                         w = ow * (h/oh);
13891                         w = Math.min(Math.max(mw, w), mxw);
13892                         h = oh * (w/ow);
13893                         x += (tw - w) / 2;
13894                         break;
13895                     case "southwest":
13896                         h = oh * (w/ow);
13897                         h = Math.min(Math.max(mh, h), mxh);
13898                         var tw = w;
13899                         w = ow * (h/oh);
13900                         x += tw - w;
13901                         break;
13902                     case "west":
13903                         var th = h;
13904                         h = oh * (w/ow);
13905                         h = Math.min(Math.max(mh, h), mxh);
13906                         y += (th - h) / 2;
13907                         var tw = w;
13908                         w = ow * (h/oh);
13909                         x += tw - w;
13910                        break;
13911                     case "northwest":
13912                         var tw = w;
13913                         var th = h;
13914                         h = oh * (w/ow);
13915                         h = Math.min(Math.max(mh, h), mxh);
13916                         w = ow * (h/oh);
13917                         y += th - h;
13918                         x += tw - w;
13919                        break;
13920
13921                 }
13922             }
13923             if (pos == 'hdrag') {
13924                 w = ow;
13925             }
13926             this.proxy.setBounds(x, y, w, h);
13927             if(this.dynamic){
13928                 this.resizeElement();
13929             }
13930             }catch(e){}
13931         }
13932         this.fireEvent("resizing", this, x, y, w, h, e);
13933     },
13934
13935     // private
13936     handleOver : function(){
13937         if(this.enabled){
13938             this.el.addClass("x-resizable-over");
13939         }
13940     },
13941
13942     // private
13943     handleOut : function(){
13944         if(!this.resizing){
13945             this.el.removeClass("x-resizable-over");
13946         }
13947     },
13948
13949     /**
13950      * Returns the element this component is bound to.
13951      * @return {Roo.Element}
13952      */
13953     getEl : function(){
13954         return this.el;
13955     },
13956
13957     /**
13958      * Returns the resizeChild element (or null).
13959      * @return {Roo.Element}
13960      */
13961     getResizeChild : function(){
13962         return this.resizeChild;
13963     },
13964     groupHandler : function()
13965     {
13966         
13967     },
13968     /**
13969      * Destroys this resizable. If the element was wrapped and
13970      * removeEl is not true then the element remains.
13971      * @param {Boolean} removeEl (optional) true to remove the element from the DOM
13972      */
13973     destroy : function(removeEl){
13974         this.proxy.remove();
13975         if(this.overlay){
13976             this.overlay.removeAllListeners();
13977             this.overlay.remove();
13978         }
13979         var ps = Roo.Resizable.positions;
13980         for(var k in ps){
13981             if(typeof ps[k] != "function" && this[ps[k]]){
13982                 var h = this[ps[k]];
13983                 h.el.removeAllListeners();
13984                 h.el.remove();
13985             }
13986         }
13987         if(removeEl){
13988             this.el.update("");
13989             this.el.remove();
13990         }
13991     }
13992 });
13993
13994 // private
13995 // hash to map config positions to true positions
13996 Roo.Resizable.positions = {
13997     n: "north", s: "south", e: "east", w: "west", se: "southeast", sw: "southwest", nw: "northwest", ne: "northeast", 
13998     hd: "hdrag"
13999 };
14000
14001 // private
14002 Roo.Resizable.Handle = function(rz, pos, disableTrackOver, transparent){
14003     if(!this.tpl){
14004         // only initialize the template if resizable is used
14005         var tpl = Roo.DomHelper.createTemplate(
14006             {tag: "div", cls: "x-resizable-handle x-resizable-handle-{0}"}
14007         );
14008         tpl.compile();
14009         Roo.Resizable.Handle.prototype.tpl = tpl;
14010     }
14011     this.position = pos;
14012     this.rz = rz;
14013     // show north drag fro topdra
14014     var handlepos = pos == 'hdrag' ? 'north' : pos;
14015     
14016     this.el = this.tpl.append(rz.el.dom, [handlepos], true);
14017     if (pos == 'hdrag') {
14018         this.el.setStyle('cursor', 'pointer');
14019     }
14020     this.el.unselectable();
14021     if(transparent){
14022         this.el.setOpacity(0);
14023     }
14024     this.el.on("mousedown", this.onMouseDown, this);
14025     if(!disableTrackOver){
14026         this.el.on("mouseover", this.onMouseOver, this);
14027         this.el.on("mouseout", this.onMouseOut, this);
14028     }
14029 };
14030
14031 // private
14032 Roo.Resizable.Handle.prototype = {
14033     afterResize : function(rz){
14034         // do nothing
14035     },
14036     // private
14037     onMouseDown : function(e){
14038         this.rz.onMouseDown(this, e);
14039     },
14040     // private
14041     onMouseOver : function(e){
14042         this.rz.handleOver(this, e);
14043     },
14044     // private
14045     onMouseOut : function(e){
14046         this.rz.handleOut(this, e);
14047     }
14048 };/*
14049  * Based on:
14050  * Ext JS Library 1.1.1
14051  * Copyright(c) 2006-2007, Ext JS, LLC.
14052  *
14053  * Originally Released Under LGPL - original licence link has changed is not relivant.
14054  *
14055  * Fork - LGPL
14056  * <script type="text/javascript">
14057  */
14058
14059 /**
14060  * @class Roo.Editor
14061  * @extends Roo.Component
14062  * A base editor field that handles displaying/hiding on demand and has some built-in sizing and event handling logic.
14063  * @constructor
14064  * Create a new Editor
14065  * @param {Roo.form.Field} field The Field object (or descendant)
14066  * @param {Object} config The config object
14067  */
14068 Roo.Editor = function(field, config){
14069     Roo.Editor.superclass.constructor.call(this, config);
14070     this.field = field;
14071     this.addEvents({
14072         /**
14073              * @event beforestartedit
14074              * Fires when editing is initiated, but before the value changes.  Editing can be canceled by returning
14075              * false from the handler of this event.
14076              * @param {Editor} this
14077              * @param {Roo.Element} boundEl The underlying element bound to this editor
14078              * @param {Mixed} value The field value being set
14079              */
14080         "beforestartedit" : true,
14081         /**
14082              * @event startedit
14083              * Fires when this editor is displayed
14084              * @param {Roo.Element} boundEl The underlying element bound to this editor
14085              * @param {Mixed} value The starting field value
14086              */
14087         "startedit" : true,
14088         /**
14089              * @event beforecomplete
14090              * Fires after a change has been made to the field, but before the change is reflected in the underlying
14091              * field.  Saving the change to the field can be canceled by returning false from the handler of this event.
14092              * Note that if the value has not changed and ignoreNoChange = true, the editing will still end but this
14093              * event will not fire since no edit actually occurred.
14094              * @param {Editor} this
14095              * @param {Mixed} value The current field value
14096              * @param {Mixed} startValue The original field value
14097              */
14098         "beforecomplete" : true,
14099         /**
14100              * @event complete
14101              * Fires after editing is complete and any changed value has been written to the underlying field.
14102              * @param {Editor} this
14103              * @param {Mixed} value The current field value
14104              * @param {Mixed} startValue The original field value
14105              */
14106         "complete" : true,
14107         /**
14108          * @event specialkey
14109          * Fires when any key related to navigation (arrows, tab, enter, esc, etc.) is pressed.  You can check
14110          * {@link Roo.EventObject#getKey} to determine which key was pressed.
14111          * @param {Roo.form.Field} this
14112          * @param {Roo.EventObject} e The event object
14113          */
14114         "specialkey" : true
14115     });
14116 };
14117
14118 Roo.extend(Roo.Editor, Roo.Component, {
14119     /**
14120      * @cfg {Boolean/String} autosize
14121      * True for the editor to automatically adopt the size of the underlying field, "width" to adopt the width only,
14122      * or "height" to adopt the height only (defaults to false)
14123      */
14124     /**
14125      * @cfg {Boolean} revertInvalid
14126      * True to automatically revert the field value and cancel the edit when the user completes an edit and the field
14127      * validation fails (defaults to true)
14128      */
14129     /**
14130      * @cfg {Boolean} ignoreNoChange
14131      * True to skip the the edit completion process (no save, no events fired) if the user completes an edit and
14132      * the value has not changed (defaults to false).  Applies only to string values - edits for other data types
14133      * will never be ignored.
14134      */
14135     /**
14136      * @cfg {Boolean} hideEl
14137      * False to keep the bound element visible while the editor is displayed (defaults to true)
14138      */
14139     /**
14140      * @cfg {Mixed} value
14141      * The data value of the underlying field (defaults to "")
14142      */
14143     value : "",
14144     /**
14145      * @cfg {String} alignment
14146      * The position to align to (see {@link Roo.Element#alignTo} for more details, defaults to "c-c?").
14147      */
14148     alignment: "c-c?",
14149     /**
14150      * @cfg {Boolean/String} shadow "sides" for sides/bottom only, "frame" for 4-way shadow, and "drop"
14151      * for bottom-right shadow (defaults to "frame")
14152      */
14153     shadow : "frame",
14154     /**
14155      * @cfg {Boolean} constrain True to constrain the editor to the viewport
14156      */
14157     constrain : false,
14158     /**
14159      * @cfg {Boolean} completeOnEnter True to complete the edit when the enter key is pressed (defaults to false)
14160      */
14161     completeOnEnter : false,
14162     /**
14163      * @cfg {Boolean} cancelOnEsc True to cancel the edit when the escape key is pressed (defaults to false)
14164      */
14165     cancelOnEsc : false,
14166     /**
14167      * @cfg {Boolean} updateEl True to update the innerHTML of the bound element when the update completes (defaults to false)
14168      */
14169     updateEl : false,
14170
14171     // private
14172     onRender : function(ct, position){
14173         this.el = new Roo.Layer({
14174             shadow: this.shadow,
14175             cls: "x-editor",
14176             parentEl : ct,
14177             shim : this.shim,
14178             shadowOffset:4,
14179             id: this.id,
14180             constrain: this.constrain
14181         });
14182         this.el.setStyle("overflow", Roo.isGecko ? "auto" : "hidden");
14183         if(this.field.msgTarget != 'title'){
14184             this.field.msgTarget = 'qtip';
14185         }
14186         this.field.render(this.el);
14187         if(Roo.isGecko){
14188             this.field.el.dom.setAttribute('autocomplete', 'off');
14189         }
14190         this.field.on("specialkey", this.onSpecialKey, this);
14191         if(this.swallowKeys){
14192             this.field.el.swallowEvent(['keydown','keypress']);
14193         }
14194         this.field.show();
14195         this.field.on("blur", this.onBlur, this);
14196         if(this.field.grow){
14197             this.field.on("autosize", this.el.sync,  this.el, {delay:1});
14198         }
14199     },
14200
14201     onSpecialKey : function(field, e)
14202     {
14203         //Roo.log('editor onSpecialKey');
14204         if(this.completeOnEnter && e.getKey() == e.ENTER){
14205             e.stopEvent();
14206             this.completeEdit();
14207             return;
14208         }
14209         // do not fire special key otherwise it might hide close the editor...
14210         if(e.getKey() == e.ENTER){    
14211             return;
14212         }
14213         if(this.cancelOnEsc && e.getKey() == e.ESC){
14214             this.cancelEdit();
14215             return;
14216         } 
14217         this.fireEvent('specialkey', field, e);
14218     
14219     },
14220
14221     /**
14222      * Starts the editing process and shows the editor.
14223      * @param {String/HTMLElement/Element} el The element to edit
14224      * @param {String} value (optional) A value to initialize the editor with. If a value is not provided, it defaults
14225       * to the innerHTML of el.
14226      */
14227     startEdit : function(el, value){
14228         if(this.editing){
14229             this.completeEdit();
14230         }
14231         this.boundEl = Roo.get(el);
14232         var v = value !== undefined ? value : this.boundEl.dom.innerHTML;
14233         if(!this.rendered){
14234             this.render(this.parentEl || document.body);
14235         }
14236         if(this.fireEvent("beforestartedit", this, this.boundEl, v) === false){
14237             return;
14238         }
14239         this.startValue = v;
14240         this.field.setValue(v);
14241         if(this.autoSize){
14242             var sz = this.boundEl.getSize();
14243             switch(this.autoSize){
14244                 case "width":
14245                 this.setSize(sz.width,  "");
14246                 break;
14247                 case "height":
14248                 this.setSize("",  sz.height);
14249                 break;
14250                 default:
14251                 this.setSize(sz.width,  sz.height);
14252             }
14253         }
14254         this.el.alignTo(this.boundEl, this.alignment);
14255         this.editing = true;
14256         if(Roo.QuickTips){
14257             Roo.QuickTips.disable();
14258         }
14259         this.show();
14260     },
14261
14262     /**
14263      * Sets the height and width of this editor.
14264      * @param {Number} width The new width
14265      * @param {Number} height The new height
14266      */
14267     setSize : function(w, h){
14268         this.field.setSize(w, h);
14269         if(this.el){
14270             this.el.sync();
14271         }
14272     },
14273
14274     /**
14275      * Realigns the editor to the bound field based on the current alignment config value.
14276      */
14277     realign : function(){
14278         this.el.alignTo(this.boundEl, this.alignment);
14279     },
14280
14281     /**
14282      * Ends the editing process, persists the changed value to the underlying field, and hides the editor.
14283      * @param {Boolean} remainVisible Override the default behavior and keep the editor visible after edit (defaults to false)
14284      */
14285     completeEdit : function(remainVisible){
14286         if(!this.editing){
14287             return;
14288         }
14289         var v = this.getValue();
14290         if(this.revertInvalid !== false && !this.field.isValid()){
14291             v = this.startValue;
14292             this.cancelEdit(true);
14293         }
14294         if(String(v) === String(this.startValue) && this.ignoreNoChange){
14295             this.editing = false;
14296             this.hide();
14297             return;
14298         }
14299         if(this.fireEvent("beforecomplete", this, v, this.startValue) !== false){
14300             this.editing = false;
14301             if(this.updateEl && this.boundEl){
14302                 this.boundEl.update(v);
14303             }
14304             if(remainVisible !== true){
14305                 this.hide();
14306             }
14307             this.fireEvent("complete", this, v, this.startValue);
14308         }
14309     },
14310
14311     // private
14312     onShow : function(){
14313         this.el.show();
14314         if(this.hideEl !== false){
14315             this.boundEl.hide();
14316         }
14317         this.field.show();
14318         if(Roo.isIE && !this.fixIEFocus){ // IE has problems with focusing the first time
14319             this.fixIEFocus = true;
14320             this.deferredFocus.defer(50, this);
14321         }else{
14322             this.field.focus();
14323         }
14324         this.fireEvent("startedit", this.boundEl, this.startValue);
14325     },
14326
14327     deferredFocus : function(){
14328         if(this.editing){
14329             this.field.focus();
14330         }
14331     },
14332
14333     /**
14334      * Cancels the editing process and hides the editor without persisting any changes.  The field value will be
14335      * reverted to the original starting value.
14336      * @param {Boolean} remainVisible Override the default behavior and keep the editor visible after
14337      * cancel (defaults to false)
14338      */
14339     cancelEdit : function(remainVisible){
14340         if(this.editing){
14341             this.setValue(this.startValue);
14342             if(remainVisible !== true){
14343                 this.hide();
14344             }
14345         }
14346     },
14347
14348     // private
14349     onBlur : function(){
14350         if(this.allowBlur !== true && this.editing){
14351             this.completeEdit();
14352         }
14353     },
14354
14355     // private
14356     onHide : function(){
14357         if(this.editing){
14358             this.completeEdit();
14359             return;
14360         }
14361         this.field.blur();
14362         if(this.field.collapse){
14363             this.field.collapse();
14364         }
14365         this.el.hide();
14366         if(this.hideEl !== false){
14367             this.boundEl.show();
14368         }
14369         if(Roo.QuickTips){
14370             Roo.QuickTips.enable();
14371         }
14372     },
14373
14374     /**
14375      * Sets the data value of the editor
14376      * @param {Mixed} value Any valid value supported by the underlying field
14377      */
14378     setValue : function(v){
14379         this.field.setValue(v);
14380     },
14381
14382     /**
14383      * Gets the data value of the editor
14384      * @return {Mixed} The data value
14385      */
14386     getValue : function(){
14387         return this.field.getValue();
14388     }
14389 });/*
14390  * Based on:
14391  * Ext JS Library 1.1.1
14392  * Copyright(c) 2006-2007, Ext JS, LLC.
14393  *
14394  * Originally Released Under LGPL - original licence link has changed is not relivant.
14395  *
14396  * Fork - LGPL
14397  * <script type="text/javascript">
14398  */
14399  
14400 /**
14401  * @class Roo.BasicDialog
14402  * @extends Roo.util.Observable
14403  * Lightweight Dialog Class.  The code below shows the creation of a typical dialog using existing HTML markup:
14404  * <pre><code>
14405 var dlg = new Roo.BasicDialog("my-dlg", {
14406     height: 200,
14407     width: 300,
14408     minHeight: 100,
14409     minWidth: 150,
14410     modal: true,
14411     proxyDrag: true,
14412     shadow: true
14413 });
14414 dlg.addKeyListener(27, dlg.hide, dlg); // ESC can also close the dialog
14415 dlg.addButton('OK', dlg.hide, dlg);    // Could call a save function instead of hiding
14416 dlg.addButton('Cancel', dlg.hide, dlg);
14417 dlg.show();
14418 </code></pre>
14419   <b>A Dialog should always be a direct child of the body element.</b>
14420  * @cfg {Boolean/DomHelper} autoCreate True to auto create from scratch, or using a DomHelper Object (defaults to false)
14421  * @cfg {String} title Default text to display in the title bar (defaults to null)
14422  * @cfg {Number} width Width of the dialog in pixels (can also be set via CSS).  Determined by browser if unspecified.
14423  * @cfg {Number} height Height of the dialog in pixels (can also be set via CSS).  Determined by browser if unspecified.
14424  * @cfg {Number} x The default left page coordinate of the dialog (defaults to center screen)
14425  * @cfg {Number} y The default top page coordinate of the dialog (defaults to center screen)
14426  * @cfg {String/Element} animateTarget Id or element from which the dialog should animate while opening
14427  * (defaults to null with no animation)
14428  * @cfg {Boolean} resizable False to disable manual dialog resizing (defaults to true)
14429  * @cfg {String} resizeHandles Which resize handles to display - see the {@link Roo.Resizable} handles config
14430  * property for valid values (defaults to 'all')
14431  * @cfg {Number} minHeight The minimum allowable height for a resizable dialog (defaults to 80)
14432  * @cfg {Number} minWidth The minimum allowable width for a resizable dialog (defaults to 200)
14433  * @cfg {Boolean} modal True to show the dialog modally, preventing user interaction with the rest of the page (defaults to false)
14434  * @cfg {Boolean} autoScroll True to allow the dialog body contents to overflow and display scrollbars (defaults to false)
14435  * @cfg {Boolean} closable False to remove the built-in top-right corner close button (defaults to true)
14436  * @cfg {Boolean} collapsible False to remove the built-in top-right corner collapse button (defaults to true)
14437  * @cfg {Boolean} constraintoviewport True to keep the dialog constrained within the visible viewport boundaries (defaults to true)
14438  * @cfg {Boolean} syncHeightBeforeShow True to cause the dimensions to be recalculated before the dialog is shown (defaults to false)
14439  * @cfg {Boolean} draggable False to disable dragging of the dialog within the viewport (defaults to true)
14440  * @cfg {Boolean} autoTabs If true, all elements with class 'x-dlg-tab' will get automatically converted to tabs (defaults to false)
14441  * @cfg {String} tabTag The tag name of tab elements, used when autoTabs = true (defaults to 'div')
14442  * @cfg {Boolean} proxyDrag True to drag a lightweight proxy element rather than the dialog itself, used when
14443  * draggable = true (defaults to false)
14444  * @cfg {Boolean} fixedcenter True to ensure that anytime the dialog is shown or resized it gets centered (defaults to false)
14445  * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
14446  * shadow (defaults to false)
14447  * @cfg {Number} shadowOffset The number of pixels to offset the shadow if displayed (defaults to 5)
14448  * @cfg {String} buttonAlign Valid values are "left," "center" and "right" (defaults to "right")
14449  * @cfg {Number} minButtonWidth Minimum width of all dialog buttons (defaults to 75)
14450  * @cfg {Array} buttons Array of buttons
14451  * @cfg {Boolean} shim True to create an iframe shim that prevents selects from showing through (defaults to false)
14452  * @constructor
14453  * Create a new BasicDialog.
14454  * @param {String/HTMLElement/Roo.Element} el The container element or DOM node, or its id
14455  * @param {Object} config Configuration options
14456  */
14457 Roo.BasicDialog = function(el, config){
14458     this.el = Roo.get(el);
14459     var dh = Roo.DomHelper;
14460     if(!this.el && config && config.autoCreate){
14461         if(typeof config.autoCreate == "object"){
14462             if(!config.autoCreate.id){
14463                 config.autoCreate.id = el;
14464             }
14465             this.el = dh.append(document.body,
14466                         config.autoCreate, true);
14467         }else{
14468             this.el = dh.append(document.body,
14469                         {tag: "div", id: el, style:'visibility:hidden;'}, true);
14470         }
14471     }
14472     el = this.el;
14473     el.setDisplayed(true);
14474     el.hide = this.hideAction;
14475     this.id = el.id;
14476     el.addClass("x-dlg");
14477
14478     Roo.apply(this, config);
14479
14480     this.proxy = el.createProxy("x-dlg-proxy");
14481     this.proxy.hide = this.hideAction;
14482     this.proxy.setOpacity(.5);
14483     this.proxy.hide();
14484
14485     if(config.width){
14486         el.setWidth(config.width);
14487     }
14488     if(config.height){
14489         el.setHeight(config.height);
14490     }
14491     this.size = el.getSize();
14492     if(typeof config.x != "undefined" && typeof config.y != "undefined"){
14493         this.xy = [config.x,config.y];
14494     }else{
14495         this.xy = el.getCenterXY(true);
14496     }
14497     /** The header element @type Roo.Element */
14498     this.header = el.child("> .x-dlg-hd");
14499     /** The body element @type Roo.Element */
14500     this.body = el.child("> .x-dlg-bd");
14501     /** The footer element @type Roo.Element */
14502     this.footer = el.child("> .x-dlg-ft");
14503
14504     if(!this.header){
14505         this.header = el.createChild({tag: "div", cls:"x-dlg-hd", html: "&#160;"}, this.body ? this.body.dom : null);
14506     }
14507     if(!this.body){
14508         this.body = el.createChild({tag: "div", cls:"x-dlg-bd"});
14509     }
14510
14511     this.header.unselectable();
14512     if(this.title){
14513         this.header.update(this.title);
14514     }
14515     // this element allows the dialog to be focused for keyboard event
14516     this.focusEl = el.createChild({tag: "a", href:"#", cls:"x-dlg-focus", tabIndex:"-1"});
14517     this.focusEl.swallowEvent("click", true);
14518
14519     this.header.wrap({cls:"x-dlg-hd-right"}).wrap({cls:"x-dlg-hd-left"}, true);
14520
14521     // wrap the body and footer for special rendering
14522     this.bwrap = this.body.wrap({tag: "div", cls:"x-dlg-dlg-body"});
14523     if(this.footer){
14524         this.bwrap.dom.appendChild(this.footer.dom);
14525     }
14526
14527     this.bg = this.el.createChild({
14528         tag: "div", cls:"x-dlg-bg",
14529         html: '<div class="x-dlg-bg-left"><div class="x-dlg-bg-right"><div class="x-dlg-bg-center">&#160;</div></div></div>'
14530     });
14531     this.centerBg = this.bg.child("div.x-dlg-bg-center");
14532
14533
14534     if(this.autoScroll !== false && !this.autoTabs){
14535         this.body.setStyle("overflow", "auto");
14536     }
14537
14538     this.toolbox = this.el.createChild({cls: "x-dlg-toolbox"});
14539
14540     if(this.closable !== false){
14541         this.el.addClass("x-dlg-closable");
14542         this.close = this.toolbox.createChild({cls:"x-dlg-close"});
14543         this.close.on("click", this.closeClick, this);
14544         this.close.addClassOnOver("x-dlg-close-over");
14545     }
14546     if(this.collapsible !== false){
14547         this.collapseBtn = this.toolbox.createChild({cls:"x-dlg-collapse"});
14548         this.collapseBtn.on("click", this.collapseClick, this);
14549         this.collapseBtn.addClassOnOver("x-dlg-collapse-over");
14550         this.header.on("dblclick", this.collapseClick, this);
14551     }
14552     if(this.resizable !== false){
14553         this.el.addClass("x-dlg-resizable");
14554         this.resizer = new Roo.Resizable(el, {
14555             minWidth: this.minWidth || 80,
14556             minHeight:this.minHeight || 80,
14557             handles: this.resizeHandles || "all",
14558             pinned: true
14559         });
14560         this.resizer.on("beforeresize", this.beforeResize, this);
14561         this.resizer.on("resize", this.onResize, this);
14562     }
14563     if(this.draggable !== false){
14564         el.addClass("x-dlg-draggable");
14565         if (!this.proxyDrag) {
14566             var dd = new Roo.dd.DD(el.dom.id, "WindowDrag");
14567         }
14568         else {
14569             var dd = new Roo.dd.DDProxy(el.dom.id, "WindowDrag", {dragElId: this.proxy.id});
14570         }
14571         dd.setHandleElId(this.header.id);
14572         dd.endDrag = this.endMove.createDelegate(this);
14573         dd.startDrag = this.startMove.createDelegate(this);
14574         dd.onDrag = this.onDrag.createDelegate(this);
14575         dd.scroll = false;
14576         this.dd = dd;
14577     }
14578     if(this.modal){
14579         this.mask = dh.append(document.body, {tag: "div", cls:"x-dlg-mask"}, true);
14580         this.mask.enableDisplayMode("block");
14581         this.mask.hide();
14582         this.el.addClass("x-dlg-modal");
14583     }
14584     if(this.shadow){
14585         this.shadow = new Roo.Shadow({
14586             mode : typeof this.shadow == "string" ? this.shadow : "sides",
14587             offset : this.shadowOffset
14588         });
14589     }else{
14590         this.shadowOffset = 0;
14591     }
14592     if(Roo.useShims && this.shim !== false){
14593         this.shim = this.el.createShim();
14594         this.shim.hide = this.hideAction;
14595         this.shim.hide();
14596     }else{
14597         this.shim = false;
14598     }
14599     if(this.autoTabs){
14600         this.initTabs();
14601     }
14602     if (this.buttons) { 
14603         var bts= this.buttons;
14604         this.buttons = [];
14605         Roo.each(bts, function(b) {
14606             this.addButton(b);
14607         }, this);
14608     }
14609     
14610     
14611     this.addEvents({
14612         /**
14613          * @event keydown
14614          * Fires when a key is pressed
14615          * @param {Roo.BasicDialog} this
14616          * @param {Roo.EventObject} e
14617          */
14618         "keydown" : true,
14619         /**
14620          * @event move
14621          * Fires when this dialog is moved by the user.
14622          * @param {Roo.BasicDialog} this
14623          * @param {Number} x The new page X
14624          * @param {Number} y The new page Y
14625          */
14626         "move" : true,
14627         /**
14628          * @event resize
14629          * Fires when this dialog is resized by the user.
14630          * @param {Roo.BasicDialog} this
14631          * @param {Number} width The new width
14632          * @param {Number} height The new height
14633          */
14634         "resize" : true,
14635         /**
14636          * @event beforehide
14637          * Fires before this dialog is hidden.
14638          * @param {Roo.BasicDialog} this
14639          */
14640         "beforehide" : true,
14641         /**
14642          * @event hide
14643          * Fires when this dialog is hidden.
14644          * @param {Roo.BasicDialog} this
14645          */
14646         "hide" : true,
14647         /**
14648          * @event beforeshow
14649          * Fires before this dialog is shown.
14650          * @param {Roo.BasicDialog} this
14651          */
14652         "beforeshow" : true,
14653         /**
14654          * @event show
14655          * Fires when this dialog is shown.
14656          * @param {Roo.BasicDialog} this
14657          */
14658         "show" : true
14659     });
14660     el.on("keydown", this.onKeyDown, this);
14661     el.on("mousedown", this.toFront, this);
14662     Roo.EventManager.onWindowResize(this.adjustViewport, this, true);
14663     this.el.hide();
14664     Roo.DialogManager.register(this);
14665     Roo.BasicDialog.superclass.constructor.call(this);
14666 };
14667
14668 Roo.extend(Roo.BasicDialog, Roo.util.Observable, {
14669     shadowOffset: Roo.isIE ? 6 : 5,
14670     minHeight: 80,
14671     minWidth: 200,
14672     minButtonWidth: 75,
14673     defaultButton: null,
14674     buttonAlign: "right",
14675     tabTag: 'div',
14676     firstShow: true,
14677
14678     /**
14679      * Sets the dialog title text
14680      * @param {String} text The title text to display
14681      * @return {Roo.BasicDialog} this
14682      */
14683     setTitle : function(text){
14684         this.header.update(text);
14685         return this;
14686     },
14687
14688     // private
14689     closeClick : function(){
14690         this.hide();
14691     },
14692
14693     // private
14694     collapseClick : function(){
14695         this[this.collapsed ? "expand" : "collapse"]();
14696     },
14697
14698     /**
14699      * Collapses the dialog to its minimized state (only the title bar is visible).
14700      * Equivalent to the user clicking the collapse dialog button.
14701      */
14702     collapse : function(){
14703         if(!this.collapsed){
14704             this.collapsed = true;
14705             this.el.addClass("x-dlg-collapsed");
14706             this.restoreHeight = this.el.getHeight();
14707             this.resizeTo(this.el.getWidth(), this.header.getHeight());
14708         }
14709     },
14710
14711     /**
14712      * Expands a collapsed dialog back to its normal state.  Equivalent to the user
14713      * clicking the expand dialog button.
14714      */
14715     expand : function(){
14716         if(this.collapsed){
14717             this.collapsed = false;
14718             this.el.removeClass("x-dlg-collapsed");
14719             this.resizeTo(this.el.getWidth(), this.restoreHeight);
14720         }
14721     },
14722
14723     /**
14724      * Reinitializes the tabs component, clearing out old tabs and finding new ones.
14725      * @return {Roo.TabPanel} The tabs component
14726      */
14727     initTabs : function(){
14728         var tabs = this.getTabs();
14729         while(tabs.getTab(0)){
14730             tabs.removeTab(0);
14731         }
14732         this.el.select(this.tabTag+'.x-dlg-tab').each(function(el){
14733             var dom = el.dom;
14734             tabs.addTab(Roo.id(dom), dom.title);
14735             dom.title = "";
14736         });
14737         tabs.activate(0);
14738         return tabs;
14739     },
14740
14741     // private
14742     beforeResize : function(){
14743         this.resizer.minHeight = Math.max(this.minHeight, this.getHeaderFooterHeight(true)+40);
14744     },
14745
14746     // private
14747     onResize : function(){
14748         this.refreshSize();
14749         this.syncBodyHeight();
14750         this.adjustAssets();
14751         this.focus();
14752         this.fireEvent("resize", this, this.size.width, this.size.height);
14753     },
14754
14755     // private
14756     onKeyDown : function(e){
14757         if(this.isVisible()){
14758             this.fireEvent("keydown", this, e);
14759         }
14760     },
14761
14762     /**
14763      * Resizes the dialog.
14764      * @param {Number} width
14765      * @param {Number} height
14766      * @return {Roo.BasicDialog} this
14767      */
14768     resizeTo : function(width, height){
14769         this.el.setSize(width, height);
14770         this.size = {width: width, height: height};
14771         this.syncBodyHeight();
14772         if(this.fixedcenter){
14773             this.center();
14774         }
14775         if(this.isVisible()){
14776             this.constrainXY();
14777             this.adjustAssets();
14778         }
14779         this.fireEvent("resize", this, width, height);
14780         return this;
14781     },
14782
14783
14784     /**
14785      * Resizes the dialog to fit the specified content size.
14786      * @param {Number} width
14787      * @param {Number} height
14788      * @return {Roo.BasicDialog} this
14789      */
14790     setContentSize : function(w, h){
14791         h += this.getHeaderFooterHeight() + this.body.getMargins("tb");
14792         w += this.body.getMargins("lr") + this.bwrap.getMargins("lr") + this.centerBg.getPadding("lr");
14793         //if(!this.el.isBorderBox()){
14794             h +=  this.body.getPadding("tb") + this.bwrap.getBorderWidth("tb") + this.body.getBorderWidth("tb") + this.el.getBorderWidth("tb");
14795             w += this.body.getPadding("lr") + this.bwrap.getBorderWidth("lr") + this.body.getBorderWidth("lr") + this.bwrap.getPadding("lr") + this.el.getBorderWidth("lr");
14796         //}
14797         if(this.tabs){
14798             h += this.tabs.stripWrap.getHeight() + this.tabs.bodyEl.getMargins("tb") + this.tabs.bodyEl.getPadding("tb");
14799             w += this.tabs.bodyEl.getMargins("lr") + this.tabs.bodyEl.getPadding("lr");
14800         }
14801         this.resizeTo(w, h);
14802         return this;
14803     },
14804
14805     /**
14806      * Adds a key listener for when this dialog is displayed.  This allows you to hook in a function that will be
14807      * executed in response to a particular key being pressed while the dialog is active.
14808      * @param {Number/Array/Object} key Either the numeric key code, array of key codes or an object with the following options:
14809      *                                  {key: (number or array), shift: (true/false), ctrl: (true/false), alt: (true/false)}
14810      * @param {Function} fn The function to call
14811      * @param {Object} scope (optional) The scope of the function
14812      * @return {Roo.BasicDialog} this
14813      */
14814     addKeyListener : function(key, fn, scope){
14815         var keyCode, shift, ctrl, alt;
14816         if(typeof key == "object" && !(key instanceof Array)){
14817             keyCode = key["key"];
14818             shift = key["shift"];
14819             ctrl = key["ctrl"];
14820             alt = key["alt"];
14821         }else{
14822             keyCode = key;
14823         }
14824         var handler = function(dlg, e){
14825             if((!shift || e.shiftKey) && (!ctrl || e.ctrlKey) &&  (!alt || e.altKey)){
14826                 var k = e.getKey();
14827                 if(keyCode instanceof Array){
14828                     for(var i = 0, len = keyCode.length; i < len; i++){
14829                         if(keyCode[i] == k){
14830                           fn.call(scope || window, dlg, k, e);
14831                           return;
14832                         }
14833                     }
14834                 }else{
14835                     if(k == keyCode){
14836                         fn.call(scope || window, dlg, k, e);
14837                     }
14838                 }
14839             }
14840         };
14841         this.on("keydown", handler);
14842         return this;
14843     },
14844
14845     /**
14846      * Returns the TabPanel component (creates it if it doesn't exist).
14847      * Note: If you wish to simply check for the existence of tabs without creating them,
14848      * check for a null 'tabs' property.
14849      * @return {Roo.TabPanel} The tabs component
14850      */
14851     getTabs : function(){
14852         if(!this.tabs){
14853             this.el.addClass("x-dlg-auto-tabs");
14854             this.body.addClass(this.tabPosition == "bottom" ? "x-tabs-bottom" : "x-tabs-top");
14855             this.tabs = new Roo.TabPanel(this.body.dom, this.tabPosition == "bottom");
14856         }
14857         return this.tabs;
14858     },
14859
14860     /**
14861      * Adds a button to the footer section of the dialog.
14862      * @param {String/Object} config A string becomes the button text, an object can either be a Button config
14863      * object or a valid Roo.DomHelper element config
14864      * @param {Function} handler The function called when the button is clicked
14865      * @param {Object} scope (optional) The scope of the handler function (accepts position as a property)
14866      * @return {Roo.Button} The new button
14867      */
14868     addButton : function(config, handler, scope){
14869         var dh = Roo.DomHelper;
14870         if(!this.footer){
14871             this.footer = dh.append(this.bwrap, {tag: "div", cls:"x-dlg-ft"}, true);
14872         }
14873         if(!this.btnContainer){
14874             var tb = this.footer.createChild({
14875
14876                 cls:"x-dlg-btns x-dlg-btns-"+this.buttonAlign,
14877                 html:'<table cellspacing="0"><tbody><tr></tr></tbody></table><div class="x-clear"></div>'
14878             }, null, true);
14879             this.btnContainer = tb.firstChild.firstChild.firstChild;
14880         }
14881         var bconfig = {
14882             handler: handler,
14883             scope: scope,
14884             minWidth: this.minButtonWidth,
14885             hideParent:true
14886         };
14887         if(typeof config == "string"){
14888             bconfig.text = config;
14889         }else{
14890             if(config.tag){
14891                 bconfig.dhconfig = config;
14892             }else{
14893                 Roo.apply(bconfig, config);
14894             }
14895         }
14896         var fc = false;
14897         if ((typeof(bconfig.position) != 'undefined') && bconfig.position < this.btnContainer.childNodes.length-1) {
14898             bconfig.position = Math.max(0, bconfig.position);
14899             fc = this.btnContainer.childNodes[bconfig.position];
14900         }
14901          
14902         var btn = new Roo.Button(
14903             fc ? 
14904                 this.btnContainer.insertBefore(document.createElement("td"),fc)
14905                 : this.btnContainer.appendChild(document.createElement("td")),
14906             //Roo.get(this.btnContainer).createChild( { tag: 'td'},  fc ),
14907             bconfig
14908         );
14909         this.syncBodyHeight();
14910         if(!this.buttons){
14911             /**
14912              * Array of all the buttons that have been added to this dialog via addButton
14913              * @type Array
14914              */
14915             this.buttons = [];
14916         }
14917         this.buttons.push(btn);
14918         return btn;
14919     },
14920
14921     /**
14922      * Sets the default button to be focused when the dialog is displayed.
14923      * @param {Roo.BasicDialog.Button} btn The button object returned by {@link #addButton}
14924      * @return {Roo.BasicDialog} this
14925      */
14926     setDefaultButton : function(btn){
14927         this.defaultButton = btn;
14928         return this;
14929     },
14930
14931     // private
14932     getHeaderFooterHeight : function(safe){
14933         var height = 0;
14934         if(this.header){
14935            height += this.header.getHeight();
14936         }
14937         if(this.footer){
14938            var fm = this.footer.getMargins();
14939             height += (this.footer.getHeight()+fm.top+fm.bottom);
14940         }
14941         height += this.bwrap.getPadding("tb")+this.bwrap.getBorderWidth("tb");
14942         height += this.centerBg.getPadding("tb");
14943         return height;
14944     },
14945
14946     // private
14947     syncBodyHeight : function()
14948     {
14949         var bd = this.body, // the text
14950             cb = this.centerBg, // wrapper around bottom.. but does not seem to be used..
14951             bw = this.bwrap;
14952         var height = this.size.height - this.getHeaderFooterHeight(false);
14953         bd.setHeight(height-bd.getMargins("tb"));
14954         var hh = this.header.getHeight();
14955         var h = this.size.height-hh;
14956         cb.setHeight(h);
14957         
14958         bw.setLeftTop(cb.getPadding("l"), hh+cb.getPadding("t"));
14959         bw.setHeight(h-cb.getPadding("tb"));
14960         
14961         bw.setWidth(this.el.getWidth(true)-cb.getPadding("lr"));
14962         bd.setWidth(bw.getWidth(true));
14963         if(this.tabs){
14964             this.tabs.syncHeight();
14965             if(Roo.isIE){
14966                 this.tabs.el.repaint();
14967             }
14968         }
14969     },
14970
14971     /**
14972      * Restores the previous state of the dialog if Roo.state is configured.
14973      * @return {Roo.BasicDialog} this
14974      */
14975     restoreState : function(){
14976         var box = Roo.state.Manager.get(this.stateId || (this.el.id + "-state"));
14977         if(box && box.width){
14978             this.xy = [box.x, box.y];
14979             this.resizeTo(box.width, box.height);
14980         }
14981         return this;
14982     },
14983
14984     // private
14985     beforeShow : function(){
14986         this.expand();
14987         if(this.fixedcenter){
14988             this.xy = this.el.getCenterXY(true);
14989         }
14990         if(this.modal){
14991             Roo.get(document.body).addClass("x-body-masked");
14992             this.mask.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
14993             this.mask.show();
14994         }
14995         this.constrainXY();
14996     },
14997
14998     // private
14999     animShow : function(){
15000         var b = Roo.get(this.animateTarget).getBox();
15001         this.proxy.setSize(b.width, b.height);
15002         this.proxy.setLocation(b.x, b.y);
15003         this.proxy.show();
15004         this.proxy.setBounds(this.xy[0], this.xy[1], this.size.width, this.size.height,
15005                     true, .35, this.showEl.createDelegate(this));
15006     },
15007
15008     /**
15009      * Shows the dialog.
15010      * @param {String/HTMLElement/Roo.Element} animateTarget (optional) Reset the animation target
15011      * @return {Roo.BasicDialog} this
15012      */
15013     show : function(animateTarget){
15014         if (this.fireEvent("beforeshow", this) === false){
15015             return;
15016         }
15017         if(this.syncHeightBeforeShow){
15018             this.syncBodyHeight();
15019         }else if(this.firstShow){
15020             this.firstShow = false;
15021             this.syncBodyHeight(); // sync the height on the first show instead of in the constructor
15022         }
15023         this.animateTarget = animateTarget || this.animateTarget;
15024         if(!this.el.isVisible()){
15025             this.beforeShow();
15026             if(this.animateTarget && Roo.get(this.animateTarget)){
15027                 this.animShow();
15028             }else{
15029                 this.showEl();
15030             }
15031         }
15032         return this;
15033     },
15034
15035     // private
15036     showEl : function(){
15037         this.proxy.hide();
15038         this.el.setXY(this.xy);
15039         this.el.show();
15040         this.adjustAssets(true);
15041         this.toFront();
15042         this.focus();
15043         // IE peekaboo bug - fix found by Dave Fenwick
15044         if(Roo.isIE){
15045             this.el.repaint();
15046         }
15047         this.fireEvent("show", this);
15048     },
15049
15050     /**
15051      * Focuses the dialog.  If a defaultButton is set, it will receive focus, otherwise the
15052      * dialog itself will receive focus.
15053      */
15054     focus : function(){
15055         if(this.defaultButton){
15056             this.defaultButton.focus();
15057         }else{
15058             this.focusEl.focus();
15059         }
15060     },
15061
15062     // private
15063     constrainXY : function(){
15064         if(this.constraintoviewport !== false){
15065             if(!this.viewSize){
15066                 if(this.container){
15067                     var s = this.container.getSize();
15068                     this.viewSize = [s.width, s.height];
15069                 }else{
15070                     this.viewSize = [Roo.lib.Dom.getViewWidth(),Roo.lib.Dom.getViewHeight()];
15071                 }
15072             }
15073             var s = Roo.get(this.container||document).getScroll();
15074
15075             var x = this.xy[0], y = this.xy[1];
15076             var w = this.size.width, h = this.size.height;
15077             var vw = this.viewSize[0], vh = this.viewSize[1];
15078             // only move it if it needs it
15079             var moved = false;
15080             // first validate right/bottom
15081             if(x + w > vw+s.left){
15082                 x = vw - w;
15083                 moved = true;
15084             }
15085             if(y + h > vh+s.top){
15086                 y = vh - h;
15087                 moved = true;
15088             }
15089             // then make sure top/left isn't negative
15090             if(x < s.left){
15091                 x = s.left;
15092                 moved = true;
15093             }
15094             if(y < s.top){
15095                 y = s.top;
15096                 moved = true;
15097             }
15098             if(moved){
15099                 // cache xy
15100                 this.xy = [x, y];
15101                 if(this.isVisible()){
15102                     this.el.setLocation(x, y);
15103                     this.adjustAssets();
15104                 }
15105             }
15106         }
15107     },
15108
15109     // private
15110     onDrag : function(){
15111         if(!this.proxyDrag){
15112             this.xy = this.el.getXY();
15113             this.adjustAssets();
15114         }
15115     },
15116
15117     // private
15118     adjustAssets : function(doShow){
15119         var x = this.xy[0], y = this.xy[1];
15120         var w = this.size.width, h = this.size.height;
15121         if(doShow === true){
15122             if(this.shadow){
15123                 this.shadow.show(this.el);
15124             }
15125             if(this.shim){
15126                 this.shim.show();
15127             }
15128         }
15129         if(this.shadow && this.shadow.isVisible()){
15130             this.shadow.show(this.el);
15131         }
15132         if(this.shim && this.shim.isVisible()){
15133             this.shim.setBounds(x, y, w, h);
15134         }
15135     },
15136
15137     // private
15138     adjustViewport : function(w, h){
15139         if(!w || !h){
15140             w = Roo.lib.Dom.getViewWidth();
15141             h = Roo.lib.Dom.getViewHeight();
15142         }
15143         // cache the size
15144         this.viewSize = [w, h];
15145         if(this.modal && this.mask.isVisible()){
15146             this.mask.setSize(w, h); // first make sure the mask isn't causing overflow
15147             this.mask.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
15148         }
15149         if(this.isVisible()){
15150             this.constrainXY();
15151         }
15152     },
15153
15154     /**
15155      * Destroys this dialog and all its supporting elements (including any tabs, shim,
15156      * shadow, proxy, mask, etc.)  Also removes all event listeners.
15157      * @param {Boolean} removeEl (optional) true to remove the element from the DOM
15158      */
15159     destroy : function(removeEl){
15160         if(this.isVisible()){
15161             this.animateTarget = null;
15162             this.hide();
15163         }
15164         Roo.EventManager.removeResizeListener(this.adjustViewport, this);
15165         if(this.tabs){
15166             this.tabs.destroy(removeEl);
15167         }
15168         Roo.destroy(
15169              this.shim,
15170              this.proxy,
15171              this.resizer,
15172              this.close,
15173              this.mask
15174         );
15175         if(this.dd){
15176             this.dd.unreg();
15177         }
15178         if(this.buttons){
15179            for(var i = 0, len = this.buttons.length; i < len; i++){
15180                this.buttons[i].destroy();
15181            }
15182         }
15183         this.el.removeAllListeners();
15184         if(removeEl === true){
15185             this.el.update("");
15186             this.el.remove();
15187         }
15188         Roo.DialogManager.unregister(this);
15189     },
15190
15191     // private
15192     startMove : function(){
15193         if(this.proxyDrag){
15194             this.proxy.show();
15195         }
15196         if(this.constraintoviewport !== false){
15197             this.dd.constrainTo(document.body, {right: this.shadowOffset, bottom: this.shadowOffset});
15198         }
15199     },
15200
15201     // private
15202     endMove : function(){
15203         if(!this.proxyDrag){
15204             Roo.dd.DD.prototype.endDrag.apply(this.dd, arguments);
15205         }else{
15206             Roo.dd.DDProxy.prototype.endDrag.apply(this.dd, arguments);
15207             this.proxy.hide();
15208         }
15209         this.refreshSize();
15210         this.adjustAssets();
15211         this.focus();
15212         this.fireEvent("move", this, this.xy[0], this.xy[1]);
15213     },
15214
15215     /**
15216      * Brings this dialog to the front of any other visible dialogs
15217      * @return {Roo.BasicDialog} this
15218      */
15219     toFront : function(){
15220         Roo.DialogManager.bringToFront(this);
15221         return this;
15222     },
15223
15224     /**
15225      * Sends this dialog to the back (under) of any other visible dialogs
15226      * @return {Roo.BasicDialog} this
15227      */
15228     toBack : function(){
15229         Roo.DialogManager.sendToBack(this);
15230         return this;
15231     },
15232
15233     /**
15234      * Centers this dialog in the viewport
15235      * @return {Roo.BasicDialog} this
15236      */
15237     center : function(){
15238         var xy = this.el.getCenterXY(true);
15239         this.moveTo(xy[0], xy[1]);
15240         return this;
15241     },
15242
15243     /**
15244      * Moves the dialog's top-left corner to the specified point
15245      * @param {Number} x
15246      * @param {Number} y
15247      * @return {Roo.BasicDialog} this
15248      */
15249     moveTo : function(x, y){
15250         this.xy = [x,y];
15251         if(this.isVisible()){
15252             this.el.setXY(this.xy);
15253             this.adjustAssets();
15254         }
15255         return this;
15256     },
15257
15258     /**
15259      * Aligns the dialog to the specified element
15260      * @param {String/HTMLElement/Roo.Element} element The element to align to.
15261      * @param {String} position The position to align to (see {@link Roo.Element#alignTo} for more details).
15262      * @param {Array} offsets (optional) Offset the positioning by [x, y]
15263      * @return {Roo.BasicDialog} this
15264      */
15265     alignTo : function(element, position, offsets){
15266         this.xy = this.el.getAlignToXY(element, position, offsets);
15267         if(this.isVisible()){
15268             this.el.setXY(this.xy);
15269             this.adjustAssets();
15270         }
15271         return this;
15272     },
15273
15274     /**
15275      * Anchors an element to another element and realigns it when the window is resized.
15276      * @param {String/HTMLElement/Roo.Element} element The element to align to.
15277      * @param {String} position The position to align to (see {@link Roo.Element#alignTo} for more details)
15278      * @param {Array} offsets (optional) Offset the positioning by [x, y]
15279      * @param {Boolean/Number} monitorScroll (optional) true to monitor body scroll and reposition. If this parameter
15280      * is a number, it is used as the buffer delay (defaults to 50ms).
15281      * @return {Roo.BasicDialog} this
15282      */
15283     anchorTo : function(el, alignment, offsets, monitorScroll){
15284         var action = function(){
15285             this.alignTo(el, alignment, offsets);
15286         };
15287         Roo.EventManager.onWindowResize(action, this);
15288         var tm = typeof monitorScroll;
15289         if(tm != 'undefined'){
15290             Roo.EventManager.on(window, 'scroll', action, this,
15291                 {buffer: tm == 'number' ? monitorScroll : 50});
15292         }
15293         action.call(this);
15294         return this;
15295     },
15296
15297     /**
15298      * Returns true if the dialog is visible
15299      * @return {Boolean}
15300      */
15301     isVisible : function(){
15302         return this.el.isVisible();
15303     },
15304
15305     // private
15306     animHide : function(callback){
15307         var b = Roo.get(this.animateTarget).getBox();
15308         this.proxy.show();
15309         this.proxy.setBounds(this.xy[0], this.xy[1], this.size.width, this.size.height);
15310         this.el.hide();
15311         this.proxy.setBounds(b.x, b.y, b.width, b.height, true, .35,
15312                     this.hideEl.createDelegate(this, [callback]));
15313     },
15314
15315     /**
15316      * Hides the dialog.
15317      * @param {Function} callback (optional) Function to call when the dialog is hidden
15318      * @return {Roo.BasicDialog} this
15319      */
15320     hide : function(callback){
15321         if (this.fireEvent("beforehide", this) === false){
15322             return;
15323         }
15324         if(this.shadow){
15325             this.shadow.hide();
15326         }
15327         if(this.shim) {
15328           this.shim.hide();
15329         }
15330         // sometimes animateTarget seems to get set.. causing problems...
15331         // this just double checks..
15332         if(this.animateTarget && Roo.get(this.animateTarget)) {
15333            this.animHide(callback);
15334         }else{
15335             this.el.hide();
15336             this.hideEl(callback);
15337         }
15338         return this;
15339     },
15340
15341     // private
15342     hideEl : function(callback){
15343         this.proxy.hide();
15344         if(this.modal){
15345             this.mask.hide();
15346             Roo.get(document.body).removeClass("x-body-masked");
15347         }
15348         this.fireEvent("hide", this);
15349         if(typeof callback == "function"){
15350             callback();
15351         }
15352     },
15353
15354     // private
15355     hideAction : function(){
15356         this.setLeft("-10000px");
15357         this.setTop("-10000px");
15358         this.setStyle("visibility", "hidden");
15359     },
15360
15361     // private
15362     refreshSize : function(){
15363         this.size = this.el.getSize();
15364         this.xy = this.el.getXY();
15365         Roo.state.Manager.set(this.stateId || this.el.id + "-state", this.el.getBox());
15366     },
15367
15368     // private
15369     // z-index is managed by the DialogManager and may be overwritten at any time
15370     setZIndex : function(index){
15371         if(this.modal){
15372             this.mask.setStyle("z-index", index);
15373         }
15374         if(this.shim){
15375             this.shim.setStyle("z-index", ++index);
15376         }
15377         if(this.shadow){
15378             this.shadow.setZIndex(++index);
15379         }
15380         this.el.setStyle("z-index", ++index);
15381         if(this.proxy){
15382             this.proxy.setStyle("z-index", ++index);
15383         }
15384         if(this.resizer){
15385             this.resizer.proxy.setStyle("z-index", ++index);
15386         }
15387
15388         this.lastZIndex = index;
15389     },
15390
15391     /**
15392      * Returns the element for this dialog
15393      * @return {Roo.Element} The underlying dialog Element
15394      */
15395     getEl : function(){
15396         return this.el;
15397     }
15398 });
15399
15400 /**
15401  * @class Roo.DialogManager
15402  * Provides global access to BasicDialogs that have been created and
15403  * support for z-indexing (layering) multiple open dialogs.
15404  */
15405 Roo.DialogManager = function(){
15406     var list = {};
15407     var accessList = [];
15408     var front = null;
15409
15410     // private
15411     var sortDialogs = function(d1, d2){
15412         return (!d1._lastAccess || d1._lastAccess < d2._lastAccess) ? -1 : 1;
15413     };
15414
15415     // private
15416     var orderDialogs = function(){
15417         accessList.sort(sortDialogs);
15418         var seed = Roo.DialogManager.zseed;
15419         for(var i = 0, len = accessList.length; i < len; i++){
15420             var dlg = accessList[i];
15421             if(dlg){
15422                 dlg.setZIndex(seed + (i*10));
15423             }
15424         }
15425     };
15426
15427     return {
15428         /**
15429          * The starting z-index for BasicDialogs (defaults to 9000)
15430          * @type Number The z-index value
15431          */
15432         zseed : 9000,
15433
15434         // private
15435         register : function(dlg){
15436             list[dlg.id] = dlg;
15437             accessList.push(dlg);
15438         },
15439
15440         // private
15441         unregister : function(dlg){
15442             delete list[dlg.id];
15443             var i=0;
15444             var len=0;
15445             if(!accessList.indexOf){
15446                 for(  i = 0, len = accessList.length; i < len; i++){
15447                     if(accessList[i] == dlg){
15448                         accessList.splice(i, 1);
15449                         return;
15450                     }
15451                 }
15452             }else{
15453                  i = accessList.indexOf(dlg);
15454                 if(i != -1){
15455                     accessList.splice(i, 1);
15456                 }
15457             }
15458         },
15459
15460         /**
15461          * Gets a registered dialog by id
15462          * @param {String/Object} id The id of the dialog or a dialog
15463          * @return {Roo.BasicDialog} this
15464          */
15465         get : function(id){
15466             return typeof id == "object" ? id : list[id];
15467         },
15468
15469         /**
15470          * Brings the specified dialog to the front
15471          * @param {String/Object} dlg The id of the dialog or a dialog
15472          * @return {Roo.BasicDialog} this
15473          */
15474         bringToFront : function(dlg){
15475             dlg = this.get(dlg);
15476             if(dlg != front){
15477                 front = dlg;
15478                 dlg._lastAccess = new Date().getTime();
15479                 orderDialogs();
15480             }
15481             return dlg;
15482         },
15483
15484         /**
15485          * Sends the specified dialog to the back
15486          * @param {String/Object} dlg The id of the dialog or a dialog
15487          * @return {Roo.BasicDialog} this
15488          */
15489         sendToBack : function(dlg){
15490             dlg = this.get(dlg);
15491             dlg._lastAccess = -(new Date().getTime());
15492             orderDialogs();
15493             return dlg;
15494         },
15495
15496         /**
15497          * Hides all dialogs
15498          */
15499         hideAll : function(){
15500             for(var id in list){
15501                 if(list[id] && typeof list[id] != "function" && list[id].isVisible()){
15502                     list[id].hide();
15503                 }
15504             }
15505         }
15506     };
15507 }();
15508
15509 /**
15510  * @class Roo.LayoutDialog
15511  * @extends Roo.BasicDialog
15512  * Dialog which provides adjustments for working with a layout in a Dialog.
15513  * Add your necessary layout config options to the dialog's config.<br>
15514  * Example usage (including a nested layout):
15515  * <pre><code>
15516 if(!dialog){
15517     dialog = new Roo.LayoutDialog("download-dlg", {
15518         modal: true,
15519         width:600,
15520         height:450,
15521         shadow:true,
15522         minWidth:500,
15523         minHeight:350,
15524         autoTabs:true,
15525         proxyDrag:true,
15526         // layout config merges with the dialog config
15527         center:{
15528             tabPosition: "top",
15529             alwaysShowTabs: true
15530         }
15531     });
15532     dialog.addKeyListener(27, dialog.hide, dialog);
15533     dialog.setDefaultButton(dialog.addButton("Close", dialog.hide, dialog));
15534     dialog.addButton("Build It!", this.getDownload, this);
15535
15536     // we can even add nested layouts
15537     var innerLayout = new Roo.BorderLayout("dl-inner", {
15538         east: {
15539             initialSize: 200,
15540             autoScroll:true,
15541             split:true
15542         },
15543         center: {
15544             autoScroll:true
15545         }
15546     });
15547     innerLayout.beginUpdate();
15548     innerLayout.add("east", new Roo.ContentPanel("dl-details"));
15549     innerLayout.add("center", new Roo.ContentPanel("selection-panel"));
15550     innerLayout.endUpdate(true);
15551
15552     var layout = dialog.getLayout();
15553     layout.beginUpdate();
15554     layout.add("center", new Roo.ContentPanel("standard-panel",
15555                         {title: "Download the Source", fitToFrame:true}));
15556     layout.add("center", new Roo.NestedLayoutPanel(innerLayout,
15557                {title: "Build your own roo.js"}));
15558     layout.getRegion("center").showPanel(sp);
15559     layout.endUpdate();
15560 }
15561 </code></pre>
15562     * @constructor
15563     * @param {String/HTMLElement/Roo.Element} el The id of or container element, or config
15564     * @param {Object} config configuration options
15565   */
15566 Roo.LayoutDialog = function(el, cfg){
15567     
15568     var config=  cfg;
15569     if (typeof(cfg) == 'undefined') {
15570         config = Roo.apply({}, el);
15571         // not sure why we use documentElement here.. - it should always be body.
15572         // IE7 borks horribly if we use documentElement.
15573         // webkit also does not like documentElement - it creates a body element...
15574         el = Roo.get( document.body || document.documentElement ).createChild();
15575         //config.autoCreate = true;
15576     }
15577     
15578     
15579     config.autoTabs = false;
15580     Roo.LayoutDialog.superclass.constructor.call(this, el, config);
15581     this.body.setStyle({overflow:"hidden", position:"relative"});
15582     this.layout = new Roo.BorderLayout(this.body.dom, config);
15583     this.layout.monitorWindowResize = false;
15584     this.el.addClass("x-dlg-auto-layout");
15585     // fix case when center region overwrites center function
15586     this.center = Roo.BasicDialog.prototype.center;
15587     this.on("show", this.layout.layout, this.layout, true);
15588     if (config.items) {
15589         var xitems = config.items;
15590         delete config.items;
15591         Roo.each(xitems, this.addxtype, this);
15592     }
15593     
15594     
15595 };
15596 Roo.extend(Roo.LayoutDialog, Roo.BasicDialog, {
15597     /**
15598      * Ends update of the layout <strike>and resets display to none</strike>. Use standard beginUpdate/endUpdate on the layout.
15599      * @deprecated
15600      */
15601     endUpdate : function(){
15602         this.layout.endUpdate();
15603     },
15604
15605     /**
15606      * Begins an update of the layout <strike>and sets display to block and visibility to hidden</strike>. Use standard beginUpdate/endUpdate on the layout.
15607      *  @deprecated
15608      */
15609     beginUpdate : function(){
15610         this.layout.beginUpdate();
15611     },
15612
15613     /**
15614      * Get the BorderLayout for this dialog
15615      * @return {Roo.BorderLayout}
15616      */
15617     getLayout : function(){
15618         return this.layout;
15619     },
15620
15621     showEl : function(){
15622         Roo.LayoutDialog.superclass.showEl.apply(this, arguments);
15623         if(Roo.isIE7){
15624             this.layout.layout();
15625         }
15626     },
15627
15628     // private
15629     // Use the syncHeightBeforeShow config option to control this automatically
15630     syncBodyHeight : function(){
15631         Roo.LayoutDialog.superclass.syncBodyHeight.call(this);
15632         if(this.layout){this.layout.layout();}
15633     },
15634     
15635       /**
15636      * Add an xtype element (actually adds to the layout.)
15637      * @return {Object} xdata xtype object data.
15638      */
15639     
15640     addxtype : function(c) {
15641         return this.layout.addxtype(c);
15642     }
15643 });/*
15644  * Based on:
15645  * Ext JS Library 1.1.1
15646  * Copyright(c) 2006-2007, Ext JS, LLC.
15647  *
15648  * Originally Released Under LGPL - original licence link has changed is not relivant.
15649  *
15650  * Fork - LGPL
15651  * <script type="text/javascript">
15652  */
15653  
15654 /**
15655  * @class Roo.MessageBox
15656  * Utility class for generating different styles of message boxes.  The alias Roo.Msg can also be used.
15657  * Example usage:
15658  *<pre><code>
15659 // Basic alert:
15660 Roo.Msg.alert('Status', 'Changes saved successfully.');
15661
15662 // Prompt for user data:
15663 Roo.Msg.prompt('Name', 'Please enter your name:', function(btn, text){
15664     if (btn == 'ok'){
15665         // process text value...
15666     }
15667 });
15668
15669 // Show a dialog using config options:
15670 Roo.Msg.show({
15671    title:'Save Changes?',
15672    msg: 'Your are closing a tab that has unsaved changes. Would you like to save your changes?',
15673    buttons: Roo.Msg.YESNOCANCEL,
15674    fn: processResult,
15675    animEl: 'elId'
15676 });
15677 </code></pre>
15678  * @singleton
15679  */
15680 Roo.MessageBox = function(){
15681     var dlg, opt, mask, waitTimer;
15682     var bodyEl, msgEl, textboxEl, textareaEl, progressEl, pp;
15683     var buttons, activeTextEl, bwidth;
15684
15685     // private
15686     var handleButton = function(button){
15687         dlg.hide();
15688         Roo.callback(opt.fn, opt.scope||window, [button, activeTextEl.dom.value], 1);
15689     };
15690
15691     // private
15692     var handleHide = function(){
15693         if(opt && opt.cls){
15694             dlg.el.removeClass(opt.cls);
15695         }
15696         if(waitTimer){
15697             Roo.TaskMgr.stop(waitTimer);
15698             waitTimer = null;
15699         }
15700     };
15701
15702     // private
15703     var updateButtons = function(b){
15704         var width = 0;
15705         if(!b){
15706             buttons["ok"].hide();
15707             buttons["cancel"].hide();
15708             buttons["yes"].hide();
15709             buttons["no"].hide();
15710             dlg.footer.dom.style.display = 'none';
15711             return width;
15712         }
15713         dlg.footer.dom.style.display = '';
15714         for(var k in buttons){
15715             if(typeof buttons[k] != "function"){
15716                 if(b[k]){
15717                     buttons[k].show();
15718                     buttons[k].setText(typeof b[k] == "string" ? b[k] : Roo.MessageBox.buttonText[k]);
15719                     width += buttons[k].el.getWidth()+15;
15720                 }else{
15721                     buttons[k].hide();
15722                 }
15723             }
15724         }
15725         return width;
15726     };
15727
15728     // private
15729     var handleEsc = function(d, k, e){
15730         if(opt && opt.closable !== false){
15731             dlg.hide();
15732         }
15733         if(e){
15734             e.stopEvent();
15735         }
15736     };
15737
15738     return {
15739         /**
15740          * Returns a reference to the underlying {@link Roo.BasicDialog} element
15741          * @return {Roo.BasicDialog} The BasicDialog element
15742          */
15743         getDialog : function(){
15744            if(!dlg){
15745                 dlg = new Roo.BasicDialog("x-msg-box", {
15746                     autoCreate : true,
15747                     shadow: true,
15748                     draggable: true,
15749                     resizable:false,
15750                     constraintoviewport:false,
15751                     fixedcenter:true,
15752                     collapsible : false,
15753                     shim:true,
15754                     modal: true,
15755                     width:400, height:100,
15756                     buttonAlign:"center",
15757                     closeClick : function(){
15758                         if(opt && opt.buttons && opt.buttons.no && !opt.buttons.cancel){
15759                             handleButton("no");
15760                         }else{
15761                             handleButton("cancel");
15762                         }
15763                     }
15764                 });
15765                 dlg.on("hide", handleHide);
15766                 mask = dlg.mask;
15767                 dlg.addKeyListener(27, handleEsc);
15768                 buttons = {};
15769                 var bt = this.buttonText;
15770                 buttons["ok"] = dlg.addButton(bt["ok"], handleButton.createCallback("ok"));
15771                 buttons["yes"] = dlg.addButton(bt["yes"], handleButton.createCallback("yes"));
15772                 buttons["no"] = dlg.addButton(bt["no"], handleButton.createCallback("no"));
15773                 buttons["cancel"] = dlg.addButton(bt["cancel"], handleButton.createCallback("cancel"));
15774                 bodyEl = dlg.body.createChild({
15775
15776                     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>'
15777                 });
15778                 msgEl = bodyEl.dom.firstChild;
15779                 textboxEl = Roo.get(bodyEl.dom.childNodes[2]);
15780                 textboxEl.enableDisplayMode();
15781                 textboxEl.addKeyListener([10,13], function(){
15782                     if(dlg.isVisible() && opt && opt.buttons){
15783                         if(opt.buttons.ok){
15784                             handleButton("ok");
15785                         }else if(opt.buttons.yes){
15786                             handleButton("yes");
15787                         }
15788                     }
15789                 });
15790                 textareaEl = Roo.get(bodyEl.dom.childNodes[3]);
15791                 textareaEl.enableDisplayMode();
15792                 progressEl = Roo.get(bodyEl.dom.childNodes[4]);
15793                 progressEl.enableDisplayMode();
15794                 var pf = progressEl.dom.firstChild;
15795                 if (pf) {
15796                     pp = Roo.get(pf.firstChild);
15797                     pp.setHeight(pf.offsetHeight);
15798                 }
15799                 
15800             }
15801             return dlg;
15802         },
15803
15804         /**
15805          * Updates the message box body text
15806          * @param {String} text (optional) Replaces the message box element's innerHTML with the specified string (defaults to
15807          * the XHTML-compliant non-breaking space character '&amp;#160;')
15808          * @return {Roo.MessageBox} This message box
15809          */
15810         updateText : function(text){
15811             if(!dlg.isVisible() && !opt.width){
15812                 dlg.resizeTo(this.maxWidth, 100); // resize first so content is never clipped from previous shows
15813             }
15814             msgEl.innerHTML = text || '&#160;';
15815       
15816             var cw =  Math.max(msgEl.offsetWidth, msgEl.parentNode.scrollWidth);
15817             //Roo.log("guesed size: " + JSON.stringify([cw,msgEl.offsetWidth, msgEl.parentNode.scrollWidth]));
15818             var w = Math.max(
15819                     Math.min(opt.width || cw , this.maxWidth), 
15820                     Math.max(opt.minWidth || this.minWidth, bwidth)
15821             );
15822             if(opt.prompt){
15823                 activeTextEl.setWidth(w);
15824             }
15825             if(dlg.isVisible()){
15826                 dlg.fixedcenter = false;
15827             }
15828             // to big, make it scroll. = But as usual stupid IE does not support
15829             // !important..
15830             
15831             if ( bodyEl.getHeight() > (Roo.lib.Dom.getViewHeight() - 100)) {
15832                 bodyEl.setHeight ( Roo.lib.Dom.getViewHeight() - 100 );
15833                 bodyEl.dom.style.overflowY = 'auto' + ( Roo.isIE ? '' : ' !important');
15834             } else {
15835                 bodyEl.dom.style.height = '';
15836                 bodyEl.dom.style.overflowY = '';
15837             }
15838             if (cw > w) {
15839                 bodyEl.dom.style.get = 'auto' + ( Roo.isIE ? '' : ' !important');
15840             } else {
15841                 bodyEl.dom.style.overflowX = '';
15842             }
15843             
15844             dlg.setContentSize(w, bodyEl.getHeight());
15845             if(dlg.isVisible()){
15846                 dlg.fixedcenter = true;
15847             }
15848             return this;
15849         },
15850
15851         /**
15852          * Updates a progress-style message box's text and progress bar.  Only relevant on message boxes
15853          * initiated via {@link Roo.MessageBox#progress} or by calling {@link Roo.MessageBox#show} with progress: true.
15854          * @param {Number} value Any number between 0 and 1 (e.g., .5)
15855          * @param {String} text (optional) If defined, the message box's body text is replaced with the specified string (defaults to undefined)
15856          * @return {Roo.MessageBox} This message box
15857          */
15858         updateProgress : function(value, text){
15859             if(text){
15860                 this.updateText(text);
15861             }
15862             if (pp) { // weird bug on my firefox - for some reason this is not defined
15863                 pp.setWidth(Math.floor(value*progressEl.dom.firstChild.offsetWidth));
15864             }
15865             return this;
15866         },        
15867
15868         /**
15869          * Returns true if the message box is currently displayed
15870          * @return {Boolean} True if the message box is visible, else false
15871          */
15872         isVisible : function(){
15873             return dlg && dlg.isVisible();  
15874         },
15875
15876         /**
15877          * Hides the message box if it is displayed
15878          */
15879         hide : function(){
15880             if(this.isVisible()){
15881                 dlg.hide();
15882             }  
15883         },
15884
15885         /**
15886          * Displays a new message box, or reinitializes an existing message box, based on the config options
15887          * passed in. All functions (e.g. prompt, alert, etc) on MessageBox call this function internally.
15888          * The following config object properties are supported:
15889          * <pre>
15890 Property    Type             Description
15891 ----------  ---------------  ------------------------------------------------------------------------------------
15892 animEl            String/Element   An id or Element from which the message box should animate as it opens and
15893                                    closes (defaults to undefined)
15894 buttons           Object/Boolean   A button config object (e.g., Roo.MessageBox.OKCANCEL or {ok:'Foo',
15895                                    cancel:'Bar'}), or false to not show any buttons (defaults to false)
15896 closable          Boolean          False to hide the top-right close button (defaults to true).  Note that
15897                                    progress and wait dialogs will ignore this property and always hide the
15898                                    close button as they can only be closed programmatically.
15899 cls               String           A custom CSS class to apply to the message box element
15900 defaultTextHeight Number           The default height in pixels of the message box's multiline textarea if
15901                                    displayed (defaults to 75)
15902 fn                Function         A callback function to execute after closing the dialog.  The arguments to the
15903                                    function will be btn (the name of the button that was clicked, if applicable,
15904                                    e.g. "ok"), and text (the value of the active text field, if applicable).
15905                                    Progress and wait dialogs will ignore this option since they do not respond to
15906                                    user actions and can only be closed programmatically, so any required function
15907                                    should be called by the same code after it closes the dialog.
15908 icon              String           A CSS class that provides a background image to be used as an icon for
15909                                    the dialog (e.g., Roo.MessageBox.WARNING or 'custom-class', defaults to '')
15910 maxWidth          Number           The maximum width in pixels of the message box (defaults to 600)
15911 minWidth          Number           The minimum width in pixels of the message box (defaults to 100)
15912 modal             Boolean          False to allow user interaction with the page while the message box is
15913                                    displayed (defaults to true)
15914 msg               String           A string that will replace the existing message box body text (defaults
15915                                    to the XHTML-compliant non-breaking space character '&#160;')
15916 multiline         Boolean          True to prompt the user to enter multi-line text (defaults to false)
15917 progress          Boolean          True to display a progress bar (defaults to false)
15918 progressText      String           The text to display inside the progress bar if progress = true (defaults to '')
15919 prompt            Boolean          True to prompt the user to enter single-line text (defaults to false)
15920 proxyDrag         Boolean          True to display a lightweight proxy while dragging (defaults to false)
15921 title             String           The title text
15922 value             String           The string value to set into the active textbox element if displayed
15923 wait              Boolean          True to display a progress bar (defaults to false)
15924 width             Number           The width of the dialog in pixels
15925 </pre>
15926          *
15927          * Example usage:
15928          * <pre><code>
15929 Roo.Msg.show({
15930    title: 'Address',
15931    msg: 'Please enter your address:',
15932    width: 300,
15933    buttons: Roo.MessageBox.OKCANCEL,
15934    multiline: true,
15935    fn: saveAddress,
15936    animEl: 'addAddressBtn'
15937 });
15938 </code></pre>
15939          * @param {Object} config Configuration options
15940          * @return {Roo.MessageBox} This message box
15941          */
15942         show : function(options)
15943         {
15944             
15945             // this causes nightmares if you show one dialog after another
15946             // especially on callbacks..
15947              
15948             if(this.isVisible()){
15949                 
15950                 this.hide();
15951                 Roo.log("[Roo.Messagebox] Show called while message displayed:" );
15952                 Roo.log("Old Dialog Message:" +  msgEl.innerHTML );
15953                 Roo.log("New Dialog Message:" +  options.msg )
15954                 //this.alert("ERROR", "Multiple dialogs where displayed at the same time");
15955                 //throw "Roo.MessageBox ERROR : Multiple dialogs where displayed at the same time";
15956                 
15957             }
15958             var d = this.getDialog();
15959             opt = options;
15960             d.setTitle(opt.title || "&#160;");
15961             d.close.setDisplayed(opt.closable !== false);
15962             activeTextEl = textboxEl;
15963             opt.prompt = opt.prompt || (opt.multiline ? true : false);
15964             if(opt.prompt){
15965                 if(opt.multiline){
15966                     textboxEl.hide();
15967                     textareaEl.show();
15968                     textareaEl.setHeight(typeof opt.multiline == "number" ?
15969                         opt.multiline : this.defaultTextHeight);
15970                     activeTextEl = textareaEl;
15971                 }else{
15972                     textboxEl.show();
15973                     textareaEl.hide();
15974                 }
15975             }else{
15976                 textboxEl.hide();
15977                 textareaEl.hide();
15978             }
15979             progressEl.setDisplayed(opt.progress === true);
15980             this.updateProgress(0);
15981             activeTextEl.dom.value = opt.value || "";
15982             if(opt.prompt){
15983                 dlg.setDefaultButton(activeTextEl);
15984             }else{
15985                 var bs = opt.buttons;
15986                 var db = null;
15987                 if(bs && bs.ok){
15988                     db = buttons["ok"];
15989                 }else if(bs && bs.yes){
15990                     db = buttons["yes"];
15991                 }
15992                 dlg.setDefaultButton(db);
15993             }
15994             bwidth = updateButtons(opt.buttons);
15995             this.updateText(opt.msg);
15996             if(opt.cls){
15997                 d.el.addClass(opt.cls);
15998             }
15999             d.proxyDrag = opt.proxyDrag === true;
16000             d.modal = opt.modal !== false;
16001             d.mask = opt.modal !== false ? mask : false;
16002             if(!d.isVisible()){
16003                 // force it to the end of the z-index stack so it gets a cursor in FF
16004                 document.body.appendChild(dlg.el.dom);
16005                 d.animateTarget = null;
16006                 d.show(options.animEl);
16007             }
16008             return this;
16009         },
16010
16011         /**
16012          * Displays a message box with a progress bar.  This message box has no buttons and is not closeable by
16013          * the user.  You are responsible for updating the progress bar as needed via {@link Roo.MessageBox#updateProgress}
16014          * and closing the message box when the process is complete.
16015          * @param {String} title The title bar text
16016          * @param {String} msg The message box body text
16017          * @return {Roo.MessageBox} This message box
16018          */
16019         progress : function(title, msg){
16020             this.show({
16021                 title : title,
16022                 msg : msg,
16023                 buttons: false,
16024                 progress:true,
16025                 closable:false,
16026                 minWidth: this.minProgressWidth,
16027                 modal : true
16028             });
16029             return this;
16030         },
16031
16032         /**
16033          * Displays a standard read-only message box with an OK button (comparable to the basic JavaScript Window.alert).
16034          * If a callback function is passed it will be called after the user clicks the button, and the
16035          * id of the button that was clicked will be passed as the only parameter to the callback
16036          * (could also be the top-right close button).
16037          * @param {String} title The title bar text
16038          * @param {String} msg The message box body text
16039          * @param {Function} fn (optional) The callback function invoked after the message box is closed
16040          * @param {Object} scope (optional) The scope of the callback function
16041          * @return {Roo.MessageBox} This message box
16042          */
16043         alert : function(title, msg, fn, scope){
16044             this.show({
16045                 title : title,
16046                 msg : msg,
16047                 buttons: this.OK,
16048                 fn: fn,
16049                 scope : scope,
16050                 modal : true
16051             });
16052             return this;
16053         },
16054
16055         /**
16056          * Displays a message box with an infinitely auto-updating progress bar.  This can be used to block user
16057          * interaction while waiting for a long-running process to complete that does not have defined intervals.
16058          * You are responsible for closing the message box when the process is complete.
16059          * @param {String} msg The message box body text
16060          * @param {String} title (optional) The title bar text
16061          * @return {Roo.MessageBox} This message box
16062          */
16063         wait : function(msg, title){
16064             this.show({
16065                 title : title,
16066                 msg : msg,
16067                 buttons: false,
16068                 closable:false,
16069                 progress:true,
16070                 modal:true,
16071                 width:300,
16072                 wait:true
16073             });
16074             waitTimer = Roo.TaskMgr.start({
16075                 run: function(i){
16076                     Roo.MessageBox.updateProgress(((((i+20)%20)+1)*5)*.01);
16077                 },
16078                 interval: 1000
16079             });
16080             return this;
16081         },
16082
16083         /**
16084          * Displays a confirmation message box with Yes and No buttons (comparable to JavaScript's Window.confirm).
16085          * If a callback function is passed it will be called after the user clicks either button, and the id of the
16086          * button that was clicked will be passed as the only parameter to the callback (could also be the top-right close button).
16087          * @param {String} title The title bar text
16088          * @param {String} msg The message box body text
16089          * @param {Function} fn (optional) The callback function invoked after the message box is closed
16090          * @param {Object} scope (optional) The scope of the callback function
16091          * @return {Roo.MessageBox} This message box
16092          */
16093         confirm : function(title, msg, fn, scope){
16094             this.show({
16095                 title : title,
16096                 msg : msg,
16097                 buttons: this.YESNO,
16098                 fn: fn,
16099                 scope : scope,
16100                 modal : true
16101             });
16102             return this;
16103         },
16104
16105         /**
16106          * Displays a message box with OK and Cancel buttons prompting the user to enter some text (comparable to
16107          * JavaScript's Window.prompt).  The prompt can be a single-line or multi-line textbox.  If a callback function
16108          * is passed it will be called after the user clicks either button, and the id of the button that was clicked
16109          * (could also be the top-right close button) and the text that was entered will be passed as the two
16110          * parameters to the callback.
16111          * @param {String} title The title bar text
16112          * @param {String} msg The message box body text
16113          * @param {Function} fn (optional) The callback function invoked after the message box is closed
16114          * @param {Object} scope (optional) The scope of the callback function
16115          * @param {Boolean/Number} multiline (optional) True to create a multiline textbox using the defaultTextHeight
16116          * property, or the height in pixels to create the textbox (defaults to false / single-line)
16117          * @return {Roo.MessageBox} This message box
16118          */
16119         prompt : function(title, msg, fn, scope, multiline){
16120             this.show({
16121                 title : title,
16122                 msg : msg,
16123                 buttons: this.OKCANCEL,
16124                 fn: fn,
16125                 minWidth:250,
16126                 scope : scope,
16127                 prompt:true,
16128                 multiline: multiline,
16129                 modal : true
16130             });
16131             return this;
16132         },
16133
16134         /**
16135          * Button config that displays a single OK button
16136          * @type Object
16137          */
16138         OK : {ok:true},
16139         /**
16140          * Button config that displays Yes and No buttons
16141          * @type Object
16142          */
16143         YESNO : {yes:true, no:true},
16144         /**
16145          * Button config that displays OK and Cancel buttons
16146          * @type Object
16147          */
16148         OKCANCEL : {ok:true, cancel:true},
16149         /**
16150          * Button config that displays Yes, No and Cancel buttons
16151          * @type Object
16152          */
16153         YESNOCANCEL : {yes:true, no:true, cancel:true},
16154
16155         /**
16156          * The default height in pixels of the message box's multiline textarea if displayed (defaults to 75)
16157          * @type Number
16158          */
16159         defaultTextHeight : 75,
16160         /**
16161          * The maximum width in pixels of the message box (defaults to 600)
16162          * @type Number
16163          */
16164         maxWidth : 600,
16165         /**
16166          * The minimum width in pixels of the message box (defaults to 100)
16167          * @type Number
16168          */
16169         minWidth : 100,
16170         /**
16171          * The minimum width in pixels of the message box if it is a progress-style dialog.  This is useful
16172          * for setting a different minimum width than text-only dialogs may need (defaults to 250)
16173          * @type Number
16174          */
16175         minProgressWidth : 250,
16176         /**
16177          * An object containing the default button text strings that can be overriden for localized language support.
16178          * Supported properties are: ok, cancel, yes and no.
16179          * Customize the default text like so: Roo.MessageBox.buttonText.yes = "S?";
16180          * @type Object
16181          */
16182         buttonText : {
16183             ok : "OK",
16184             cancel : "Cancel",
16185             yes : "Yes",
16186             no : "No"
16187         }
16188     };
16189 }();
16190
16191 /**
16192  * Shorthand for {@link Roo.MessageBox}
16193  */
16194 Roo.Msg = Roo.MessageBox;/*
16195  * Based on:
16196  * Ext JS Library 1.1.1
16197  * Copyright(c) 2006-2007, Ext JS, LLC.
16198  *
16199  * Originally Released Under LGPL - original licence link has changed is not relivant.
16200  *
16201  * Fork - LGPL
16202  * <script type="text/javascript">
16203  */
16204 /**
16205  * @class Roo.QuickTips
16206  * Provides attractive and customizable tooltips for any element.
16207  * @singleton
16208  */
16209 Roo.QuickTips = function(){
16210     var el, tipBody, tipBodyText, tipTitle, tm, cfg, close, tagEls = {}, esc, removeCls = null, bdLeft, bdRight;
16211     var ce, bd, xy, dd;
16212     var visible = false, disabled = true, inited = false;
16213     var showProc = 1, hideProc = 1, dismissProc = 1, locks = [];
16214     
16215     var onOver = function(e){
16216         if(disabled){
16217             return;
16218         }
16219         var t = e.getTarget();
16220         if(!t || t.nodeType !== 1 || t == document || t == document.body){
16221             return;
16222         }
16223         if(ce && t == ce.el){
16224             clearTimeout(hideProc);
16225             return;
16226         }
16227         if(t && tagEls[t.id]){
16228             tagEls[t.id].el = t;
16229             showProc = show.defer(tm.showDelay, tm, [tagEls[t.id]]);
16230             return;
16231         }
16232         var ttp, et = Roo.fly(t);
16233         var ns = cfg.namespace;
16234         if(tm.interceptTitles && t.title){
16235             ttp = t.title;
16236             t.qtip = ttp;
16237             t.removeAttribute("title");
16238             e.preventDefault();
16239         }else{
16240             ttp = t.qtip || et.getAttributeNS(ns, cfg.attribute);
16241         }
16242         if(ttp){
16243             showProc = show.defer(tm.showDelay, tm, [{
16244                 el: t, 
16245                 text: ttp, 
16246                 width: et.getAttributeNS(ns, cfg.width),
16247                 autoHide: et.getAttributeNS(ns, cfg.hide) != "user",
16248                 title: et.getAttributeNS(ns, cfg.title),
16249                     cls: et.getAttributeNS(ns, cfg.cls)
16250             }]);
16251         }
16252     };
16253     
16254     var onOut = function(e){
16255         clearTimeout(showProc);
16256         var t = e.getTarget();
16257         if(t && ce && ce.el == t && (tm.autoHide && ce.autoHide !== false)){
16258             hideProc = setTimeout(hide, tm.hideDelay);
16259         }
16260     };
16261     
16262     var onMove = function(e){
16263         if(disabled){
16264             return;
16265         }
16266         xy = e.getXY();
16267         xy[1] += 18;
16268         if(tm.trackMouse && ce){
16269             el.setXY(xy);
16270         }
16271     };
16272     
16273     var onDown = function(e){
16274         clearTimeout(showProc);
16275         clearTimeout(hideProc);
16276         if(!e.within(el)){
16277             if(tm.hideOnClick){
16278                 hide();
16279                 tm.disable();
16280                 tm.enable.defer(100, tm);
16281             }
16282         }
16283     };
16284     
16285     var getPad = function(){
16286         return 2;//bdLeft.getPadding('l')+bdRight.getPadding('r');
16287     };
16288
16289     var show = function(o){
16290         if(disabled){
16291             return;
16292         }
16293         clearTimeout(dismissProc);
16294         ce = o;
16295         if(removeCls){ // in case manually hidden
16296             el.removeClass(removeCls);
16297             removeCls = null;
16298         }
16299         if(ce.cls){
16300             el.addClass(ce.cls);
16301             removeCls = ce.cls;
16302         }
16303         if(ce.title){
16304             tipTitle.update(ce.title);
16305             tipTitle.show();
16306         }else{
16307             tipTitle.update('');
16308             tipTitle.hide();
16309         }
16310         el.dom.style.width  = tm.maxWidth+'px';
16311         //tipBody.dom.style.width = '';
16312         tipBodyText.update(o.text);
16313         var p = getPad(), w = ce.width;
16314         if(!w){
16315             var td = tipBodyText.dom;
16316             var aw = Math.max(td.offsetWidth, td.clientWidth, td.scrollWidth);
16317             if(aw > tm.maxWidth){
16318                 w = tm.maxWidth;
16319             }else if(aw < tm.minWidth){
16320                 w = tm.minWidth;
16321             }else{
16322                 w = aw;
16323             }
16324         }
16325         //tipBody.setWidth(w);
16326         el.setWidth(parseInt(w, 10) + p);
16327         if(ce.autoHide === false){
16328             close.setDisplayed(true);
16329             if(dd){
16330                 dd.unlock();
16331             }
16332         }else{
16333             close.setDisplayed(false);
16334             if(dd){
16335                 dd.lock();
16336             }
16337         }
16338         if(xy){
16339             el.avoidY = xy[1]-18;
16340             el.setXY(xy);
16341         }
16342         if(tm.animate){
16343             el.setOpacity(.1);
16344             el.setStyle("visibility", "visible");
16345             el.fadeIn({callback: afterShow});
16346         }else{
16347             afterShow();
16348         }
16349     };
16350     
16351     var afterShow = function(){
16352         if(ce){
16353             el.show();
16354             esc.enable();
16355             if(tm.autoDismiss && ce.autoHide !== false){
16356                 dismissProc = setTimeout(hide, tm.autoDismissDelay);
16357             }
16358         }
16359     };
16360     
16361     var hide = function(noanim){
16362         clearTimeout(dismissProc);
16363         clearTimeout(hideProc);
16364         ce = null;
16365         if(el.isVisible()){
16366             esc.disable();
16367             if(noanim !== true && tm.animate){
16368                 el.fadeOut({callback: afterHide});
16369             }else{
16370                 afterHide();
16371             } 
16372         }
16373     };
16374     
16375     var afterHide = function(){
16376         el.hide();
16377         if(removeCls){
16378             el.removeClass(removeCls);
16379             removeCls = null;
16380         }
16381     };
16382     
16383     return {
16384         /**
16385         * @cfg {Number} minWidth
16386         * The minimum width of the quick tip (defaults to 40)
16387         */
16388        minWidth : 40,
16389         /**
16390         * @cfg {Number} maxWidth
16391         * The maximum width of the quick tip (defaults to 300)
16392         */
16393        maxWidth : 300,
16394         /**
16395         * @cfg {Boolean} interceptTitles
16396         * True to automatically use the element's DOM title value if available (defaults to false)
16397         */
16398        interceptTitles : false,
16399         /**
16400         * @cfg {Boolean} trackMouse
16401         * True to have the quick tip follow the mouse as it moves over the target element (defaults to false)
16402         */
16403        trackMouse : false,
16404         /**
16405         * @cfg {Boolean} hideOnClick
16406         * True to hide the quick tip if the user clicks anywhere in the document (defaults to true)
16407         */
16408        hideOnClick : true,
16409         /**
16410         * @cfg {Number} showDelay
16411         * Delay in milliseconds before the quick tip displays after the mouse enters the target element (defaults to 500)
16412         */
16413        showDelay : 500,
16414         /**
16415         * @cfg {Number} hideDelay
16416         * Delay in milliseconds before the quick tip hides when autoHide = true (defaults to 200)
16417         */
16418        hideDelay : 200,
16419         /**
16420         * @cfg {Boolean} autoHide
16421         * True to automatically hide the quick tip after the mouse exits the target element (defaults to true).
16422         * Used in conjunction with hideDelay.
16423         */
16424        autoHide : true,
16425         /**
16426         * @cfg {Boolean}
16427         * True to automatically hide the quick tip after a set period of time, regardless of the user's actions
16428         * (defaults to true).  Used in conjunction with autoDismissDelay.
16429         */
16430        autoDismiss : true,
16431         /**
16432         * @cfg {Number}
16433         * Delay in milliseconds before the quick tip hides when autoDismiss = true (defaults to 5000)
16434         */
16435        autoDismissDelay : 5000,
16436        /**
16437         * @cfg {Boolean} animate
16438         * True to turn on fade animation. Defaults to false (ClearType/scrollbar flicker issues in IE7).
16439         */
16440        animate : false,
16441
16442        /**
16443         * @cfg {String} title
16444         * Title text to display (defaults to '').  This can be any valid HTML markup.
16445         */
16446         title: '',
16447        /**
16448         * @cfg {String} text
16449         * Body text to display (defaults to '').  This can be any valid HTML markup.
16450         */
16451         text : '',
16452        /**
16453         * @cfg {String} cls
16454         * A CSS class to apply to the base quick tip element (defaults to '').
16455         */
16456         cls : '',
16457        /**
16458         * @cfg {Number} width
16459         * Width in pixels of the quick tip (defaults to auto).  Width will be ignored if it exceeds the bounds of
16460         * minWidth or maxWidth.
16461         */
16462         width : null,
16463
16464     /**
16465      * Initialize and enable QuickTips for first use.  This should be called once before the first attempt to access
16466      * or display QuickTips in a page.
16467      */
16468        init : function(){
16469           tm = Roo.QuickTips;
16470           cfg = tm.tagConfig;
16471           if(!inited){
16472               if(!Roo.isReady){ // allow calling of init() before onReady
16473                   Roo.onReady(Roo.QuickTips.init, Roo.QuickTips);
16474                   return;
16475               }
16476               el = new Roo.Layer({cls:"x-tip", shadow:"drop", shim: true, constrain:true, shadowOffset:4});
16477               el.fxDefaults = {stopFx: true};
16478               // maximum custom styling
16479               //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>');
16480               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>');              
16481               tipTitle = el.child('h3');
16482               tipTitle.enableDisplayMode("block");
16483               tipBody = el.child('div.x-tip-bd');
16484               tipBodyText = el.child('div.x-tip-bd-inner');
16485               //bdLeft = el.child('div.x-tip-bd-left');
16486               //bdRight = el.child('div.x-tip-bd-right');
16487               close = el.child('div.x-tip-close');
16488               close.enableDisplayMode("block");
16489               close.on("click", hide);
16490               var d = Roo.get(document);
16491               d.on("mousedown", onDown);
16492               d.on("mouseover", onOver);
16493               d.on("mouseout", onOut);
16494               d.on("mousemove", onMove);
16495               esc = d.addKeyListener(27, hide);
16496               esc.disable();
16497               if(Roo.dd.DD){
16498                   dd = el.initDD("default", null, {
16499                       onDrag : function(){
16500                           el.sync();  
16501                       }
16502                   });
16503                   dd.setHandleElId(tipTitle.id);
16504                   dd.lock();
16505               }
16506               inited = true;
16507           }
16508           this.enable(); 
16509        },
16510
16511     /**
16512      * Configures a new quick tip instance and assigns it to a target element.  The following config options
16513      * are supported:
16514      * <pre>
16515 Property    Type                   Description
16516 ----------  ---------------------  ------------------------------------------------------------------------
16517 target      Element/String/Array   An Element, id or array of ids that this quick tip should be tied to
16518      * </ul>
16519      * @param {Object} config The config object
16520      */
16521        register : function(config){
16522            var cs = config instanceof Array ? config : arguments;
16523            for(var i = 0, len = cs.length; i < len; i++) {
16524                var c = cs[i];
16525                var target = c.target;
16526                if(target){
16527                    if(target instanceof Array){
16528                        for(var j = 0, jlen = target.length; j < jlen; j++){
16529                            tagEls[target[j]] = c;
16530                        }
16531                    }else{
16532                        tagEls[typeof target == 'string' ? target : Roo.id(target)] = c;
16533                    }
16534                }
16535            }
16536        },
16537
16538     /**
16539      * Removes this quick tip from its element and destroys it.
16540      * @param {String/HTMLElement/Element} el The element from which the quick tip is to be removed.
16541      */
16542        unregister : function(el){
16543            delete tagEls[Roo.id(el)];
16544        },
16545
16546     /**
16547      * Enable this quick tip.
16548      */
16549        enable : function(){
16550            if(inited && disabled){
16551                locks.pop();
16552                if(locks.length < 1){
16553                    disabled = false;
16554                }
16555            }
16556        },
16557
16558     /**
16559      * Disable this quick tip.
16560      */
16561        disable : function(){
16562           disabled = true;
16563           clearTimeout(showProc);
16564           clearTimeout(hideProc);
16565           clearTimeout(dismissProc);
16566           if(ce){
16567               hide(true);
16568           }
16569           locks.push(1);
16570        },
16571
16572     /**
16573      * Returns true if the quick tip is enabled, else false.
16574      */
16575        isEnabled : function(){
16576             return !disabled;
16577        },
16578
16579         // private
16580        tagConfig : {
16581            namespace : "ext",
16582            attribute : "qtip",
16583            width : "width",
16584            target : "target",
16585            title : "qtitle",
16586            hide : "hide",
16587            cls : "qclass"
16588        }
16589    };
16590 }();
16591
16592 // backwards compat
16593 Roo.QuickTips.tips = Roo.QuickTips.register;/*
16594  * Based on:
16595  * Ext JS Library 1.1.1
16596  * Copyright(c) 2006-2007, Ext JS, LLC.
16597  *
16598  * Originally Released Under LGPL - original licence link has changed is not relivant.
16599  *
16600  * Fork - LGPL
16601  * <script type="text/javascript">
16602  */
16603  
16604
16605 /**
16606  * @class Roo.tree.TreePanel
16607  * @extends Roo.data.Tree
16608
16609  * @cfg {Boolean} rootVisible false to hide the root node (defaults to true)
16610  * @cfg {Boolean} lines false to disable tree lines (defaults to true)
16611  * @cfg {Boolean} enableDD true to enable drag and drop
16612  * @cfg {Boolean} enableDrag true to enable just drag
16613  * @cfg {Boolean} enableDrop true to enable just drop
16614  * @cfg {Object} dragConfig Custom config to pass to the {@link Roo.tree.TreeDragZone} instance
16615  * @cfg {Object} dropConfig Custom config to pass to the {@link Roo.tree.TreeDropZone} instance
16616  * @cfg {String} ddGroup The DD group this TreePanel belongs to
16617  * @cfg {String} ddAppendOnly True if the tree should only allow append drops (use for trees which are sorted)
16618  * @cfg {Boolean} ddScroll true to enable YUI body scrolling
16619  * @cfg {Boolean} containerScroll true to register this container with ScrollManager
16620  * @cfg {Boolean} hlDrop false to disable node highlight on drop (defaults to the value of Roo.enableFx)
16621  * @cfg {String} hlColor The color of the node highlight (defaults to C3DAF9)
16622  * @cfg {Boolean} animate true to enable animated expand/collapse (defaults to the value of Roo.enableFx)
16623  * @cfg {Boolean} singleExpand true if only 1 node per branch may be expanded
16624  * @cfg {Boolean} selModel A tree selection model to use with this TreePanel (defaults to a {@link Roo.tree.DefaultSelectionModel})
16625  * @cfg {Boolean} loader A TreeLoader for use with this TreePanel
16626  * @cfg {Object|Roo.tree.TreeEditor} editor The TreeEditor or xtype data to display when clicked.
16627  * @cfg {String} pathSeparator The token used to separate sub-paths in path strings (defaults to '/')
16628  * @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>
16629  * @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>
16630  * 
16631  * @constructor
16632  * @param {String/HTMLElement/Element} el The container element
16633  * @param {Object} config
16634  */
16635 Roo.tree.TreePanel = function(el, config){
16636     var root = false;
16637     var loader = false;
16638     if (config.root) {
16639         root = config.root;
16640         delete config.root;
16641     }
16642     if (config.loader) {
16643         loader = config.loader;
16644         delete config.loader;
16645     }
16646     
16647     Roo.apply(this, config);
16648     Roo.tree.TreePanel.superclass.constructor.call(this);
16649     this.el = Roo.get(el);
16650     this.el.addClass('x-tree');
16651     //console.log(root);
16652     if (root) {
16653         this.setRootNode( Roo.factory(root, Roo.tree));
16654     }
16655     if (loader) {
16656         this.loader = Roo.factory(loader, Roo.tree);
16657     }
16658    /**
16659     * Read-only. The id of the container element becomes this TreePanel's id.
16660     */
16661     this.id = this.el.id;
16662     this.addEvents({
16663         /**
16664         * @event beforeload
16665         * Fires before a node is loaded, return false to cancel
16666         * @param {Node} node The node being loaded
16667         */
16668         "beforeload" : true,
16669         /**
16670         * @event load
16671         * Fires when a node is loaded
16672         * @param {Node} node The node that was loaded
16673         */
16674         "load" : true,
16675         /**
16676         * @event textchange
16677         * Fires when the text for a node is changed
16678         * @param {Node} node The node
16679         * @param {String} text The new text
16680         * @param {String} oldText The old text
16681         */
16682         "textchange" : true,
16683         /**
16684         * @event beforeexpand
16685         * Fires before a node is expanded, return false to cancel.
16686         * @param {Node} node The node
16687         * @param {Boolean} deep
16688         * @param {Boolean} anim
16689         */
16690         "beforeexpand" : true,
16691         /**
16692         * @event beforecollapse
16693         * Fires before a node is collapsed, return false to cancel.
16694         * @param {Node} node The node
16695         * @param {Boolean} deep
16696         * @param {Boolean} anim
16697         */
16698         "beforecollapse" : true,
16699         /**
16700         * @event expand
16701         * Fires when a node is expanded
16702         * @param {Node} node The node
16703         */
16704         "expand" : true,
16705         /**
16706         * @event disabledchange
16707         * Fires when the disabled status of a node changes
16708         * @param {Node} node The node
16709         * @param {Boolean} disabled
16710         */
16711         "disabledchange" : true,
16712         /**
16713         * @event collapse
16714         * Fires when a node is collapsed
16715         * @param {Node} node The node
16716         */
16717         "collapse" : true,
16718         /**
16719         * @event beforeclick
16720         * Fires before click processing on a node. Return false to cancel the default action.
16721         * @param {Node} node The node
16722         * @param {Roo.EventObject} e The event object
16723         */
16724         "beforeclick":true,
16725         /**
16726         * @event checkchange
16727         * Fires when a node with a checkbox's checked property changes
16728         * @param {Node} this This node
16729         * @param {Boolean} checked
16730         */
16731         "checkchange":true,
16732         /**
16733         * @event click
16734         * Fires when a node is clicked
16735         * @param {Node} node The node
16736         * @param {Roo.EventObject} e The event object
16737         */
16738         "click":true,
16739         /**
16740         * @event dblclick
16741         * Fires when a node is double clicked
16742         * @param {Node} node The node
16743         * @param {Roo.EventObject} e The event object
16744         */
16745         "dblclick":true,
16746         /**
16747         * @event contextmenu
16748         * Fires when a node is right clicked
16749         * @param {Node} node The node
16750         * @param {Roo.EventObject} e The event object
16751         */
16752         "contextmenu":true,
16753         /**
16754         * @event beforechildrenrendered
16755         * Fires right before the child nodes for a node are rendered
16756         * @param {Node} node The node
16757         */
16758         "beforechildrenrendered":true,
16759         /**
16760         * @event startdrag
16761         * Fires when a node starts being dragged
16762         * @param {Roo.tree.TreePanel} this
16763         * @param {Roo.tree.TreeNode} node
16764         * @param {event} e The raw browser event
16765         */ 
16766        "startdrag" : true,
16767        /**
16768         * @event enddrag
16769         * Fires when a drag operation is complete
16770         * @param {Roo.tree.TreePanel} this
16771         * @param {Roo.tree.TreeNode} node
16772         * @param {event} e The raw browser event
16773         */
16774        "enddrag" : true,
16775        /**
16776         * @event dragdrop
16777         * Fires when a dragged node is dropped on a valid DD target
16778         * @param {Roo.tree.TreePanel} this
16779         * @param {Roo.tree.TreeNode} node
16780         * @param {DD} dd The dd it was dropped on
16781         * @param {event} e The raw browser event
16782         */
16783        "dragdrop" : true,
16784        /**
16785         * @event beforenodedrop
16786         * Fires when a DD object is dropped on a node in this tree for preprocessing. Return false to cancel the drop. The dropEvent
16787         * passed to handlers has the following properties:<br />
16788         * <ul style="padding:5px;padding-left:16px;">
16789         * <li>tree - The TreePanel</li>
16790         * <li>target - The node being targeted for the drop</li>
16791         * <li>data - The drag data from the drag source</li>
16792         * <li>point - The point of the drop - append, above or below</li>
16793         * <li>source - The drag source</li>
16794         * <li>rawEvent - Raw mouse event</li>
16795         * <li>dropNode - Drop node(s) provided by the source <b>OR</b> you can supply node(s)
16796         * to be inserted by setting them on this object.</li>
16797         * <li>cancel - Set this to true to cancel the drop.</li>
16798         * </ul>
16799         * @param {Object} dropEvent
16800         */
16801        "beforenodedrop" : true,
16802        /**
16803         * @event nodedrop
16804         * Fires after a DD object is dropped on a node in this tree. The dropEvent
16805         * passed to handlers has the following properties:<br />
16806         * <ul style="padding:5px;padding-left:16px;">
16807         * <li>tree - The TreePanel</li>
16808         * <li>target - The node being targeted for the drop</li>
16809         * <li>data - The drag data from the drag source</li>
16810         * <li>point - The point of the drop - append, above or below</li>
16811         * <li>source - The drag source</li>
16812         * <li>rawEvent - Raw mouse event</li>
16813         * <li>dropNode - Dropped node(s).</li>
16814         * </ul>
16815         * @param {Object} dropEvent
16816         */
16817        "nodedrop" : true,
16818         /**
16819         * @event nodedragover
16820         * Fires when a tree node is being targeted for a drag drop, return false to signal drop not allowed. The dragOverEvent
16821         * passed to handlers has the following properties:<br />
16822         * <ul style="padding:5px;padding-left:16px;">
16823         * <li>tree - The TreePanel</li>
16824         * <li>target - The node being targeted for the drop</li>
16825         * <li>data - The drag data from the drag source</li>
16826         * <li>point - The point of the drop - append, above or below</li>
16827         * <li>source - The drag source</li>
16828         * <li>rawEvent - Raw mouse event</li>
16829         * <li>dropNode - Drop node(s) provided by the source.</li>
16830         * <li>cancel - Set this to true to signal drop not allowed.</li>
16831         * </ul>
16832         * @param {Object} dragOverEvent
16833         */
16834        "nodedragover" : true
16835         
16836     });
16837     if(this.singleExpand){
16838        this.on("beforeexpand", this.restrictExpand, this);
16839     }
16840     if (this.editor) {
16841         this.editor.tree = this;
16842         this.editor = Roo.factory(this.editor, Roo.tree);
16843     }
16844     
16845     if (this.selModel) {
16846         this.selModel = Roo.factory(this.selModel, Roo.tree);
16847     }
16848    
16849 };
16850 Roo.extend(Roo.tree.TreePanel, Roo.data.Tree, {
16851     rootVisible : true,
16852     animate: Roo.enableFx,
16853     lines : true,
16854     enableDD : false,
16855     hlDrop : Roo.enableFx,
16856   
16857     renderer: false,
16858     
16859     rendererTip: false,
16860     // private
16861     restrictExpand : function(node){
16862         var p = node.parentNode;
16863         if(p){
16864             if(p.expandedChild && p.expandedChild.parentNode == p){
16865                 p.expandedChild.collapse();
16866             }
16867             p.expandedChild = node;
16868         }
16869     },
16870
16871     // private override
16872     setRootNode : function(node){
16873         Roo.tree.TreePanel.superclass.setRootNode.call(this, node);
16874         if(!this.rootVisible){
16875             node.ui = new Roo.tree.RootTreeNodeUI(node);
16876         }
16877         return node;
16878     },
16879
16880     /**
16881      * Returns the container element for this TreePanel
16882      */
16883     getEl : function(){
16884         return this.el;
16885     },
16886
16887     /**
16888      * Returns the default TreeLoader for this TreePanel
16889      */
16890     getLoader : function(){
16891         return this.loader;
16892     },
16893
16894     /**
16895      * Expand all nodes
16896      */
16897     expandAll : function(){
16898         this.root.expand(true);
16899     },
16900
16901     /**
16902      * Collapse all nodes
16903      */
16904     collapseAll : function(){
16905         this.root.collapse(true);
16906     },
16907
16908     /**
16909      * Returns the selection model used by this TreePanel
16910      */
16911     getSelectionModel : function(){
16912         if(!this.selModel){
16913             this.selModel = new Roo.tree.DefaultSelectionModel();
16914         }
16915         return this.selModel;
16916     },
16917
16918     /**
16919      * Retrieve an array of checked nodes, or an array of a specific attribute of checked nodes (e.g. "id")
16920      * @param {String} attribute (optional) Defaults to null (return the actual nodes)
16921      * @param {TreeNode} startNode (optional) The node to start from, defaults to the root
16922      * @return {Array}
16923      */
16924     getChecked : function(a, startNode){
16925         startNode = startNode || this.root;
16926         var r = [];
16927         var f = function(){
16928             if(this.attributes.checked){
16929                 r.push(!a ? this : (a == 'id' ? this.id : this.attributes[a]));
16930             }
16931         }
16932         startNode.cascade(f);
16933         return r;
16934     },
16935
16936     /**
16937      * Expands a specified path in this TreePanel. A path can be retrieved from a node with {@link Roo.data.Node#getPath}
16938      * @param {String} path
16939      * @param {String} attr (optional) The attribute used in the path (see {@link Roo.data.Node#getPath} for more info)
16940      * @param {Function} callback (optional) The callback to call when the expand is complete. The callback will be called with
16941      * (bSuccess, oLastNode) where bSuccess is if the expand was successful and oLastNode is the last node that was expanded.
16942      */
16943     expandPath : function(path, attr, callback){
16944         attr = attr || "id";
16945         var keys = path.split(this.pathSeparator);
16946         var curNode = this.root;
16947         if(curNode.attributes[attr] != keys[1]){ // invalid root
16948             if(callback){
16949                 callback(false, null);
16950             }
16951             return;
16952         }
16953         var index = 1;
16954         var f = function(){
16955             if(++index == keys.length){
16956                 if(callback){
16957                     callback(true, curNode);
16958                 }
16959                 return;
16960             }
16961             var c = curNode.findChild(attr, keys[index]);
16962             if(!c){
16963                 if(callback){
16964                     callback(false, curNode);
16965                 }
16966                 return;
16967             }
16968             curNode = c;
16969             c.expand(false, false, f);
16970         };
16971         curNode.expand(false, false, f);
16972     },
16973
16974     /**
16975      * Selects the node in this tree at the specified path. A path can be retrieved from a node with {@link Roo.data.Node#getPath}
16976      * @param {String} path
16977      * @param {String} attr (optional) The attribute used in the path (see {@link Roo.data.Node#getPath} for more info)
16978      * @param {Function} callback (optional) The callback to call when the selection is complete. The callback will be called with
16979      * (bSuccess, oSelNode) where bSuccess is if the selection was successful and oSelNode is the selected node.
16980      */
16981     selectPath : function(path, attr, callback){
16982         attr = attr || "id";
16983         var keys = path.split(this.pathSeparator);
16984         var v = keys.pop();
16985         if(keys.length > 0){
16986             var f = function(success, node){
16987                 if(success && node){
16988                     var n = node.findChild(attr, v);
16989                     if(n){
16990                         n.select();
16991                         if(callback){
16992                             callback(true, n);
16993                         }
16994                     }else if(callback){
16995                         callback(false, n);
16996                     }
16997                 }else{
16998                     if(callback){
16999                         callback(false, n);
17000                     }
17001                 }
17002             };
17003             this.expandPath(keys.join(this.pathSeparator), attr, f);
17004         }else{
17005             this.root.select();
17006             if(callback){
17007                 callback(true, this.root);
17008             }
17009         }
17010     },
17011
17012     getTreeEl : function(){
17013         return this.el;
17014     },
17015
17016     /**
17017      * Trigger rendering of this TreePanel
17018      */
17019     render : function(){
17020         if (this.innerCt) {
17021             return this; // stop it rendering more than once!!
17022         }
17023         
17024         this.innerCt = this.el.createChild({tag:"ul",
17025                cls:"x-tree-root-ct " +
17026                (this.lines ? "x-tree-lines" : "x-tree-no-lines")});
17027
17028         if(this.containerScroll){
17029             Roo.dd.ScrollManager.register(this.el);
17030         }
17031         if((this.enableDD || this.enableDrop) && !this.dropZone){
17032            /**
17033             * The dropZone used by this tree if drop is enabled
17034             * @type Roo.tree.TreeDropZone
17035             */
17036              this.dropZone = new Roo.tree.TreeDropZone(this, this.dropConfig || {
17037                ddGroup: this.ddGroup || "TreeDD", appendOnly: this.ddAppendOnly === true
17038            });
17039         }
17040         if((this.enableDD || this.enableDrag) && !this.dragZone){
17041            /**
17042             * The dragZone used by this tree if drag is enabled
17043             * @type Roo.tree.TreeDragZone
17044             */
17045             this.dragZone = new Roo.tree.TreeDragZone(this, this.dragConfig || {
17046                ddGroup: this.ddGroup || "TreeDD",
17047                scroll: this.ddScroll
17048            });
17049         }
17050         this.getSelectionModel().init(this);
17051         if (!this.root) {
17052             Roo.log("ROOT not set in tree");
17053             return this;
17054         }
17055         this.root.render();
17056         if(!this.rootVisible){
17057             this.root.renderChildren();
17058         }
17059         return this;
17060     }
17061 });/*
17062  * Based on:
17063  * Ext JS Library 1.1.1
17064  * Copyright(c) 2006-2007, Ext JS, LLC.
17065  *
17066  * Originally Released Under LGPL - original licence link has changed is not relivant.
17067  *
17068  * Fork - LGPL
17069  * <script type="text/javascript">
17070  */
17071  
17072
17073 /**
17074  * @class Roo.tree.DefaultSelectionModel
17075  * @extends Roo.util.Observable
17076  * The default single selection for a TreePanel.
17077  * @param {Object} cfg Configuration
17078  */
17079 Roo.tree.DefaultSelectionModel = function(cfg){
17080    this.selNode = null;
17081    
17082    
17083    
17084    this.addEvents({
17085        /**
17086         * @event selectionchange
17087         * Fires when the selected node changes
17088         * @param {DefaultSelectionModel} this
17089         * @param {TreeNode} node the new selection
17090         */
17091        "selectionchange" : true,
17092
17093        /**
17094         * @event beforeselect
17095         * Fires before the selected node changes, return false to cancel the change
17096         * @param {DefaultSelectionModel} this
17097         * @param {TreeNode} node the new selection
17098         * @param {TreeNode} node the old selection
17099         */
17100        "beforeselect" : true
17101    });
17102    
17103     Roo.tree.DefaultSelectionModel.superclass.constructor.call(this,cfg);
17104 };
17105
17106 Roo.extend(Roo.tree.DefaultSelectionModel, Roo.util.Observable, {
17107     init : function(tree){
17108         this.tree = tree;
17109         tree.getTreeEl().on("keydown", this.onKeyDown, this);
17110         tree.on("click", this.onNodeClick, this);
17111     },
17112     
17113     onNodeClick : function(node, e){
17114         if (e.ctrlKey && this.selNode == node)  {
17115             this.unselect(node);
17116             return;
17117         }
17118         this.select(node);
17119     },
17120     
17121     /**
17122      * Select a node.
17123      * @param {TreeNode} node The node to select
17124      * @return {TreeNode} The selected node
17125      */
17126     select : function(node){
17127         var last = this.selNode;
17128         if(last != node && this.fireEvent('beforeselect', this, node, last) !== false){
17129             if(last){
17130                 last.ui.onSelectedChange(false);
17131             }
17132             this.selNode = node;
17133             node.ui.onSelectedChange(true);
17134             this.fireEvent("selectionchange", this, node, last);
17135         }
17136         return node;
17137     },
17138     
17139     /**
17140      * Deselect a node.
17141      * @param {TreeNode} node The node to unselect
17142      */
17143     unselect : function(node){
17144         if(this.selNode == node){
17145             this.clearSelections();
17146         }    
17147     },
17148     
17149     /**
17150      * Clear all selections
17151      */
17152     clearSelections : function(){
17153         var n = this.selNode;
17154         if(n){
17155             n.ui.onSelectedChange(false);
17156             this.selNode = null;
17157             this.fireEvent("selectionchange", this, null);
17158         }
17159         return n;
17160     },
17161     
17162     /**
17163      * Get the selected node
17164      * @return {TreeNode} The selected node
17165      */
17166     getSelectedNode : function(){
17167         return this.selNode;    
17168     },
17169     
17170     /**
17171      * Returns true if the node is selected
17172      * @param {TreeNode} node The node to check
17173      * @return {Boolean}
17174      */
17175     isSelected : function(node){
17176         return this.selNode == node;  
17177     },
17178
17179     /**
17180      * Selects the node above the selected node in the tree, intelligently walking the nodes
17181      * @return TreeNode The new selection
17182      */
17183     selectPrevious : function(){
17184         var s = this.selNode || this.lastSelNode;
17185         if(!s){
17186             return null;
17187         }
17188         var ps = s.previousSibling;
17189         if(ps){
17190             if(!ps.isExpanded() || ps.childNodes.length < 1){
17191                 return this.select(ps);
17192             } else{
17193                 var lc = ps.lastChild;
17194                 while(lc && lc.isExpanded() && lc.childNodes.length > 0){
17195                     lc = lc.lastChild;
17196                 }
17197                 return this.select(lc);
17198             }
17199         } else if(s.parentNode && (this.tree.rootVisible || !s.parentNode.isRoot)){
17200             return this.select(s.parentNode);
17201         }
17202         return null;
17203     },
17204
17205     /**
17206      * Selects the node above the selected node in the tree, intelligently walking the nodes
17207      * @return TreeNode The new selection
17208      */
17209     selectNext : function(){
17210         var s = this.selNode || this.lastSelNode;
17211         if(!s){
17212             return null;
17213         }
17214         if(s.firstChild && s.isExpanded()){
17215              return this.select(s.firstChild);
17216          }else if(s.nextSibling){
17217              return this.select(s.nextSibling);
17218          }else if(s.parentNode){
17219             var newS = null;
17220             s.parentNode.bubble(function(){
17221                 if(this.nextSibling){
17222                     newS = this.getOwnerTree().selModel.select(this.nextSibling);
17223                     return false;
17224                 }
17225             });
17226             return newS;
17227          }
17228         return null;
17229     },
17230
17231     onKeyDown : function(e){
17232         var s = this.selNode || this.lastSelNode;
17233         // undesirable, but required
17234         var sm = this;
17235         if(!s){
17236             return;
17237         }
17238         var k = e.getKey();
17239         switch(k){
17240              case e.DOWN:
17241                  e.stopEvent();
17242                  this.selectNext();
17243              break;
17244              case e.UP:
17245                  e.stopEvent();
17246                  this.selectPrevious();
17247              break;
17248              case e.RIGHT:
17249                  e.preventDefault();
17250                  if(s.hasChildNodes()){
17251                      if(!s.isExpanded()){
17252                          s.expand();
17253                      }else if(s.firstChild){
17254                          this.select(s.firstChild, e);
17255                      }
17256                  }
17257              break;
17258              case e.LEFT:
17259                  e.preventDefault();
17260                  if(s.hasChildNodes() && s.isExpanded()){
17261                      s.collapse();
17262                  }else if(s.parentNode && (this.tree.rootVisible || s.parentNode != this.tree.getRootNode())){
17263                      this.select(s.parentNode, e);
17264                  }
17265              break;
17266         };
17267     }
17268 });
17269
17270 /**
17271  * @class Roo.tree.MultiSelectionModel
17272  * @extends Roo.util.Observable
17273  * Multi selection for a TreePanel.
17274  * @param {Object} cfg Configuration
17275  */
17276 Roo.tree.MultiSelectionModel = function(){
17277    this.selNodes = [];
17278    this.selMap = {};
17279    this.addEvents({
17280        /**
17281         * @event selectionchange
17282         * Fires when the selected nodes change
17283         * @param {MultiSelectionModel} this
17284         * @param {Array} nodes Array of the selected nodes
17285         */
17286        "selectionchange" : true
17287    });
17288    Roo.tree.MultiSelectionModel.superclass.constructor.call(this,cfg);
17289    
17290 };
17291
17292 Roo.extend(Roo.tree.MultiSelectionModel, Roo.util.Observable, {
17293     init : function(tree){
17294         this.tree = tree;
17295         tree.getTreeEl().on("keydown", this.onKeyDown, this);
17296         tree.on("click", this.onNodeClick, this);
17297     },
17298     
17299     onNodeClick : function(node, e){
17300         this.select(node, e, e.ctrlKey);
17301     },
17302     
17303     /**
17304      * Select a node.
17305      * @param {TreeNode} node The node to select
17306      * @param {EventObject} e (optional) An event associated with the selection
17307      * @param {Boolean} keepExisting True to retain existing selections
17308      * @return {TreeNode} The selected node
17309      */
17310     select : function(node, e, keepExisting){
17311         if(keepExisting !== true){
17312             this.clearSelections(true);
17313         }
17314         if(this.isSelected(node)){
17315             this.lastSelNode = node;
17316             return node;
17317         }
17318         this.selNodes.push(node);
17319         this.selMap[node.id] = node;
17320         this.lastSelNode = node;
17321         node.ui.onSelectedChange(true);
17322         this.fireEvent("selectionchange", this, this.selNodes);
17323         return node;
17324     },
17325     
17326     /**
17327      * Deselect a node.
17328      * @param {TreeNode} node The node to unselect
17329      */
17330     unselect : function(node){
17331         if(this.selMap[node.id]){
17332             node.ui.onSelectedChange(false);
17333             var sn = this.selNodes;
17334             var index = -1;
17335             if(sn.indexOf){
17336                 index = sn.indexOf(node);
17337             }else{
17338                 for(var i = 0, len = sn.length; i < len; i++){
17339                     if(sn[i] == node){
17340                         index = i;
17341                         break;
17342                     }
17343                 }
17344             }
17345             if(index != -1){
17346                 this.selNodes.splice(index, 1);
17347             }
17348             delete this.selMap[node.id];
17349             this.fireEvent("selectionchange", this, this.selNodes);
17350         }
17351     },
17352     
17353     /**
17354      * Clear all selections
17355      */
17356     clearSelections : function(suppressEvent){
17357         var sn = this.selNodes;
17358         if(sn.length > 0){
17359             for(var i = 0, len = sn.length; i < len; i++){
17360                 sn[i].ui.onSelectedChange(false);
17361             }
17362             this.selNodes = [];
17363             this.selMap = {};
17364             if(suppressEvent !== true){
17365                 this.fireEvent("selectionchange", this, this.selNodes);
17366             }
17367         }
17368     },
17369     
17370     /**
17371      * Returns true if the node is selected
17372      * @param {TreeNode} node The node to check
17373      * @return {Boolean}
17374      */
17375     isSelected : function(node){
17376         return this.selMap[node.id] ? true : false;  
17377     },
17378     
17379     /**
17380      * Returns an array of the selected nodes
17381      * @return {Array}
17382      */
17383     getSelectedNodes : function(){
17384         return this.selNodes;    
17385     },
17386
17387     onKeyDown : Roo.tree.DefaultSelectionModel.prototype.onKeyDown,
17388
17389     selectNext : Roo.tree.DefaultSelectionModel.prototype.selectNext,
17390
17391     selectPrevious : Roo.tree.DefaultSelectionModel.prototype.selectPrevious
17392 });/*
17393  * Based on:
17394  * Ext JS Library 1.1.1
17395  * Copyright(c) 2006-2007, Ext JS, LLC.
17396  *
17397  * Originally Released Under LGPL - original licence link has changed is not relivant.
17398  *
17399  * Fork - LGPL
17400  * <script type="text/javascript">
17401  */
17402  
17403 /**
17404  * @class Roo.tree.TreeNode
17405  * @extends Roo.data.Node
17406  * @cfg {String} text The text for this node
17407  * @cfg {Boolean} expanded true to start the node expanded
17408  * @cfg {Boolean} allowDrag false to make this node undraggable if DD is on (defaults to true)
17409  * @cfg {Boolean} allowDrop false if this node cannot be drop on
17410  * @cfg {Boolean} disabled true to start the node disabled
17411  * @cfg {String} icon The path to an icon for the node. The preferred way to do this
17412  * is to use the cls or iconCls attributes and add the icon via a CSS background image.
17413  * @cfg {String} cls A css class to be added to the node
17414  * @cfg {String} iconCls A css class to be added to the nodes icon element for applying css background images
17415  * @cfg {String} href URL of the link used for the node (defaults to #)
17416  * @cfg {String} hrefTarget target frame for the link
17417  * @cfg {String} qtip An Ext QuickTip for the node
17418  * @cfg {String} qtipCfg An Ext QuickTip config for the node (used instead of qtip)
17419  * @cfg {Boolean} singleClickExpand True for single click expand on this node
17420  * @cfg {Function} uiProvider A UI <b>class</b> to use for this node (defaults to Roo.tree.TreeNodeUI)
17421  * @cfg {Boolean} checked True to render a checked checkbox for this node, false to render an unchecked checkbox
17422  * (defaults to undefined with no checkbox rendered)
17423  * @constructor
17424  * @param {Object/String} attributes The attributes/config for the node or just a string with the text for the node
17425  */
17426 Roo.tree.TreeNode = function(attributes){
17427     attributes = attributes || {};
17428     if(typeof attributes == "string"){
17429         attributes = {text: attributes};
17430     }
17431     this.childrenRendered = false;
17432     this.rendered = false;
17433     Roo.tree.TreeNode.superclass.constructor.call(this, attributes);
17434     this.expanded = attributes.expanded === true;
17435     this.isTarget = attributes.isTarget !== false;
17436     this.draggable = attributes.draggable !== false && attributes.allowDrag !== false;
17437     this.allowChildren = attributes.allowChildren !== false && attributes.allowDrop !== false;
17438
17439     /**
17440      * Read-only. The text for this node. To change it use setText().
17441      * @type String
17442      */
17443     this.text = attributes.text;
17444     /**
17445      * True if this node is disabled.
17446      * @type Boolean
17447      */
17448     this.disabled = attributes.disabled === true;
17449
17450     this.addEvents({
17451         /**
17452         * @event textchange
17453         * Fires when the text for this node is changed
17454         * @param {Node} this This node
17455         * @param {String} text The new text
17456         * @param {String} oldText The old text
17457         */
17458         "textchange" : true,
17459         /**
17460         * @event beforeexpand
17461         * Fires before this node is expanded, return false to cancel.
17462         * @param {Node} this This node
17463         * @param {Boolean} deep
17464         * @param {Boolean} anim
17465         */
17466         "beforeexpand" : true,
17467         /**
17468         * @event beforecollapse
17469         * Fires before this node is collapsed, return false to cancel.
17470         * @param {Node} this This node
17471         * @param {Boolean} deep
17472         * @param {Boolean} anim
17473         */
17474         "beforecollapse" : true,
17475         /**
17476         * @event expand
17477         * Fires when this node is expanded
17478         * @param {Node} this This node
17479         */
17480         "expand" : true,
17481         /**
17482         * @event disabledchange
17483         * Fires when the disabled status of this node changes
17484         * @param {Node} this This node
17485         * @param {Boolean} disabled
17486         */
17487         "disabledchange" : true,
17488         /**
17489         * @event collapse
17490         * Fires when this node is collapsed
17491         * @param {Node} this This node
17492         */
17493         "collapse" : true,
17494         /**
17495         * @event beforeclick
17496         * Fires before click processing. Return false to cancel the default action.
17497         * @param {Node} this This node
17498         * @param {Roo.EventObject} e The event object
17499         */
17500         "beforeclick":true,
17501         /**
17502         * @event checkchange
17503         * Fires when a node with a checkbox's checked property changes
17504         * @param {Node} this This node
17505         * @param {Boolean} checked
17506         */
17507         "checkchange":true,
17508         /**
17509         * @event click
17510         * Fires when this node is clicked
17511         * @param {Node} this This node
17512         * @param {Roo.EventObject} e The event object
17513         */
17514         "click":true,
17515         /**
17516         * @event dblclick
17517         * Fires when this node is double clicked
17518         * @param {Node} this This node
17519         * @param {Roo.EventObject} e The event object
17520         */
17521         "dblclick":true,
17522         /**
17523         * @event contextmenu
17524         * Fires when this node is right clicked
17525         * @param {Node} this This node
17526         * @param {Roo.EventObject} e The event object
17527         */
17528         "contextmenu":true,
17529         /**
17530         * @event beforechildrenrendered
17531         * Fires right before the child nodes for this node are rendered
17532         * @param {Node} this This node
17533         */
17534         "beforechildrenrendered":true
17535     });
17536
17537     var uiClass = this.attributes.uiProvider || Roo.tree.TreeNodeUI;
17538
17539     /**
17540      * Read-only. The UI for this node
17541      * @type TreeNodeUI
17542      */
17543     this.ui = new uiClass(this);
17544     
17545     // finally support items[]
17546     if (typeof(this.attributes.items) == 'undefined' || !this.attributes.items) {
17547         return;
17548     }
17549     
17550     
17551     Roo.each(this.attributes.items, function(c) {
17552         this.appendChild(Roo.factory(c,Roo.Tree));
17553     }, this);
17554     delete this.attributes.items;
17555     
17556     
17557     
17558 };
17559 Roo.extend(Roo.tree.TreeNode, Roo.data.Node, {
17560     preventHScroll: true,
17561     /**
17562      * Returns true if this node is expanded
17563      * @return {Boolean}
17564      */
17565     isExpanded : function(){
17566         return this.expanded;
17567     },
17568
17569     /**
17570      * Returns the UI object for this node
17571      * @return {TreeNodeUI}
17572      */
17573     getUI : function(){
17574         return this.ui;
17575     },
17576
17577     // private override
17578     setFirstChild : function(node){
17579         var of = this.firstChild;
17580         Roo.tree.TreeNode.superclass.setFirstChild.call(this, node);
17581         if(this.childrenRendered && of && node != of){
17582             of.renderIndent(true, true);
17583         }
17584         if(this.rendered){
17585             this.renderIndent(true, true);
17586         }
17587     },
17588
17589     // private override
17590     setLastChild : function(node){
17591         var ol = this.lastChild;
17592         Roo.tree.TreeNode.superclass.setLastChild.call(this, node);
17593         if(this.childrenRendered && ol && node != ol){
17594             ol.renderIndent(true, true);
17595         }
17596         if(this.rendered){
17597             this.renderIndent(true, true);
17598         }
17599     },
17600
17601     // these methods are overridden to provide lazy rendering support
17602     // private override
17603     appendChild : function()
17604     {
17605         var node = Roo.tree.TreeNode.superclass.appendChild.apply(this, arguments);
17606         if(node && this.childrenRendered){
17607             node.render();
17608         }
17609         this.ui.updateExpandIcon();
17610         return node;
17611     },
17612
17613     // private override
17614     removeChild : function(node){
17615         this.ownerTree.getSelectionModel().unselect(node);
17616         Roo.tree.TreeNode.superclass.removeChild.apply(this, arguments);
17617         // if it's been rendered remove dom node
17618         if(this.childrenRendered){
17619             node.ui.remove();
17620         }
17621         if(this.childNodes.length < 1){
17622             this.collapse(false, false);
17623         }else{
17624             this.ui.updateExpandIcon();
17625         }
17626         if(!this.firstChild) {
17627             this.childrenRendered = false;
17628         }
17629         return node;
17630     },
17631
17632     // private override
17633     insertBefore : function(node, refNode){
17634         var newNode = Roo.tree.TreeNode.superclass.insertBefore.apply(this, arguments);
17635         if(newNode && refNode && this.childrenRendered){
17636             node.render();
17637         }
17638         this.ui.updateExpandIcon();
17639         return newNode;
17640     },
17641
17642     /**
17643      * Sets the text for this node
17644      * @param {String} text
17645      */
17646     setText : function(text){
17647         var oldText = this.text;
17648         this.text = text;
17649         this.attributes.text = text;
17650         if(this.rendered){ // event without subscribing
17651             this.ui.onTextChange(this, text, oldText);
17652         }
17653         this.fireEvent("textchange", this, text, oldText);
17654     },
17655
17656     /**
17657      * Triggers selection of this node
17658      */
17659     select : function(){
17660         this.getOwnerTree().getSelectionModel().select(this);
17661     },
17662
17663     /**
17664      * Triggers deselection of this node
17665      */
17666     unselect : function(){
17667         this.getOwnerTree().getSelectionModel().unselect(this);
17668     },
17669
17670     /**
17671      * Returns true if this node is selected
17672      * @return {Boolean}
17673      */
17674     isSelected : function(){
17675         return this.getOwnerTree().getSelectionModel().isSelected(this);
17676     },
17677
17678     /**
17679      * Expand this node.
17680      * @param {Boolean} deep (optional) True to expand all children as well
17681      * @param {Boolean} anim (optional) false to cancel the default animation
17682      * @param {Function} callback (optional) A callback to be called when
17683      * expanding this node completes (does not wait for deep expand to complete).
17684      * Called with 1 parameter, this node.
17685      */
17686     expand : function(deep, anim, callback){
17687         if(!this.expanded){
17688             if(this.fireEvent("beforeexpand", this, deep, anim) === false){
17689                 return;
17690             }
17691             if(!this.childrenRendered){
17692                 this.renderChildren();
17693             }
17694             this.expanded = true;
17695             if(!this.isHiddenRoot() && (this.getOwnerTree().animate && anim !== false) || anim){
17696                 this.ui.animExpand(function(){
17697                     this.fireEvent("expand", this);
17698                     if(typeof callback == "function"){
17699                         callback(this);
17700                     }
17701                     if(deep === true){
17702                         this.expandChildNodes(true);
17703                     }
17704                 }.createDelegate(this));
17705                 return;
17706             }else{
17707                 this.ui.expand();
17708                 this.fireEvent("expand", this);
17709                 if(typeof callback == "function"){
17710                     callback(this);
17711                 }
17712             }
17713         }else{
17714            if(typeof callback == "function"){
17715                callback(this);
17716            }
17717         }
17718         if(deep === true){
17719             this.expandChildNodes(true);
17720         }
17721     },
17722
17723     isHiddenRoot : function(){
17724         return this.isRoot && !this.getOwnerTree().rootVisible;
17725     },
17726
17727     /**
17728      * Collapse this node.
17729      * @param {Boolean} deep (optional) True to collapse all children as well
17730      * @param {Boolean} anim (optional) false to cancel the default animation
17731      */
17732     collapse : function(deep, anim){
17733         if(this.expanded && !this.isHiddenRoot()){
17734             if(this.fireEvent("beforecollapse", this, deep, anim) === false){
17735                 return;
17736             }
17737             this.expanded = false;
17738             if((this.getOwnerTree().animate && anim !== false) || anim){
17739                 this.ui.animCollapse(function(){
17740                     this.fireEvent("collapse", this);
17741                     if(deep === true){
17742                         this.collapseChildNodes(true);
17743                     }
17744                 }.createDelegate(this));
17745                 return;
17746             }else{
17747                 this.ui.collapse();
17748                 this.fireEvent("collapse", this);
17749             }
17750         }
17751         if(deep === true){
17752             var cs = this.childNodes;
17753             for(var i = 0, len = cs.length; i < len; i++) {
17754                 cs[i].collapse(true, false);
17755             }
17756         }
17757     },
17758
17759     // private
17760     delayedExpand : function(delay){
17761         if(!this.expandProcId){
17762             this.expandProcId = this.expand.defer(delay, this);
17763         }
17764     },
17765
17766     // private
17767     cancelExpand : function(){
17768         if(this.expandProcId){
17769             clearTimeout(this.expandProcId);
17770         }
17771         this.expandProcId = false;
17772     },
17773
17774     /**
17775      * Toggles expanded/collapsed state of the node
17776      */
17777     toggle : function(){
17778         if(this.expanded){
17779             this.collapse();
17780         }else{
17781             this.expand();
17782         }
17783     },
17784
17785     /**
17786      * Ensures all parent nodes are expanded
17787      */
17788     ensureVisible : function(callback){
17789         var tree = this.getOwnerTree();
17790         tree.expandPath(this.parentNode.getPath(), false, function(){
17791             tree.getTreeEl().scrollChildIntoView(this.ui.anchor);
17792             Roo.callback(callback);
17793         }.createDelegate(this));
17794     },
17795
17796     /**
17797      * Expand all child nodes
17798      * @param {Boolean} deep (optional) true if the child nodes should also expand their child nodes
17799      */
17800     expandChildNodes : function(deep){
17801         var cs = this.childNodes;
17802         for(var i = 0, len = cs.length; i < len; i++) {
17803                 cs[i].expand(deep);
17804         }
17805     },
17806
17807     /**
17808      * Collapse all child nodes
17809      * @param {Boolean} deep (optional) true if the child nodes should also collapse their child nodes
17810      */
17811     collapseChildNodes : function(deep){
17812         var cs = this.childNodes;
17813         for(var i = 0, len = cs.length; i < len; i++) {
17814                 cs[i].collapse(deep);
17815         }
17816     },
17817
17818     /**
17819      * Disables this node
17820      */
17821     disable : function(){
17822         this.disabled = true;
17823         this.unselect();
17824         if(this.rendered && this.ui.onDisableChange){ // event without subscribing
17825             this.ui.onDisableChange(this, true);
17826         }
17827         this.fireEvent("disabledchange", this, true);
17828     },
17829
17830     /**
17831      * Enables this node
17832      */
17833     enable : function(){
17834         this.disabled = false;
17835         if(this.rendered && this.ui.onDisableChange){ // event without subscribing
17836             this.ui.onDisableChange(this, false);
17837         }
17838         this.fireEvent("disabledchange", this, false);
17839     },
17840
17841     // private
17842     renderChildren : function(suppressEvent){
17843         if(suppressEvent !== false){
17844             this.fireEvent("beforechildrenrendered", this);
17845         }
17846         var cs = this.childNodes;
17847         for(var i = 0, len = cs.length; i < len; i++){
17848             cs[i].render(true);
17849         }
17850         this.childrenRendered = true;
17851     },
17852
17853     // private
17854     sort : function(fn, scope){
17855         Roo.tree.TreeNode.superclass.sort.apply(this, arguments);
17856         if(this.childrenRendered){
17857             var cs = this.childNodes;
17858             for(var i = 0, len = cs.length; i < len; i++){
17859                 cs[i].render(true);
17860             }
17861         }
17862     },
17863
17864     // private
17865     render : function(bulkRender){
17866         this.ui.render(bulkRender);
17867         if(!this.rendered){
17868             this.rendered = true;
17869             if(this.expanded){
17870                 this.expanded = false;
17871                 this.expand(false, false);
17872             }
17873         }
17874     },
17875
17876     // private
17877     renderIndent : function(deep, refresh){
17878         if(refresh){
17879             this.ui.childIndent = null;
17880         }
17881         this.ui.renderIndent();
17882         if(deep === true && this.childrenRendered){
17883             var cs = this.childNodes;
17884             for(var i = 0, len = cs.length; i < len; i++){
17885                 cs[i].renderIndent(true, refresh);
17886             }
17887         }
17888     }
17889 });/*
17890  * Based on:
17891  * Ext JS Library 1.1.1
17892  * Copyright(c) 2006-2007, Ext JS, LLC.
17893  *
17894  * Originally Released Under LGPL - original licence link has changed is not relivant.
17895  *
17896  * Fork - LGPL
17897  * <script type="text/javascript">
17898  */
17899  
17900 /**
17901  * @class Roo.tree.AsyncTreeNode
17902  * @extends Roo.tree.TreeNode
17903  * @cfg {TreeLoader} loader A TreeLoader to be used by this node (defaults to the loader defined on the tree)
17904  * @constructor
17905  * @param {Object/String} attributes The attributes/config for the node or just a string with the text for the node 
17906  */
17907  Roo.tree.AsyncTreeNode = function(config){
17908     this.loaded = false;
17909     this.loading = false;
17910     Roo.tree.AsyncTreeNode.superclass.constructor.apply(this, arguments);
17911     /**
17912     * @event beforeload
17913     * Fires before this node is loaded, return false to cancel
17914     * @param {Node} this This node
17915     */
17916     this.addEvents({'beforeload':true, 'load': true});
17917     /**
17918     * @event load
17919     * Fires when this node is loaded
17920     * @param {Node} this This node
17921     */
17922     /**
17923      * The loader used by this node (defaults to using the tree's defined loader)
17924      * @type TreeLoader
17925      * @property loader
17926      */
17927 };
17928 Roo.extend(Roo.tree.AsyncTreeNode, Roo.tree.TreeNode, {
17929     expand : function(deep, anim, callback){
17930         if(this.loading){ // if an async load is already running, waiting til it's done
17931             var timer;
17932             var f = function(){
17933                 if(!this.loading){ // done loading
17934                     clearInterval(timer);
17935                     this.expand(deep, anim, callback);
17936                 }
17937             }.createDelegate(this);
17938             timer = setInterval(f, 200);
17939             return;
17940         }
17941         if(!this.loaded){
17942             if(this.fireEvent("beforeload", this) === false){
17943                 return;
17944             }
17945             this.loading = true;
17946             this.ui.beforeLoad(this);
17947             var loader = this.loader || this.attributes.loader || this.getOwnerTree().getLoader();
17948             if(loader){
17949                 loader.load(this, this.loadComplete.createDelegate(this, [deep, anim, callback]));
17950                 return;
17951             }
17952         }
17953         Roo.tree.AsyncTreeNode.superclass.expand.call(this, deep, anim, callback);
17954     },
17955     
17956     /**
17957      * Returns true if this node is currently loading
17958      * @return {Boolean}
17959      */
17960     isLoading : function(){
17961         return this.loading;  
17962     },
17963     
17964     loadComplete : function(deep, anim, callback){
17965         this.loading = false;
17966         this.loaded = true;
17967         this.ui.afterLoad(this);
17968         this.fireEvent("load", this);
17969         this.expand(deep, anim, callback);
17970     },
17971     
17972     /**
17973      * Returns true if this node has been loaded
17974      * @return {Boolean}
17975      */
17976     isLoaded : function(){
17977         return this.loaded;
17978     },
17979     
17980     hasChildNodes : function(){
17981         if(!this.isLeaf() && !this.loaded){
17982             return true;
17983         }else{
17984             return Roo.tree.AsyncTreeNode.superclass.hasChildNodes.call(this);
17985         }
17986     },
17987
17988     /**
17989      * Trigger a reload for this node
17990      * @param {Function} callback
17991      */
17992     reload : function(callback){
17993         this.collapse(false, false);
17994         while(this.firstChild){
17995             this.removeChild(this.firstChild);
17996         }
17997         this.childrenRendered = false;
17998         this.loaded = false;
17999         if(this.isHiddenRoot()){
18000             this.expanded = false;
18001         }
18002         this.expand(false, false, callback);
18003     }
18004 });/*
18005  * Based on:
18006  * Ext JS Library 1.1.1
18007  * Copyright(c) 2006-2007, Ext JS, LLC.
18008  *
18009  * Originally Released Under LGPL - original licence link has changed is not relivant.
18010  *
18011  * Fork - LGPL
18012  * <script type="text/javascript">
18013  */
18014  
18015 /**
18016  * @class Roo.tree.TreeNodeUI
18017  * @constructor
18018  * @param {Object} node The node to render
18019  * The TreeNode UI implementation is separate from the
18020  * tree implementation. Unless you are customizing the tree UI,
18021  * you should never have to use this directly.
18022  */
18023 Roo.tree.TreeNodeUI = function(node){
18024     this.node = node;
18025     this.rendered = false;
18026     this.animating = false;
18027     this.emptyIcon = Roo.BLANK_IMAGE_URL;
18028 };
18029
18030 Roo.tree.TreeNodeUI.prototype = {
18031     removeChild : function(node){
18032         if(this.rendered){
18033             this.ctNode.removeChild(node.ui.getEl());
18034         }
18035     },
18036
18037     beforeLoad : function(){
18038          this.addClass("x-tree-node-loading");
18039     },
18040
18041     afterLoad : function(){
18042          this.removeClass("x-tree-node-loading");
18043     },
18044
18045     onTextChange : function(node, text, oldText){
18046         if(this.rendered){
18047             this.textNode.innerHTML = text;
18048         }
18049     },
18050
18051     onDisableChange : function(node, state){
18052         this.disabled = state;
18053         if(state){
18054             this.addClass("x-tree-node-disabled");
18055         }else{
18056             this.removeClass("x-tree-node-disabled");
18057         }
18058     },
18059
18060     onSelectedChange : function(state){
18061         if(state){
18062             this.focus();
18063             this.addClass("x-tree-selected");
18064         }else{
18065             //this.blur();
18066             this.removeClass("x-tree-selected");
18067         }
18068     },
18069
18070     onMove : function(tree, node, oldParent, newParent, index, refNode){
18071         this.childIndent = null;
18072         if(this.rendered){
18073             var targetNode = newParent.ui.getContainer();
18074             if(!targetNode){//target not rendered
18075                 this.holder = document.createElement("div");
18076                 this.holder.appendChild(this.wrap);
18077                 return;
18078             }
18079             var insertBefore = refNode ? refNode.ui.getEl() : null;
18080             if(insertBefore){
18081                 targetNode.insertBefore(this.wrap, insertBefore);
18082             }else{
18083                 targetNode.appendChild(this.wrap);
18084             }
18085             this.node.renderIndent(true);
18086         }
18087     },
18088
18089     addClass : function(cls){
18090         if(this.elNode){
18091             Roo.fly(this.elNode).addClass(cls);
18092         }
18093     },
18094
18095     removeClass : function(cls){
18096         if(this.elNode){
18097             Roo.fly(this.elNode).removeClass(cls);
18098         }
18099     },
18100
18101     remove : function(){
18102         if(this.rendered){
18103             this.holder = document.createElement("div");
18104             this.holder.appendChild(this.wrap);
18105         }
18106     },
18107
18108     fireEvent : function(){
18109         return this.node.fireEvent.apply(this.node, arguments);
18110     },
18111
18112     initEvents : function(){
18113         this.node.on("move", this.onMove, this);
18114         var E = Roo.EventManager;
18115         var a = this.anchor;
18116
18117         var el = Roo.fly(a, '_treeui');
18118
18119         if(Roo.isOpera){ // opera render bug ignores the CSS
18120             el.setStyle("text-decoration", "none");
18121         }
18122
18123         el.on("click", this.onClick, this);
18124         el.on("dblclick", this.onDblClick, this);
18125
18126         if(this.checkbox){
18127             Roo.EventManager.on(this.checkbox,
18128                     Roo.isIE ? 'click' : 'change', this.onCheckChange, this);
18129         }
18130
18131         el.on("contextmenu", this.onContextMenu, this);
18132
18133         var icon = Roo.fly(this.iconNode);
18134         icon.on("click", this.onClick, this);
18135         icon.on("dblclick", this.onDblClick, this);
18136         icon.on("contextmenu", this.onContextMenu, this);
18137         E.on(this.ecNode, "click", this.ecClick, this, true);
18138
18139         if(this.node.disabled){
18140             this.addClass("x-tree-node-disabled");
18141         }
18142         if(this.node.hidden){
18143             this.addClass("x-tree-node-disabled");
18144         }
18145         var ot = this.node.getOwnerTree();
18146         var dd = ot.enableDD || ot.enableDrag || ot.enableDrop;
18147         if(dd && (!this.node.isRoot || ot.rootVisible)){
18148             Roo.dd.Registry.register(this.elNode, {
18149                 node: this.node,
18150                 handles: this.getDDHandles(),
18151                 isHandle: false
18152             });
18153         }
18154     },
18155
18156     getDDHandles : function(){
18157         return [this.iconNode, this.textNode];
18158     },
18159
18160     hide : function(){
18161         if(this.rendered){
18162             this.wrap.style.display = "none";
18163         }
18164     },
18165
18166     show : function(){
18167         if(this.rendered){
18168             this.wrap.style.display = "";
18169         }
18170     },
18171
18172     onContextMenu : function(e){
18173         if (this.node.hasListener("contextmenu") || this.node.getOwnerTree().hasListener("contextmenu")) {
18174             e.preventDefault();
18175             this.focus();
18176             this.fireEvent("contextmenu", this.node, e);
18177         }
18178     },
18179
18180     onClick : function(e){
18181         if(this.dropping){
18182             e.stopEvent();
18183             return;
18184         }
18185         if(this.fireEvent("beforeclick", this.node, e) !== false){
18186             if(!this.disabled && this.node.attributes.href){
18187                 this.fireEvent("click", this.node, e);
18188                 return;
18189             }
18190             e.preventDefault();
18191             if(this.disabled){
18192                 return;
18193             }
18194
18195             if(this.node.attributes.singleClickExpand && !this.animating && this.node.hasChildNodes()){
18196                 this.node.toggle();
18197             }
18198
18199             this.fireEvent("click", this.node, e);
18200         }else{
18201             e.stopEvent();
18202         }
18203     },
18204
18205     onDblClick : function(e){
18206         e.preventDefault();
18207         if(this.disabled){
18208             return;
18209         }
18210         if(this.checkbox){
18211             this.toggleCheck();
18212         }
18213         if(!this.animating && this.node.hasChildNodes()){
18214             this.node.toggle();
18215         }
18216         this.fireEvent("dblclick", this.node, e);
18217     },
18218
18219     onCheckChange : function(){
18220         var checked = this.checkbox.checked;
18221         this.node.attributes.checked = checked;
18222         this.fireEvent('checkchange', this.node, checked);
18223     },
18224
18225     ecClick : function(e){
18226         if(!this.animating && this.node.hasChildNodes()){
18227             this.node.toggle();
18228         }
18229     },
18230
18231     startDrop : function(){
18232         this.dropping = true;
18233     },
18234
18235     // delayed drop so the click event doesn't get fired on a drop
18236     endDrop : function(){
18237        setTimeout(function(){
18238            this.dropping = false;
18239        }.createDelegate(this), 50);
18240     },
18241
18242     expand : function(){
18243         this.updateExpandIcon();
18244         this.ctNode.style.display = "";
18245     },
18246
18247     focus : function(){
18248         if(!this.node.preventHScroll){
18249             try{this.anchor.focus();
18250             }catch(e){}
18251         }else if(!Roo.isIE){
18252             try{
18253                 var noscroll = this.node.getOwnerTree().getTreeEl().dom;
18254                 var l = noscroll.scrollLeft;
18255                 this.anchor.focus();
18256                 noscroll.scrollLeft = l;
18257             }catch(e){}
18258         }
18259     },
18260
18261     toggleCheck : function(value){
18262         var cb = this.checkbox;
18263         if(cb){
18264             cb.checked = (value === undefined ? !cb.checked : value);
18265         }
18266     },
18267
18268     blur : function(){
18269         try{
18270             this.anchor.blur();
18271         }catch(e){}
18272     },
18273
18274     animExpand : function(callback){
18275         var ct = Roo.get(this.ctNode);
18276         ct.stopFx();
18277         if(!this.node.hasChildNodes()){
18278             this.updateExpandIcon();
18279             this.ctNode.style.display = "";
18280             Roo.callback(callback);
18281             return;
18282         }
18283         this.animating = true;
18284         this.updateExpandIcon();
18285
18286         ct.slideIn('t', {
18287            callback : function(){
18288                this.animating = false;
18289                Roo.callback(callback);
18290             },
18291             scope: this,
18292             duration: this.node.ownerTree.duration || .25
18293         });
18294     },
18295
18296     highlight : function(){
18297         var tree = this.node.getOwnerTree();
18298         Roo.fly(this.wrap).highlight(
18299             tree.hlColor || "C3DAF9",
18300             {endColor: tree.hlBaseColor}
18301         );
18302     },
18303
18304     collapse : function(){
18305         this.updateExpandIcon();
18306         this.ctNode.style.display = "none";
18307     },
18308
18309     animCollapse : function(callback){
18310         var ct = Roo.get(this.ctNode);
18311         ct.enableDisplayMode('block');
18312         ct.stopFx();
18313
18314         this.animating = true;
18315         this.updateExpandIcon();
18316
18317         ct.slideOut('t', {
18318             callback : function(){
18319                this.animating = false;
18320                Roo.callback(callback);
18321             },
18322             scope: this,
18323             duration: this.node.ownerTree.duration || .25
18324         });
18325     },
18326
18327     getContainer : function(){
18328         return this.ctNode;
18329     },
18330
18331     getEl : function(){
18332         return this.wrap;
18333     },
18334
18335     appendDDGhost : function(ghostNode){
18336         ghostNode.appendChild(this.elNode.cloneNode(true));
18337     },
18338
18339     getDDRepairXY : function(){
18340         return Roo.lib.Dom.getXY(this.iconNode);
18341     },
18342
18343     onRender : function(){
18344         this.render();
18345     },
18346
18347     render : function(bulkRender){
18348         var n = this.node, a = n.attributes;
18349         var targetNode = n.parentNode ?
18350               n.parentNode.ui.getContainer() : n.ownerTree.innerCt.dom;
18351
18352         if(!this.rendered){
18353             this.rendered = true;
18354
18355             this.renderElements(n, a, targetNode, bulkRender);
18356
18357             if(a.qtip){
18358                if(this.textNode.setAttributeNS){
18359                    this.textNode.setAttributeNS("ext", "qtip", a.qtip);
18360                    if(a.qtipTitle){
18361                        this.textNode.setAttributeNS("ext", "qtitle", a.qtipTitle);
18362                    }
18363                }else{
18364                    this.textNode.setAttribute("ext:qtip", a.qtip);
18365                    if(a.qtipTitle){
18366                        this.textNode.setAttribute("ext:qtitle", a.qtipTitle);
18367                    }
18368                }
18369             }else if(a.qtipCfg){
18370                 a.qtipCfg.target = Roo.id(this.textNode);
18371                 Roo.QuickTips.register(a.qtipCfg);
18372             }
18373             this.initEvents();
18374             if(!this.node.expanded){
18375                 this.updateExpandIcon();
18376             }
18377         }else{
18378             if(bulkRender === true) {
18379                 targetNode.appendChild(this.wrap);
18380             }
18381         }
18382     },
18383
18384     renderElements : function(n, a, targetNode, bulkRender)
18385     {
18386         // add some indent caching, this helps performance when rendering a large tree
18387         this.indentMarkup = n.parentNode ? n.parentNode.ui.getChildIndent() : '';
18388         var t = n.getOwnerTree();
18389         var txt = t.renderer ? t.renderer(n.attributes) : Roo.util.Format.htmlEncode(n.text);
18390         if (typeof(n.attributes.html) != 'undefined') {
18391             txt = n.attributes.html;
18392         }
18393         var tip = t.rendererTip ? t.rendererTip(n.attributes) : txt;
18394         var cb = typeof a.checked == 'boolean';
18395         var href = a.href ? a.href : Roo.isGecko ? "" : "#";
18396         var buf = ['<li class="x-tree-node"><div class="x-tree-node-el ', a.cls,'">',
18397             '<span class="x-tree-node-indent">',this.indentMarkup,"</span>",
18398             '<img src="', this.emptyIcon, '" class="x-tree-ec-icon" />',
18399             '<img src="', a.icon || this.emptyIcon, '" class="x-tree-node-icon',(a.icon ? " x-tree-node-inline-icon" : ""),(a.iconCls ? " "+a.iconCls : ""),'" unselectable="on" />',
18400             cb ? ('<input class="x-tree-node-cb" type="checkbox" ' + (a.checked ? 'checked="checked" />' : ' />')) : '',
18401             '<a hidefocus="on" href="',href,'" tabIndex="1" ',
18402              a.hrefTarget ? ' target="'+a.hrefTarget+'"' : "", 
18403                 '><span unselectable="on" qtip="' , tip ,'">',txt,"</span></a></div>",
18404             '<ul class="x-tree-node-ct" style="display:none;"></ul>',
18405             "</li>"];
18406
18407         if(bulkRender !== true && n.nextSibling && n.nextSibling.ui.getEl()){
18408             this.wrap = Roo.DomHelper.insertHtml("beforeBegin",
18409                                 n.nextSibling.ui.getEl(), buf.join(""));
18410         }else{
18411             this.wrap = Roo.DomHelper.insertHtml("beforeEnd", targetNode, buf.join(""));
18412         }
18413
18414         this.elNode = this.wrap.childNodes[0];
18415         this.ctNode = this.wrap.childNodes[1];
18416         var cs = this.elNode.childNodes;
18417         this.indentNode = cs[0];
18418         this.ecNode = cs[1];
18419         this.iconNode = cs[2];
18420         var index = 3;
18421         if(cb){
18422             this.checkbox = cs[3];
18423             index++;
18424         }
18425         this.anchor = cs[index];
18426         this.textNode = cs[index].firstChild;
18427     },
18428
18429     getAnchor : function(){
18430         return this.anchor;
18431     },
18432
18433     getTextEl : function(){
18434         return this.textNode;
18435     },
18436
18437     getIconEl : function(){
18438         return this.iconNode;
18439     },
18440
18441     isChecked : function(){
18442         return this.checkbox ? this.checkbox.checked : false;
18443     },
18444
18445     updateExpandIcon : function(){
18446         if(this.rendered){
18447             var n = this.node, c1, c2;
18448             var cls = n.isLast() ? "x-tree-elbow-end" : "x-tree-elbow";
18449             var hasChild = n.hasChildNodes();
18450             if(hasChild){
18451                 if(n.expanded){
18452                     cls += "-minus";
18453                     c1 = "x-tree-node-collapsed";
18454                     c2 = "x-tree-node-expanded";
18455                 }else{
18456                     cls += "-plus";
18457                     c1 = "x-tree-node-expanded";
18458                     c2 = "x-tree-node-collapsed";
18459                 }
18460                 if(this.wasLeaf){
18461                     this.removeClass("x-tree-node-leaf");
18462                     this.wasLeaf = false;
18463                 }
18464                 if(this.c1 != c1 || this.c2 != c2){
18465                     Roo.fly(this.elNode).replaceClass(c1, c2);
18466                     this.c1 = c1; this.c2 = c2;
18467                 }
18468             }else{
18469                 // this changes non-leafs into leafs if they have no children.
18470                 // it's not very rational behaviour..
18471                 
18472                 if(!this.wasLeaf && this.node.leaf){
18473                     Roo.fly(this.elNode).replaceClass("x-tree-node-expanded", "x-tree-node-leaf");
18474                     delete this.c1;
18475                     delete this.c2;
18476                     this.wasLeaf = true;
18477                 }
18478             }
18479             var ecc = "x-tree-ec-icon "+cls;
18480             if(this.ecc != ecc){
18481                 this.ecNode.className = ecc;
18482                 this.ecc = ecc;
18483             }
18484         }
18485     },
18486
18487     getChildIndent : function(){
18488         if(!this.childIndent){
18489             var buf = [];
18490             var p = this.node;
18491             while(p){
18492                 if(!p.isRoot || (p.isRoot && p.ownerTree.rootVisible)){
18493                     if(!p.isLast()) {
18494                         buf.unshift('<img src="'+this.emptyIcon+'" class="x-tree-elbow-line" />');
18495                     } else {
18496                         buf.unshift('<img src="'+this.emptyIcon+'" class="x-tree-icon" />');
18497                     }
18498                 }
18499                 p = p.parentNode;
18500             }
18501             this.childIndent = buf.join("");
18502         }
18503         return this.childIndent;
18504     },
18505
18506     renderIndent : function(){
18507         if(this.rendered){
18508             var indent = "";
18509             var p = this.node.parentNode;
18510             if(p){
18511                 indent = p.ui.getChildIndent();
18512             }
18513             if(this.indentMarkup != indent){ // don't rerender if not required
18514                 this.indentNode.innerHTML = indent;
18515                 this.indentMarkup = indent;
18516             }
18517             this.updateExpandIcon();
18518         }
18519     }
18520 };
18521
18522 Roo.tree.RootTreeNodeUI = function(){
18523     Roo.tree.RootTreeNodeUI.superclass.constructor.apply(this, arguments);
18524 };
18525 Roo.extend(Roo.tree.RootTreeNodeUI, Roo.tree.TreeNodeUI, {
18526     render : function(){
18527         if(!this.rendered){
18528             var targetNode = this.node.ownerTree.innerCt.dom;
18529             this.node.expanded = true;
18530             targetNode.innerHTML = '<div class="x-tree-root-node"></div>';
18531             this.wrap = this.ctNode = targetNode.firstChild;
18532         }
18533     },
18534     collapse : function(){
18535     },
18536     expand : function(){
18537     }
18538 });/*
18539  * Based on:
18540  * Ext JS Library 1.1.1
18541  * Copyright(c) 2006-2007, Ext JS, LLC.
18542  *
18543  * Originally Released Under LGPL - original licence link has changed is not relivant.
18544  *
18545  * Fork - LGPL
18546  * <script type="text/javascript">
18547  */
18548 /**
18549  * @class Roo.tree.TreeLoader
18550  * @extends Roo.util.Observable
18551  * A TreeLoader provides for lazy loading of an {@link Roo.tree.TreeNode}'s child
18552  * nodes from a specified URL. The response must be a javascript Array definition
18553  * who's elements are node definition objects. eg:
18554  * <pre><code>
18555 {  success : true,
18556    data :      [
18557    
18558     { 'id': 1, 'text': 'A folder Node', 'leaf': false },
18559     { 'id': 2, 'text': 'A leaf Node', 'leaf': true }
18560     ]
18561 }
18562
18563
18564 </code></pre>
18565  * <br><br>
18566  * The old style respose with just an array is still supported, but not recommended.
18567  * <br><br>
18568  *
18569  * A server request is sent, and child nodes are loaded only when a node is expanded.
18570  * The loading node's id is passed to the server under the parameter name "node" to
18571  * enable the server to produce the correct child nodes.
18572  * <br><br>
18573  * To pass extra parameters, an event handler may be attached to the "beforeload"
18574  * event, and the parameters specified in the TreeLoader's baseParams property:
18575  * <pre><code>
18576     myTreeLoader.on("beforeload", function(treeLoader, node) {
18577         this.baseParams.category = node.attributes.category;
18578     }, this);
18579 </code></pre><
18580  * This would pass an HTTP parameter called "category" to the server containing
18581  * the value of the Node's "category" attribute.
18582  * @constructor
18583  * Creates a new Treeloader.
18584  * @param {Object} config A config object containing config properties.
18585  */
18586 Roo.tree.TreeLoader = function(config){
18587     this.baseParams = {};
18588     this.requestMethod = "POST";
18589     Roo.apply(this, config);
18590
18591     this.addEvents({
18592     
18593         /**
18594          * @event beforeload
18595          * Fires before a network request is made to retrieve the Json text which specifies a node's children.
18596          * @param {Object} This TreeLoader object.
18597          * @param {Object} node The {@link Roo.tree.TreeNode} object being loaded.
18598          * @param {Object} callback The callback function specified in the {@link #load} call.
18599          */
18600         beforeload : true,
18601         /**
18602          * @event load
18603          * Fires when the node has been successfuly loaded.
18604          * @param {Object} This TreeLoader object.
18605          * @param {Object} node The {@link Roo.tree.TreeNode} object being loaded.
18606          * @param {Object} response The response object containing the data from the server.
18607          */
18608         load : true,
18609         /**
18610          * @event loadexception
18611          * Fires if the network request failed.
18612          * @param {Object} This TreeLoader object.
18613          * @param {Object} node The {@link Roo.tree.TreeNode} object being loaded.
18614          * @param {Object} response The response object containing the data from the server.
18615          */
18616         loadexception : true,
18617         /**
18618          * @event create
18619          * Fires before a node is created, enabling you to return custom Node types 
18620          * @param {Object} This TreeLoader object.
18621          * @param {Object} attr - the data returned from the AJAX call (modify it to suit)
18622          */
18623         create : true
18624     });
18625
18626     Roo.tree.TreeLoader.superclass.constructor.call(this);
18627 };
18628
18629 Roo.extend(Roo.tree.TreeLoader, Roo.util.Observable, {
18630     /**
18631     * @cfg {String} dataUrl The URL from which to request a Json string which
18632     * specifies an array of node definition object representing the child nodes
18633     * to be loaded.
18634     */
18635     /**
18636     * @cfg {String} requestMethod either GET or POST
18637     * defaults to POST (due to BC)
18638     * to be loaded.
18639     */
18640     /**
18641     * @cfg {Object} baseParams (optional) An object containing properties which
18642     * specify HTTP parameters to be passed to each request for child nodes.
18643     */
18644     /**
18645     * @cfg {Object} baseAttrs (optional) An object containing attributes to be added to all nodes
18646     * created by this loader. If the attributes sent by the server have an attribute in this object,
18647     * they take priority.
18648     */
18649     /**
18650     * @cfg {Object} uiProviders (optional) An object containing properties which
18651     * 
18652     * DEPRECATED - use 'create' event handler to modify attributes - which affect creation.
18653     * specify custom {@link Roo.tree.TreeNodeUI} implementations. If the optional
18654     * <i>uiProvider</i> attribute of a returned child node is a string rather
18655     * than a reference to a TreeNodeUI implementation, this that string value
18656     * is used as a property name in the uiProviders object. You can define the provider named
18657     * 'default' , and this will be used for all nodes (if no uiProvider is delivered by the node data)
18658     */
18659     uiProviders : {},
18660
18661     /**
18662     * @cfg {Boolean} clearOnLoad (optional) Default to true. Remove previously existing
18663     * child nodes before loading.
18664     */
18665     clearOnLoad : true,
18666
18667     /**
18668     * @cfg {String} root (optional) Default to false. Use this to read data from an object 
18669     * property on loading, rather than expecting an array. (eg. more compatible to a standard
18670     * Grid query { data : [ .....] }
18671     */
18672     
18673     root : false,
18674      /**
18675     * @cfg {String} queryParam (optional) 
18676     * Name of the query as it will be passed on the querystring (defaults to 'node')
18677     * eg. the request will be ?node=[id]
18678     */
18679     
18680     
18681     queryParam: false,
18682     
18683     /**
18684      * Load an {@link Roo.tree.TreeNode} from the URL specified in the constructor.
18685      * This is called automatically when a node is expanded, but may be used to reload
18686      * a node (or append new children if the {@link #clearOnLoad} option is false.)
18687      * @param {Roo.tree.TreeNode} node
18688      * @param {Function} callback
18689      */
18690     load : function(node, callback){
18691         if(this.clearOnLoad){
18692             while(node.firstChild){
18693                 node.removeChild(node.firstChild);
18694             }
18695         }
18696         if(node.attributes.children){ // preloaded json children
18697             var cs = node.attributes.children;
18698             for(var i = 0, len = cs.length; i < len; i++){
18699                 node.appendChild(this.createNode(cs[i]));
18700             }
18701             if(typeof callback == "function"){
18702                 callback();
18703             }
18704         }else if(this.dataUrl){
18705             this.requestData(node, callback);
18706         }
18707     },
18708
18709     getParams: function(node){
18710         var buf = [], bp = this.baseParams;
18711         for(var key in bp){
18712             if(typeof bp[key] != "function"){
18713                 buf.push(encodeURIComponent(key), "=", encodeURIComponent(bp[key]), "&");
18714             }
18715         }
18716         var n = this.queryParam === false ? 'node' : this.queryParam;
18717         buf.push(n + "=", encodeURIComponent(node.id));
18718         return buf.join("");
18719     },
18720
18721     requestData : function(node, callback){
18722         if(this.fireEvent("beforeload", this, node, callback) !== false){
18723             this.transId = Roo.Ajax.request({
18724                 method:this.requestMethod,
18725                 url: this.dataUrl||this.url,
18726                 success: this.handleResponse,
18727                 failure: this.handleFailure,
18728                 scope: this,
18729                 argument: {callback: callback, node: node},
18730                 params: this.getParams(node)
18731             });
18732         }else{
18733             // if the load is cancelled, make sure we notify
18734             // the node that we are done
18735             if(typeof callback == "function"){
18736                 callback();
18737             }
18738         }
18739     },
18740
18741     isLoading : function(){
18742         return this.transId ? true : false;
18743     },
18744
18745     abort : function(){
18746         if(this.isLoading()){
18747             Roo.Ajax.abort(this.transId);
18748         }
18749     },
18750
18751     // private
18752     createNode : function(attr)
18753     {
18754         // apply baseAttrs, nice idea Corey!
18755         if(this.baseAttrs){
18756             Roo.applyIf(attr, this.baseAttrs);
18757         }
18758         if(this.applyLoader !== false){
18759             attr.loader = this;
18760         }
18761         // uiProvider = depreciated..
18762         
18763         if(typeof(attr.uiProvider) == 'string'){
18764            attr.uiProvider = this.uiProviders[attr.uiProvider] || 
18765                 /**  eval:var:attr */ eval(attr.uiProvider);
18766         }
18767         if(typeof(this.uiProviders['default']) != 'undefined') {
18768             attr.uiProvider = this.uiProviders['default'];
18769         }
18770         
18771         this.fireEvent('create', this, attr);
18772         
18773         attr.leaf  = typeof(attr.leaf) == 'string' ? attr.leaf * 1 : attr.leaf;
18774         return(attr.leaf ?
18775                         new Roo.tree.TreeNode(attr) :
18776                         new Roo.tree.AsyncTreeNode(attr));
18777     },
18778
18779     processResponse : function(response, node, callback)
18780     {
18781         var json = response.responseText;
18782         try {
18783             
18784             var o = Roo.decode(json);
18785             
18786             if (this.root === false && typeof(o.success) != undefined) {
18787                 this.root = 'data'; // the default behaviour for list like data..
18788                 }
18789                 
18790             if (this.root !== false &&  !o.success) {
18791                 // it's a failure condition.
18792                 var a = response.argument;
18793                 this.fireEvent("loadexception", this, a.node, response);
18794                 Roo.log("Load failed - should have a handler really");
18795                 return;
18796             }
18797             
18798             
18799             
18800             if (this.root !== false) {
18801                  o = o[this.root];
18802             }
18803             
18804             for(var i = 0, len = o.length; i < len; i++){
18805                 var n = this.createNode(o[i]);
18806                 if(n){
18807                     node.appendChild(n);
18808                 }
18809             }
18810             if(typeof callback == "function"){
18811                 callback(this, node);
18812             }
18813         }catch(e){
18814             this.handleFailure(response);
18815         }
18816     },
18817
18818     handleResponse : function(response){
18819         this.transId = false;
18820         var a = response.argument;
18821         this.processResponse(response, a.node, a.callback);
18822         this.fireEvent("load", this, a.node, response);
18823     },
18824
18825     handleFailure : function(response)
18826     {
18827         // should handle failure better..
18828         this.transId = false;
18829         var a = response.argument;
18830         this.fireEvent("loadexception", this, a.node, response);
18831         if(typeof a.callback == "function"){
18832             a.callback(this, a.node);
18833         }
18834     }
18835 });/*
18836  * Based on:
18837  * Ext JS Library 1.1.1
18838  * Copyright(c) 2006-2007, Ext JS, LLC.
18839  *
18840  * Originally Released Under LGPL - original licence link has changed is not relivant.
18841  *
18842  * Fork - LGPL
18843  * <script type="text/javascript">
18844  */
18845
18846 /**
18847 * @class Roo.tree.TreeFilter
18848 * Note this class is experimental and doesn't update the indent (lines) or expand collapse icons of the nodes
18849 * @param {TreePanel} tree
18850 * @param {Object} config (optional)
18851  */
18852 Roo.tree.TreeFilter = function(tree, config){
18853     this.tree = tree;
18854     this.filtered = {};
18855     Roo.apply(this, config);
18856 };
18857
18858 Roo.tree.TreeFilter.prototype = {
18859     clearBlank:false,
18860     reverse:false,
18861     autoClear:false,
18862     remove:false,
18863
18864      /**
18865      * Filter the data by a specific attribute.
18866      * @param {String/RegExp} value Either string that the attribute value
18867      * should start with or a RegExp to test against the attribute
18868      * @param {String} attr (optional) The attribute passed in your node's attributes collection. Defaults to "text".
18869      * @param {TreeNode} startNode (optional) The node to start the filter at.
18870      */
18871     filter : function(value, attr, startNode){
18872         attr = attr || "text";
18873         var f;
18874         if(typeof value == "string"){
18875             var vlen = value.length;
18876             // auto clear empty filter
18877             if(vlen == 0 && this.clearBlank){
18878                 this.clear();
18879                 return;
18880             }
18881             value = value.toLowerCase();
18882             f = function(n){
18883                 return n.attributes[attr].substr(0, vlen).toLowerCase() == value;
18884             };
18885         }else if(value.exec){ // regex?
18886             f = function(n){
18887                 return value.test(n.attributes[attr]);
18888             };
18889         }else{
18890             throw 'Illegal filter type, must be string or regex';
18891         }
18892         this.filterBy(f, null, startNode);
18893         },
18894
18895     /**
18896      * Filter by a function. The passed function will be called with each
18897      * node in the tree (or from the startNode). If the function returns true, the node is kept
18898      * otherwise it is filtered. If a node is filtered, its children are also filtered.
18899      * @param {Function} fn The filter function
18900      * @param {Object} scope (optional) The scope of the function (defaults to the current node)
18901      */
18902     filterBy : function(fn, scope, startNode){
18903         startNode = startNode || this.tree.root;
18904         if(this.autoClear){
18905             this.clear();
18906         }
18907         var af = this.filtered, rv = this.reverse;
18908         var f = function(n){
18909             if(n == startNode){
18910                 return true;
18911             }
18912             if(af[n.id]){
18913                 return false;
18914             }
18915             var m = fn.call(scope || n, n);
18916             if(!m || rv){
18917                 af[n.id] = n;
18918                 n.ui.hide();
18919                 return false;
18920             }
18921             return true;
18922         };
18923         startNode.cascade(f);
18924         if(this.remove){
18925            for(var id in af){
18926                if(typeof id != "function"){
18927                    var n = af[id];
18928                    if(n && n.parentNode){
18929                        n.parentNode.removeChild(n);
18930                    }
18931                }
18932            }
18933         }
18934     },
18935
18936     /**
18937      * Clears the current filter. Note: with the "remove" option
18938      * set a filter cannot be cleared.
18939      */
18940     clear : function(){
18941         var t = this.tree;
18942         var af = this.filtered;
18943         for(var id in af){
18944             if(typeof id != "function"){
18945                 var n = af[id];
18946                 if(n){
18947                     n.ui.show();
18948                 }
18949             }
18950         }
18951         this.filtered = {};
18952     }
18953 };
18954 /*
18955  * Based on:
18956  * Ext JS Library 1.1.1
18957  * Copyright(c) 2006-2007, Ext JS, LLC.
18958  *
18959  * Originally Released Under LGPL - original licence link has changed is not relivant.
18960  *
18961  * Fork - LGPL
18962  * <script type="text/javascript">
18963  */
18964  
18965
18966 /**
18967  * @class Roo.tree.TreeSorter
18968  * Provides sorting of nodes in a TreePanel
18969  * 
18970  * @cfg {Boolean} folderSort True to sort leaf nodes under non leaf nodes
18971  * @cfg {String} property The named attribute on the node to sort by (defaults to text)
18972  * @cfg {String} dir The direction to sort (asc or desc) (defaults to asc)
18973  * @cfg {String} leafAttr The attribute used to determine leaf nodes in folder sort (defaults to "leaf")
18974  * @cfg {Boolean} caseSensitive true for case sensitive sort (defaults to false)
18975  * @cfg {Function} sortType A custom "casting" function used to convert node values before sorting
18976  * @constructor
18977  * @param {TreePanel} tree
18978  * @param {Object} config
18979  */
18980 Roo.tree.TreeSorter = function(tree, config){
18981     Roo.apply(this, config);
18982     tree.on("beforechildrenrendered", this.doSort, this);
18983     tree.on("append", this.updateSort, this);
18984     tree.on("insert", this.updateSort, this);
18985     
18986     var dsc = this.dir && this.dir.toLowerCase() == "desc";
18987     var p = this.property || "text";
18988     var sortType = this.sortType;
18989     var fs = this.folderSort;
18990     var cs = this.caseSensitive === true;
18991     var leafAttr = this.leafAttr || 'leaf';
18992
18993     this.sortFn = function(n1, n2){
18994         if(fs){
18995             if(n1.attributes[leafAttr] && !n2.attributes[leafAttr]){
18996                 return 1;
18997             }
18998             if(!n1.attributes[leafAttr] && n2.attributes[leafAttr]){
18999                 return -1;
19000             }
19001         }
19002         var v1 = sortType ? sortType(n1) : (cs ? n1.attributes[p] : n1.attributes[p].toUpperCase());
19003         var v2 = sortType ? sortType(n2) : (cs ? n2.attributes[p] : n2.attributes[p].toUpperCase());
19004         if(v1 < v2){
19005                         return dsc ? +1 : -1;
19006                 }else if(v1 > v2){
19007                         return dsc ? -1 : +1;
19008         }else{
19009                 return 0;
19010         }
19011     };
19012 };
19013
19014 Roo.tree.TreeSorter.prototype = {
19015     doSort : function(node){
19016         node.sort(this.sortFn);
19017     },
19018     
19019     compareNodes : function(n1, n2){
19020         return (n1.text.toUpperCase() > n2.text.toUpperCase() ? 1 : -1);
19021     },
19022     
19023     updateSort : function(tree, node){
19024         if(node.childrenRendered){
19025             this.doSort.defer(1, this, [node]);
19026         }
19027     }
19028 };/*
19029  * Based on:
19030  * Ext JS Library 1.1.1
19031  * Copyright(c) 2006-2007, Ext JS, LLC.
19032  *
19033  * Originally Released Under LGPL - original licence link has changed is not relivant.
19034  *
19035  * Fork - LGPL
19036  * <script type="text/javascript">
19037  */
19038
19039 if(Roo.dd.DropZone){
19040     
19041 Roo.tree.TreeDropZone = function(tree, config){
19042     this.allowParentInsert = false;
19043     this.allowContainerDrop = false;
19044     this.appendOnly = false;
19045     Roo.tree.TreeDropZone.superclass.constructor.call(this, tree.innerCt, config);
19046     this.tree = tree;
19047     this.lastInsertClass = "x-tree-no-status";
19048     this.dragOverData = {};
19049 };
19050
19051 Roo.extend(Roo.tree.TreeDropZone, Roo.dd.DropZone, {
19052     ddGroup : "TreeDD",
19053     scroll:  true,
19054     
19055     expandDelay : 1000,
19056     
19057     expandNode : function(node){
19058         if(node.hasChildNodes() && !node.isExpanded()){
19059             node.expand(false, null, this.triggerCacheRefresh.createDelegate(this));
19060         }
19061     },
19062     
19063     queueExpand : function(node){
19064         this.expandProcId = this.expandNode.defer(this.expandDelay, this, [node]);
19065     },
19066     
19067     cancelExpand : function(){
19068         if(this.expandProcId){
19069             clearTimeout(this.expandProcId);
19070             this.expandProcId = false;
19071         }
19072     },
19073     
19074     isValidDropPoint : function(n, pt, dd, e, data){
19075         if(!n || !data){ return false; }
19076         var targetNode = n.node;
19077         var dropNode = data.node;
19078         // default drop rules
19079         if(!(targetNode && targetNode.isTarget && pt)){
19080             return false;
19081         }
19082         if(pt == "append" && targetNode.allowChildren === false){
19083             return false;
19084         }
19085         if((pt == "above" || pt == "below") && (targetNode.parentNode && targetNode.parentNode.allowChildren === false)){
19086             return false;
19087         }
19088         if(dropNode && (targetNode == dropNode || dropNode.contains(targetNode))){
19089             return false;
19090         }
19091         // reuse the object
19092         var overEvent = this.dragOverData;
19093         overEvent.tree = this.tree;
19094         overEvent.target = targetNode;
19095         overEvent.data = data;
19096         overEvent.point = pt;
19097         overEvent.source = dd;
19098         overEvent.rawEvent = e;
19099         overEvent.dropNode = dropNode;
19100         overEvent.cancel = false;  
19101         var result = this.tree.fireEvent("nodedragover", overEvent);
19102         return overEvent.cancel === false && result !== false;
19103     },
19104     
19105     getDropPoint : function(e, n, dd)
19106     {
19107         var tn = n.node;
19108         if(tn.isRoot){
19109             return tn.allowChildren !== false ? "append" : false; // always append for root
19110         }
19111         var dragEl = n.ddel;
19112         var t = Roo.lib.Dom.getY(dragEl), b = t + dragEl.offsetHeight;
19113         var y = Roo.lib.Event.getPageY(e);
19114         //var noAppend = tn.allowChildren === false || tn.isLeaf();
19115         
19116         // we may drop nodes anywhere, as long as allowChildren has not been set to false..
19117         var noAppend = tn.allowChildren === false;
19118         if(this.appendOnly || tn.parentNode.allowChildren === false){
19119             return noAppend ? false : "append";
19120         }
19121         var noBelow = false;
19122         if(!this.allowParentInsert){
19123             noBelow = tn.hasChildNodes() && tn.isExpanded();
19124         }
19125         var q = (b - t) / (noAppend ? 2 : 3);
19126         if(y >= t && y < (t + q)){
19127             return "above";
19128         }else if(!noBelow && (noAppend || y >= b-q && y <= b)){
19129             return "below";
19130         }else{
19131             return "append";
19132         }
19133     },
19134     
19135     onNodeEnter : function(n, dd, e, data)
19136     {
19137         this.cancelExpand();
19138     },
19139     
19140     onNodeOver : function(n, dd, e, data)
19141     {
19142        
19143         var pt = this.getDropPoint(e, n, dd);
19144         var node = n.node;
19145         
19146         // auto node expand check
19147         if(!this.expandProcId && pt == "append" && node.hasChildNodes() && !n.node.isExpanded()){
19148             this.queueExpand(node);
19149         }else if(pt != "append"){
19150             this.cancelExpand();
19151         }
19152         
19153         // set the insert point style on the target node
19154         var returnCls = this.dropNotAllowed;
19155         if(this.isValidDropPoint(n, pt, dd, e, data)){
19156            if(pt){
19157                var el = n.ddel;
19158                var cls;
19159                if(pt == "above"){
19160                    returnCls = n.node.isFirst() ? "x-tree-drop-ok-above" : "x-tree-drop-ok-between";
19161                    cls = "x-tree-drag-insert-above";
19162                }else if(pt == "below"){
19163                    returnCls = n.node.isLast() ? "x-tree-drop-ok-below" : "x-tree-drop-ok-between";
19164                    cls = "x-tree-drag-insert-below";
19165                }else{
19166                    returnCls = "x-tree-drop-ok-append";
19167                    cls = "x-tree-drag-append";
19168                }
19169                if(this.lastInsertClass != cls){
19170                    Roo.fly(el).replaceClass(this.lastInsertClass, cls);
19171                    this.lastInsertClass = cls;
19172                }
19173            }
19174        }
19175        return returnCls;
19176     },
19177     
19178     onNodeOut : function(n, dd, e, data){
19179         
19180         this.cancelExpand();
19181         this.removeDropIndicators(n);
19182     },
19183     
19184     onNodeDrop : function(n, dd, e, data){
19185         var point = this.getDropPoint(e, n, dd);
19186         var targetNode = n.node;
19187         targetNode.ui.startDrop();
19188         if(!this.isValidDropPoint(n, point, dd, e, data)){
19189             targetNode.ui.endDrop();
19190             return false;
19191         }
19192         // first try to find the drop node
19193         var dropNode = data.node || (dd.getTreeNode ? dd.getTreeNode(data, targetNode, point, e) : null);
19194         var dropEvent = {
19195             tree : this.tree,
19196             target: targetNode,
19197             data: data,
19198             point: point,
19199             source: dd,
19200             rawEvent: e,
19201             dropNode: dropNode,
19202             cancel: !dropNode   
19203         };
19204         var retval = this.tree.fireEvent("beforenodedrop", dropEvent);
19205         if(retval === false || dropEvent.cancel === true || !dropEvent.dropNode){
19206             targetNode.ui.endDrop();
19207             return false;
19208         }
19209         // allow target changing
19210         targetNode = dropEvent.target;
19211         if(point == "append" && !targetNode.isExpanded()){
19212             targetNode.expand(false, null, function(){
19213                 this.completeDrop(dropEvent);
19214             }.createDelegate(this));
19215         }else{
19216             this.completeDrop(dropEvent);
19217         }
19218         return true;
19219     },
19220     
19221     completeDrop : function(de){
19222         var ns = de.dropNode, p = de.point, t = de.target;
19223         if(!(ns instanceof Array)){
19224             ns = [ns];
19225         }
19226         var n;
19227         for(var i = 0, len = ns.length; i < len; i++){
19228             n = ns[i];
19229             if(p == "above"){
19230                 t.parentNode.insertBefore(n, t);
19231             }else if(p == "below"){
19232                 t.parentNode.insertBefore(n, t.nextSibling);
19233             }else{
19234                 t.appendChild(n);
19235             }
19236         }
19237         n.ui.focus();
19238         if(this.tree.hlDrop){
19239             n.ui.highlight();
19240         }
19241         t.ui.endDrop();
19242         this.tree.fireEvent("nodedrop", de);
19243     },
19244     
19245     afterNodeMoved : function(dd, data, e, targetNode, dropNode){
19246         if(this.tree.hlDrop){
19247             dropNode.ui.focus();
19248             dropNode.ui.highlight();
19249         }
19250         this.tree.fireEvent("nodedrop", this.tree, targetNode, data, dd, e);
19251     },
19252     
19253     getTree : function(){
19254         return this.tree;
19255     },
19256     
19257     removeDropIndicators : function(n){
19258         if(n && n.ddel){
19259             var el = n.ddel;
19260             Roo.fly(el).removeClass([
19261                     "x-tree-drag-insert-above",
19262                     "x-tree-drag-insert-below",
19263                     "x-tree-drag-append"]);
19264             this.lastInsertClass = "_noclass";
19265         }
19266     },
19267     
19268     beforeDragDrop : function(target, e, id){
19269         this.cancelExpand();
19270         return true;
19271     },
19272     
19273     afterRepair : function(data){
19274         if(data && Roo.enableFx){
19275             data.node.ui.highlight();
19276         }
19277         this.hideProxy();
19278     } 
19279     
19280 });
19281
19282 }
19283 /*
19284  * Based on:
19285  * Ext JS Library 1.1.1
19286  * Copyright(c) 2006-2007, Ext JS, LLC.
19287  *
19288  * Originally Released Under LGPL - original licence link has changed is not relivant.
19289  *
19290  * Fork - LGPL
19291  * <script type="text/javascript">
19292  */
19293  
19294
19295 if(Roo.dd.DragZone){
19296 Roo.tree.TreeDragZone = function(tree, config){
19297     Roo.tree.TreeDragZone.superclass.constructor.call(this, tree.getTreeEl(), config);
19298     this.tree = tree;
19299 };
19300
19301 Roo.extend(Roo.tree.TreeDragZone, Roo.dd.DragZone, {
19302     ddGroup : "TreeDD",
19303    
19304     onBeforeDrag : function(data, e){
19305         var n = data.node;
19306         return n && n.draggable && !n.disabled;
19307     },
19308      
19309     
19310     onInitDrag : function(e){
19311         var data = this.dragData;
19312         this.tree.getSelectionModel().select(data.node);
19313         this.proxy.update("");
19314         data.node.ui.appendDDGhost(this.proxy.ghost.dom);
19315         this.tree.fireEvent("startdrag", this.tree, data.node, e);
19316     },
19317     
19318     getRepairXY : function(e, data){
19319         return data.node.ui.getDDRepairXY();
19320     },
19321     
19322     onEndDrag : function(data, e){
19323         this.tree.fireEvent("enddrag", this.tree, data.node, e);
19324         
19325         
19326     },
19327     
19328     onValidDrop : function(dd, e, id){
19329         this.tree.fireEvent("dragdrop", this.tree, this.dragData.node, dd, e);
19330         this.hideProxy();
19331     },
19332     
19333     beforeInvalidDrop : function(e, id){
19334         // this scrolls the original position back into view
19335         var sm = this.tree.getSelectionModel();
19336         sm.clearSelections();
19337         sm.select(this.dragData.node);
19338     }
19339 });
19340 }/*
19341  * Based on:
19342  * Ext JS Library 1.1.1
19343  * Copyright(c) 2006-2007, Ext JS, LLC.
19344  *
19345  * Originally Released Under LGPL - original licence link has changed is not relivant.
19346  *
19347  * Fork - LGPL
19348  * <script type="text/javascript">
19349  */
19350 /**
19351  * @class Roo.tree.TreeEditor
19352  * @extends Roo.Editor
19353  * Provides editor functionality for inline tree node editing.  Any valid {@link Roo.form.Field} can be used
19354  * as the editor field.
19355  * @constructor
19356  * @param {Object} config (used to be the tree panel.)
19357  * @param {Object} oldconfig DEPRECIATED Either a prebuilt {@link Roo.form.Field} instance or a Field config object
19358  * 
19359  * @cfg {Roo.tree.TreePanel} tree The tree to bind to.
19360  * @cfg {Roo.form.TextField|Object} field The field configuration
19361  *
19362  * 
19363  */
19364 Roo.tree.TreeEditor = function(config, oldconfig) { // was -- (tree, config){
19365     var tree = config;
19366     var field;
19367     if (oldconfig) { // old style..
19368         field = oldconfig.events ? oldconfig : new Roo.form.TextField(oldconfig);
19369     } else {
19370         // new style..
19371         tree = config.tree;
19372         config.field = config.field  || {};
19373         config.field.xtype = 'TextField';
19374         field = Roo.factory(config.field, Roo.form);
19375     }
19376     config = config || {};
19377     
19378     
19379     this.addEvents({
19380         /**
19381          * @event beforenodeedit
19382          * Fires when editing is initiated, but before the value changes.  Editing can be canceled by returning
19383          * false from the handler of this event.
19384          * @param {Editor} this
19385          * @param {Roo.tree.Node} node 
19386          */
19387         "beforenodeedit" : true
19388     });
19389     
19390     //Roo.log(config);
19391     Roo.tree.TreeEditor.superclass.constructor.call(this, field, config);
19392
19393     this.tree = tree;
19394
19395     tree.on('beforeclick', this.beforeNodeClick, this);
19396     tree.getTreeEl().on('mousedown', this.hide, this);
19397     this.on('complete', this.updateNode, this);
19398     this.on('beforestartedit', this.fitToTree, this);
19399     this.on('startedit', this.bindScroll, this, {delay:10});
19400     this.on('specialkey', this.onSpecialKey, this);
19401 };
19402
19403 Roo.extend(Roo.tree.TreeEditor, Roo.Editor, {
19404     /**
19405      * @cfg {String} alignment
19406      * The position to align to (see {@link Roo.Element#alignTo} for more details, defaults to "l-l").
19407      */
19408     alignment: "l-l",
19409     // inherit
19410     autoSize: false,
19411     /**
19412      * @cfg {Boolean} hideEl
19413      * True to hide the bound element while the editor is displayed (defaults to false)
19414      */
19415     hideEl : false,
19416     /**
19417      * @cfg {String} cls
19418      * CSS class to apply to the editor (defaults to "x-small-editor x-tree-editor")
19419      */
19420     cls: "x-small-editor x-tree-editor",
19421     /**
19422      * @cfg {Boolean} shim
19423      * True to shim the editor if selects/iframes could be displayed beneath it (defaults to false)
19424      */
19425     shim:false,
19426     // inherit
19427     shadow:"frame",
19428     /**
19429      * @cfg {Number} maxWidth
19430      * The maximum width in pixels of the editor field (defaults to 250).  Note that if the maxWidth would exceed
19431      * the containing tree element's size, it will be automatically limited for you to the container width, taking
19432      * scroll and client offsets into account prior to each edit.
19433      */
19434     maxWidth: 250,
19435
19436     editDelay : 350,
19437
19438     // private
19439     fitToTree : function(ed, el){
19440         var td = this.tree.getTreeEl().dom, nd = el.dom;
19441         if(td.scrollLeft >  nd.offsetLeft){ // ensure the node left point is visible
19442             td.scrollLeft = nd.offsetLeft;
19443         }
19444         var w = Math.min(
19445                 this.maxWidth,
19446                 (td.clientWidth > 20 ? td.clientWidth : td.offsetWidth) - Math.max(0, nd.offsetLeft-td.scrollLeft) - /*cushion*/5);
19447         this.setSize(w, '');
19448         
19449         return this.fireEvent('beforenodeedit', this, this.editNode);
19450         
19451     },
19452
19453     // private
19454     triggerEdit : function(node){
19455         this.completeEdit();
19456         this.editNode = node;
19457         this.startEdit(node.ui.textNode, node.text);
19458     },
19459
19460     // private
19461     bindScroll : function(){
19462         this.tree.getTreeEl().on('scroll', this.cancelEdit, this);
19463     },
19464
19465     // private
19466     beforeNodeClick : function(node, e){
19467         var sinceLast = (this.lastClick ? this.lastClick.getElapsed() : 0);
19468         this.lastClick = new Date();
19469         if(sinceLast > this.editDelay && this.tree.getSelectionModel().isSelected(node)){
19470             e.stopEvent();
19471             this.triggerEdit(node);
19472             return false;
19473         }
19474         return true;
19475     },
19476
19477     // private
19478     updateNode : function(ed, value){
19479         this.tree.getTreeEl().un('scroll', this.cancelEdit, this);
19480         this.editNode.setText(value);
19481     },
19482
19483     // private
19484     onHide : function(){
19485         Roo.tree.TreeEditor.superclass.onHide.call(this);
19486         if(this.editNode){
19487             this.editNode.ui.focus();
19488         }
19489     },
19490
19491     // private
19492     onSpecialKey : function(field, e){
19493         var k = e.getKey();
19494         if(k == e.ESC){
19495             e.stopEvent();
19496             this.cancelEdit();
19497         }else if(k == e.ENTER && !e.hasModifier()){
19498             e.stopEvent();
19499             this.completeEdit();
19500         }
19501     }
19502 });//<Script type="text/javascript">
19503 /*
19504  * Based on:
19505  * Ext JS Library 1.1.1
19506  * Copyright(c) 2006-2007, Ext JS, LLC.
19507  *
19508  * Originally Released Under LGPL - original licence link has changed is not relivant.
19509  *
19510  * Fork - LGPL
19511  * <script type="text/javascript">
19512  */
19513  
19514 /**
19515  * Not documented??? - probably should be...
19516  */
19517
19518 Roo.tree.ColumnNodeUI = Roo.extend(Roo.tree.TreeNodeUI, {
19519     //focus: Roo.emptyFn, // prevent odd scrolling behavior
19520     
19521     renderElements : function(n, a, targetNode, bulkRender){
19522         //consel.log("renderElements?");
19523         this.indentMarkup = n.parentNode ? n.parentNode.ui.getChildIndent() : '';
19524
19525         var t = n.getOwnerTree();
19526         var tid = Pman.Tab.Document_TypesTree.tree.el.id;
19527         
19528         var cols = t.columns;
19529         var bw = t.borderWidth;
19530         var c = cols[0];
19531         var href = a.href ? a.href : Roo.isGecko ? "" : "#";
19532          var cb = typeof a.checked == "boolean";
19533         var tx = String.format('{0}',n.text || (c.renderer ? c.renderer(a[c.dataIndex], n, a) : a[c.dataIndex]));
19534         var colcls = 'x-t-' + tid + '-c0';
19535         var buf = [
19536             '<li class="x-tree-node">',
19537             
19538                 
19539                 '<div class="x-tree-node-el ', a.cls,'">',
19540                     // extran...
19541                     '<div class="x-tree-col ', colcls, '" style="width:', c.width-bw, 'px;">',
19542                 
19543                 
19544                         '<span class="x-tree-node-indent">',this.indentMarkup,'</span>',
19545                         '<img src="', this.emptyIcon, '" class="x-tree-ec-icon  " />',
19546                         '<img src="', a.icon || this.emptyIcon, '" class="x-tree-node-icon',
19547                            (a.icon ? ' x-tree-node-inline-icon' : ''),
19548                            (a.iconCls ? ' '+a.iconCls : ''),
19549                            '" unselectable="on" />',
19550                         (cb ? ('<input class="x-tree-node-cb" type="checkbox" ' + 
19551                              (a.checked ? 'checked="checked" />' : ' />')) : ''),
19552                              
19553                         '<a class="x-tree-node-anchor" hidefocus="on" href="',href,'" tabIndex="1" ',
19554                             (a.hrefTarget ? ' target="' +a.hrefTarget + '"' : ''), '>',
19555                             '<span unselectable="on" qtip="' + tx + '">',
19556                              tx,
19557                              '</span></a>' ,
19558                     '</div>',
19559                      '<a class="x-tree-node-anchor" hidefocus="on" href="',href,'" tabIndex="1" ',
19560                             (a.hrefTarget ? ' target="' +a.hrefTarget + '"' : ''), '>'
19561                  ];
19562         for(var i = 1, len = cols.length; i < len; i++){
19563             c = cols[i];
19564             colcls = 'x-t-' + tid + '-c' +i;
19565             tx = String.format('{0}', (c.renderer ? c.renderer(a[c.dataIndex], n, a) : a[c.dataIndex]));
19566             buf.push('<div class="x-tree-col ', colcls, ' ' ,(c.cls?c.cls:''),'" style="width:',c.width-bw,'px;">',
19567                         '<div class="x-tree-col-text" qtip="' + tx +'">',tx,"</div>",
19568                       "</div>");
19569          }
19570          
19571          buf.push(
19572             '</a>',
19573             '<div class="x-clear"></div></div>',
19574             '<ul class="x-tree-node-ct" style="display:none;"></ul>',
19575             "</li>");
19576         
19577         if(bulkRender !== true && n.nextSibling && n.nextSibling.ui.getEl()){
19578             this.wrap = Roo.DomHelper.insertHtml("beforeBegin",
19579                                 n.nextSibling.ui.getEl(), buf.join(""));
19580         }else{
19581             this.wrap = Roo.DomHelper.insertHtml("beforeEnd", targetNode, buf.join(""));
19582         }
19583         var el = this.wrap.firstChild;
19584         this.elRow = el;
19585         this.elNode = el.firstChild;
19586         this.ranchor = el.childNodes[1];
19587         this.ctNode = this.wrap.childNodes[1];
19588         var cs = el.firstChild.childNodes;
19589         this.indentNode = cs[0];
19590         this.ecNode = cs[1];
19591         this.iconNode = cs[2];
19592         var index = 3;
19593         if(cb){
19594             this.checkbox = cs[3];
19595             index++;
19596         }
19597         this.anchor = cs[index];
19598         
19599         this.textNode = cs[index].firstChild;
19600         
19601         //el.on("click", this.onClick, this);
19602         //el.on("dblclick", this.onDblClick, this);
19603         
19604         
19605        // console.log(this);
19606     },
19607     initEvents : function(){
19608         Roo.tree.ColumnNodeUI.superclass.initEvents.call(this);
19609         
19610             
19611         var a = this.ranchor;
19612
19613         var el = Roo.get(a);
19614
19615         if(Roo.isOpera){ // opera render bug ignores the CSS
19616             el.setStyle("text-decoration", "none");
19617         }
19618
19619         el.on("click", this.onClick, this);
19620         el.on("dblclick", this.onDblClick, this);
19621         el.on("contextmenu", this.onContextMenu, this);
19622         
19623     },
19624     
19625     /*onSelectedChange : function(state){
19626         if(state){
19627             this.focus();
19628             this.addClass("x-tree-selected");
19629         }else{
19630             //this.blur();
19631             this.removeClass("x-tree-selected");
19632         }
19633     },*/
19634     addClass : function(cls){
19635         if(this.elRow){
19636             Roo.fly(this.elRow).addClass(cls);
19637         }
19638         
19639     },
19640     
19641     
19642     removeClass : function(cls){
19643         if(this.elRow){
19644             Roo.fly(this.elRow).removeClass(cls);
19645         }
19646     }
19647
19648     
19649     
19650 });//<Script type="text/javascript">
19651
19652 /*
19653  * Based on:
19654  * Ext JS Library 1.1.1
19655  * Copyright(c) 2006-2007, Ext JS, LLC.
19656  *
19657  * Originally Released Under LGPL - original licence link has changed is not relivant.
19658  *
19659  * Fork - LGPL
19660  * <script type="text/javascript">
19661  */
19662  
19663
19664 /**
19665  * @class Roo.tree.ColumnTree
19666  * @extends Roo.data.TreePanel
19667  * @cfg {Object} columns  Including width, header, renderer, cls, dataIndex 
19668  * @cfg {int} borderWidth  compined right/left border allowance
19669  * @constructor
19670  * @param {String/HTMLElement/Element} el The container element
19671  * @param {Object} config
19672  */
19673 Roo.tree.ColumnTree =  function(el, config)
19674 {
19675    Roo.tree.ColumnTree.superclass.constructor.call(this, el , config);
19676    this.addEvents({
19677         /**
19678         * @event resize
19679         * Fire this event on a container when it resizes
19680         * @param {int} w Width
19681         * @param {int} h Height
19682         */
19683        "resize" : true
19684     });
19685     this.on('resize', this.onResize, this);
19686 };
19687
19688 Roo.extend(Roo.tree.ColumnTree, Roo.tree.TreePanel, {
19689     //lines:false,
19690     
19691     
19692     borderWidth: Roo.isBorderBox ? 0 : 2, 
19693     headEls : false,
19694     
19695     render : function(){
19696         // add the header.....
19697        
19698         Roo.tree.ColumnTree.superclass.render.apply(this);
19699         
19700         this.el.addClass('x-column-tree');
19701         
19702         this.headers = this.el.createChild(
19703             {cls:'x-tree-headers'},this.innerCt.dom);
19704    
19705         var cols = this.columns, c;
19706         var totalWidth = 0;
19707         this.headEls = [];
19708         var  len = cols.length;
19709         for(var i = 0; i < len; i++){
19710              c = cols[i];
19711              totalWidth += c.width;
19712             this.headEls.push(this.headers.createChild({
19713                  cls:'x-tree-hd ' + (c.cls?c.cls+'-hd':''),
19714                  cn: {
19715                      cls:'x-tree-hd-text',
19716                      html: c.header
19717                  },
19718                  style:'width:'+(c.width-this.borderWidth)+'px;'
19719              }));
19720         }
19721         this.headers.createChild({cls:'x-clear'});
19722         // prevent floats from wrapping when clipped
19723         this.headers.setWidth(totalWidth);
19724         //this.innerCt.setWidth(totalWidth);
19725         this.innerCt.setStyle({ overflow: 'auto' });
19726         this.onResize(this.width, this.height);
19727              
19728         
19729     },
19730     onResize : function(w,h)
19731     {
19732         this.height = h;
19733         this.width = w;
19734         // resize cols..
19735         this.innerCt.setWidth(this.width);
19736         this.innerCt.setHeight(this.height-20);
19737         
19738         // headers...
19739         var cols = this.columns, c;
19740         var totalWidth = 0;
19741         var expEl = false;
19742         var len = cols.length;
19743         for(var i = 0; i < len; i++){
19744             c = cols[i];
19745             if (this.autoExpandColumn !== false && c.dataIndex == this.autoExpandColumn) {
19746                 // it's the expander..
19747                 expEl  = this.headEls[i];
19748                 continue;
19749             }
19750             totalWidth += c.width;
19751             
19752         }
19753         if (expEl) {
19754             expEl.setWidth(  ((w - totalWidth)-this.borderWidth - 20));
19755         }
19756         this.headers.setWidth(w-20);
19757
19758         
19759         
19760         
19761     }
19762 });
19763 /*
19764  * Based on:
19765  * Ext JS Library 1.1.1
19766  * Copyright(c) 2006-2007, Ext JS, LLC.
19767  *
19768  * Originally Released Under LGPL - original licence link has changed is not relivant.
19769  *
19770  * Fork - LGPL
19771  * <script type="text/javascript">
19772  */
19773  
19774 /**
19775  * @class Roo.menu.Menu
19776  * @extends Roo.util.Observable
19777  * A menu object.  This is the container to which you add all other menu items.  Menu can also serve a as a base class
19778  * when you want a specialzed menu based off of another component (like {@link Roo.menu.DateMenu} for example).
19779  * @constructor
19780  * Creates a new Menu
19781  * @param {Object} config Configuration options
19782  */
19783 Roo.menu.Menu = function(config){
19784     Roo.apply(this, config);
19785     this.id = this.id || Roo.id();
19786     this.addEvents({
19787         /**
19788          * @event beforeshow
19789          * Fires before this menu is displayed
19790          * @param {Roo.menu.Menu} this
19791          */
19792         beforeshow : true,
19793         /**
19794          * @event beforehide
19795          * Fires before this menu is hidden
19796          * @param {Roo.menu.Menu} this
19797          */
19798         beforehide : true,
19799         /**
19800          * @event show
19801          * Fires after this menu is displayed
19802          * @param {Roo.menu.Menu} this
19803          */
19804         show : true,
19805         /**
19806          * @event hide
19807          * Fires after this menu is hidden
19808          * @param {Roo.menu.Menu} this
19809          */
19810         hide : true,
19811         /**
19812          * @event click
19813          * Fires when this menu is clicked (or when the enter key is pressed while it is active)
19814          * @param {Roo.menu.Menu} this
19815          * @param {Roo.menu.Item} menuItem The menu item that was clicked
19816          * @param {Roo.EventObject} e
19817          */
19818         click : true,
19819         /**
19820          * @event mouseover
19821          * Fires when the mouse is hovering over this menu
19822          * @param {Roo.menu.Menu} this
19823          * @param {Roo.EventObject} e
19824          * @param {Roo.menu.Item} menuItem The menu item that was clicked
19825          */
19826         mouseover : true,
19827         /**
19828          * @event mouseout
19829          * Fires when the mouse exits this menu
19830          * @param {Roo.menu.Menu} this
19831          * @param {Roo.EventObject} e
19832          * @param {Roo.menu.Item} menuItem The menu item that was clicked
19833          */
19834         mouseout : true,
19835         /**
19836          * @event itemclick
19837          * Fires when a menu item contained in this menu is clicked
19838          * @param {Roo.menu.BaseItem} baseItem The BaseItem that was clicked
19839          * @param {Roo.EventObject} e
19840          */
19841         itemclick: true
19842     });
19843     if (this.registerMenu) {
19844         Roo.menu.MenuMgr.register(this);
19845     }
19846     
19847     var mis = this.items;
19848     this.items = new Roo.util.MixedCollection();
19849     if(mis){
19850         this.add.apply(this, mis);
19851     }
19852 };
19853
19854 Roo.extend(Roo.menu.Menu, Roo.util.Observable, {
19855     /**
19856      * @cfg {Number} minWidth The minimum width of the menu in pixels (defaults to 120)
19857      */
19858     minWidth : 120,
19859     /**
19860      * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop"
19861      * for bottom-right shadow (defaults to "sides")
19862      */
19863     shadow : "sides",
19864     /**
19865      * @cfg {String} subMenuAlign The {@link Roo.Element#alignTo} anchor position value to use for submenus of
19866      * this menu (defaults to "tl-tr?")
19867      */
19868     subMenuAlign : "tl-tr?",
19869     /**
19870      * @cfg {String} defaultAlign The default {@link Roo.Element#alignTo) anchor position value for this menu
19871      * relative to its element of origin (defaults to "tl-bl?")
19872      */
19873     defaultAlign : "tl-bl?",
19874     /**
19875      * @cfg {Boolean} allowOtherMenus True to allow multiple menus to be displayed at the same time (defaults to false)
19876      */
19877     allowOtherMenus : false,
19878     /**
19879      * @cfg {Boolean} registerMenu True (default) - means that clicking on screen etc. hides it.
19880      */
19881     registerMenu : true,
19882
19883     hidden:true,
19884
19885     // private
19886     render : function(){
19887         if(this.el){
19888             return;
19889         }
19890         var el = this.el = new Roo.Layer({
19891             cls: "x-menu",
19892             shadow:this.shadow,
19893             constrain: false,
19894             parentEl: this.parentEl || document.body,
19895             zindex:15000
19896         });
19897
19898         this.keyNav = new Roo.menu.MenuNav(this);
19899
19900         if(this.plain){
19901             el.addClass("x-menu-plain");
19902         }
19903         if(this.cls){
19904             el.addClass(this.cls);
19905         }
19906         // generic focus element
19907         this.focusEl = el.createChild({
19908             tag: "a", cls: "x-menu-focus", href: "#", onclick: "return false;", tabIndex:"-1"
19909         });
19910         var ul = el.createChild({tag: "ul", cls: "x-menu-list"});
19911         ul.on("click", this.onClick, this);
19912         ul.on("mouseover", this.onMouseOver, this);
19913         ul.on("mouseout", this.onMouseOut, this);
19914         this.items.each(function(item){
19915             if (item.hidden) {
19916                 return;
19917             }
19918             
19919             var li = document.createElement("li");
19920             li.className = "x-menu-list-item";
19921             ul.dom.appendChild(li);
19922             item.render(li, this);
19923         }, this);
19924         this.ul = ul;
19925         this.autoWidth();
19926     },
19927
19928     // private
19929     autoWidth : function(){
19930         var el = this.el, ul = this.ul;
19931         if(!el){
19932             return;
19933         }
19934         var w = this.width;
19935         if(w){
19936             el.setWidth(w);
19937         }else if(Roo.isIE){
19938             el.setWidth(this.minWidth);
19939             var t = el.dom.offsetWidth; // force recalc
19940             el.setWidth(ul.getWidth()+el.getFrameWidth("lr"));
19941         }
19942     },
19943
19944     // private
19945     delayAutoWidth : function(){
19946         if(this.rendered){
19947             if(!this.awTask){
19948                 this.awTask = new Roo.util.DelayedTask(this.autoWidth, this);
19949             }
19950             this.awTask.delay(20);
19951         }
19952     },
19953
19954     // private
19955     findTargetItem : function(e){
19956         var t = e.getTarget(".x-menu-list-item", this.ul,  true);
19957         if(t && t.menuItemId){
19958             return this.items.get(t.menuItemId);
19959         }
19960     },
19961
19962     // private
19963     onClick : function(e){
19964         var t;
19965         if(t = this.findTargetItem(e)){
19966             t.onClick(e);
19967             this.fireEvent("click", this, t, e);
19968         }
19969     },
19970
19971     // private
19972     setActiveItem : function(item, autoExpand){
19973         if(item != this.activeItem){
19974             if(this.activeItem){
19975                 this.activeItem.deactivate();
19976             }
19977             this.activeItem = item;
19978             item.activate(autoExpand);
19979         }else if(autoExpand){
19980             item.expandMenu();
19981         }
19982     },
19983
19984     // private
19985     tryActivate : function(start, step){
19986         var items = this.items;
19987         for(var i = start, len = items.length; i >= 0 && i < len; i+= step){
19988             var item = items.get(i);
19989             if(!item.disabled && item.canActivate){
19990                 this.setActiveItem(item, false);
19991                 return item;
19992             }
19993         }
19994         return false;
19995     },
19996
19997     // private
19998     onMouseOver : function(e){
19999         var t;
20000         if(t = this.findTargetItem(e)){
20001             if(t.canActivate && !t.disabled){
20002                 this.setActiveItem(t, true);
20003             }
20004         }
20005         this.fireEvent("mouseover", this, e, t);
20006     },
20007
20008     // private
20009     onMouseOut : function(e){
20010         var t;
20011         if(t = this.findTargetItem(e)){
20012             if(t == this.activeItem && t.shouldDeactivate(e)){
20013                 this.activeItem.deactivate();
20014                 delete this.activeItem;
20015             }
20016         }
20017         this.fireEvent("mouseout", this, e, t);
20018     },
20019
20020     /**
20021      * Read-only.  Returns true if the menu is currently displayed, else false.
20022      * @type Boolean
20023      */
20024     isVisible : function(){
20025         return this.el && !this.hidden;
20026     },
20027
20028     /**
20029      * Displays this menu relative to another element
20030      * @param {String/HTMLElement/Roo.Element} element The element to align to
20031      * @param {String} position (optional) The {@link Roo.Element#alignTo} anchor position to use in aligning to
20032      * the element (defaults to this.defaultAlign)
20033      * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
20034      */
20035     show : function(el, pos, parentMenu){
20036         this.parentMenu = parentMenu;
20037         if(!this.el){
20038             this.render();
20039         }
20040         this.fireEvent("beforeshow", this);
20041         this.showAt(this.el.getAlignToXY(el, pos || this.defaultAlign), parentMenu, false);
20042     },
20043
20044     /**
20045      * Displays this menu at a specific xy position
20046      * @param {Array} xyPosition Contains X & Y [x, y] values for the position at which to show the menu (coordinates are page-based)
20047      * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
20048      */
20049     showAt : function(xy, parentMenu, /* private: */_e){
20050         this.parentMenu = parentMenu;
20051         if(!this.el){
20052             this.render();
20053         }
20054         if(_e !== false){
20055             this.fireEvent("beforeshow", this);
20056             xy = this.el.adjustForConstraints(xy);
20057         }
20058         this.el.setXY(xy);
20059         this.el.show();
20060         this.hidden = false;
20061         this.focus();
20062         this.fireEvent("show", this);
20063     },
20064
20065     focus : function(){
20066         if(!this.hidden){
20067             this.doFocus.defer(50, this);
20068         }
20069     },
20070
20071     doFocus : function(){
20072         if(!this.hidden){
20073             this.focusEl.focus();
20074         }
20075     },
20076
20077     /**
20078      * Hides this menu and optionally all parent menus
20079      * @param {Boolean} deep (optional) True to hide all parent menus recursively, if any (defaults to false)
20080      */
20081     hide : function(deep){
20082         if(this.el && this.isVisible()){
20083             this.fireEvent("beforehide", this);
20084             if(this.activeItem){
20085                 this.activeItem.deactivate();
20086                 this.activeItem = null;
20087             }
20088             this.el.hide();
20089             this.hidden = true;
20090             this.fireEvent("hide", this);
20091         }
20092         if(deep === true && this.parentMenu){
20093             this.parentMenu.hide(true);
20094         }
20095     },
20096
20097     /**
20098      * Addds one or more items of any type supported by the Menu class, or that can be converted into menu items.
20099      * Any of the following are valid:
20100      * <ul>
20101      * <li>Any menu item object based on {@link Roo.menu.Item}</li>
20102      * <li>An HTMLElement object which will be converted to a menu item</li>
20103      * <li>A menu item config object that will be created as a new menu item</li>
20104      * <li>A string, which can either be '-' or 'separator' to add a menu separator, otherwise
20105      * it will be converted into a {@link Roo.menu.TextItem} and added</li>
20106      * </ul>
20107      * Usage:
20108      * <pre><code>
20109 // Create the menu
20110 var menu = new Roo.menu.Menu();
20111
20112 // Create a menu item to add by reference
20113 var menuItem = new Roo.menu.Item({ text: 'New Item!' });
20114
20115 // Add a bunch of items at once using different methods.
20116 // Only the last item added will be returned.
20117 var item = menu.add(
20118     menuItem,                // add existing item by ref
20119     'Dynamic Item',          // new TextItem
20120     '-',                     // new separator
20121     { text: 'Config Item' }  // new item by config
20122 );
20123 </code></pre>
20124      * @param {Mixed} args One or more menu items, menu item configs or other objects that can be converted to menu items
20125      * @return {Roo.menu.Item} The menu item that was added, or the last one if multiple items were added
20126      */
20127     add : function(){
20128         var a = arguments, l = a.length, item;
20129         for(var i = 0; i < l; i++){
20130             var el = a[i];
20131             if ((typeof(el) == "object") && el.xtype && el.xns) {
20132                 el = Roo.factory(el, Roo.menu);
20133             }
20134             
20135             if(el.render){ // some kind of Item
20136                 item = this.addItem(el);
20137             }else if(typeof el == "string"){ // string
20138                 if(el == "separator" || el == "-"){
20139                     item = this.addSeparator();
20140                 }else{
20141                     item = this.addText(el);
20142                 }
20143             }else if(el.tagName || el.el){ // element
20144                 item = this.addElement(el);
20145             }else if(typeof el == "object"){ // must be menu item config?
20146                 item = this.addMenuItem(el);
20147             }
20148         }
20149         return item;
20150     },
20151
20152     /**
20153      * Returns this menu's underlying {@link Roo.Element} object
20154      * @return {Roo.Element} The element
20155      */
20156     getEl : function(){
20157         if(!this.el){
20158             this.render();
20159         }
20160         return this.el;
20161     },
20162
20163     /**
20164      * Adds a separator bar to the menu
20165      * @return {Roo.menu.Item} The menu item that was added
20166      */
20167     addSeparator : function(){
20168         return this.addItem(new Roo.menu.Separator());
20169     },
20170
20171     /**
20172      * Adds an {@link Roo.Element} object to the menu
20173      * @param {String/HTMLElement/Roo.Element} el The element or DOM node to add, or its id
20174      * @return {Roo.menu.Item} The menu item that was added
20175      */
20176     addElement : function(el){
20177         return this.addItem(new Roo.menu.BaseItem(el));
20178     },
20179
20180     /**
20181      * Adds an existing object based on {@link Roo.menu.Item} to the menu
20182      * @param {Roo.menu.Item} item The menu item to add
20183      * @return {Roo.menu.Item} The menu item that was added
20184      */
20185     addItem : function(item){
20186         this.items.add(item);
20187         if(this.ul){
20188             var li = document.createElement("li");
20189             li.className = "x-menu-list-item";
20190             this.ul.dom.appendChild(li);
20191             item.render(li, this);
20192             this.delayAutoWidth();
20193         }
20194         return item;
20195     },
20196
20197     /**
20198      * Creates a new {@link Roo.menu.Item} based an the supplied config object and adds it to the menu
20199      * @param {Object} config A MenuItem config object
20200      * @return {Roo.menu.Item} The menu item that was added
20201      */
20202     addMenuItem : function(config){
20203         if(!(config instanceof Roo.menu.Item)){
20204             if(typeof config.checked == "boolean"){ // must be check menu item config?
20205                 config = new Roo.menu.CheckItem(config);
20206             }else{
20207                 config = new Roo.menu.Item(config);
20208             }
20209         }
20210         return this.addItem(config);
20211     },
20212
20213     /**
20214      * Creates a new {@link Roo.menu.TextItem} with the supplied text and adds it to the menu
20215      * @param {String} text The text to display in the menu item
20216      * @return {Roo.menu.Item} The menu item that was added
20217      */
20218     addText : function(text){
20219         return this.addItem(new Roo.menu.TextItem({ text : text }));
20220     },
20221
20222     /**
20223      * Inserts an existing object based on {@link Roo.menu.Item} to the menu at a specified index
20224      * @param {Number} index The index in the menu's list of current items where the new item should be inserted
20225      * @param {Roo.menu.Item} item The menu item to add
20226      * @return {Roo.menu.Item} The menu item that was added
20227      */
20228     insert : function(index, item){
20229         this.items.insert(index, item);
20230         if(this.ul){
20231             var li = document.createElement("li");
20232             li.className = "x-menu-list-item";
20233             this.ul.dom.insertBefore(li, this.ul.dom.childNodes[index]);
20234             item.render(li, this);
20235             this.delayAutoWidth();
20236         }
20237         return item;
20238     },
20239
20240     /**
20241      * Removes an {@link Roo.menu.Item} from the menu and destroys the object
20242      * @param {Roo.menu.Item} item The menu item to remove
20243      */
20244     remove : function(item){
20245         this.items.removeKey(item.id);
20246         item.destroy();
20247     },
20248
20249     /**
20250      * Removes and destroys all items in the menu
20251      */
20252     removeAll : function(){
20253         var f;
20254         while(f = this.items.first()){
20255             this.remove(f);
20256         }
20257     }
20258 });
20259
20260 // MenuNav is a private utility class used internally by the Menu
20261 Roo.menu.MenuNav = function(menu){
20262     Roo.menu.MenuNav.superclass.constructor.call(this, menu.el);
20263     this.scope = this.menu = menu;
20264 };
20265
20266 Roo.extend(Roo.menu.MenuNav, Roo.KeyNav, {
20267     doRelay : function(e, h){
20268         var k = e.getKey();
20269         if(!this.menu.activeItem && e.isNavKeyPress() && k != e.SPACE && k != e.RETURN){
20270             this.menu.tryActivate(0, 1);
20271             return false;
20272         }
20273         return h.call(this.scope || this, e, this.menu);
20274     },
20275
20276     up : function(e, m){
20277         if(!m.tryActivate(m.items.indexOf(m.activeItem)-1, -1)){
20278             m.tryActivate(m.items.length-1, -1);
20279         }
20280     },
20281
20282     down : function(e, m){
20283         if(!m.tryActivate(m.items.indexOf(m.activeItem)+1, 1)){
20284             m.tryActivate(0, 1);
20285         }
20286     },
20287
20288     right : function(e, m){
20289         if(m.activeItem){
20290             m.activeItem.expandMenu(true);
20291         }
20292     },
20293
20294     left : function(e, m){
20295         m.hide();
20296         if(m.parentMenu && m.parentMenu.activeItem){
20297             m.parentMenu.activeItem.activate();
20298         }
20299     },
20300
20301     enter : function(e, m){
20302         if(m.activeItem){
20303             e.stopPropagation();
20304             m.activeItem.onClick(e);
20305             m.fireEvent("click", this, m.activeItem);
20306             return true;
20307         }
20308     }
20309 });/*
20310  * Based on:
20311  * Ext JS Library 1.1.1
20312  * Copyright(c) 2006-2007, Ext JS, LLC.
20313  *
20314  * Originally Released Under LGPL - original licence link has changed is not relivant.
20315  *
20316  * Fork - LGPL
20317  * <script type="text/javascript">
20318  */
20319  
20320 /**
20321  * @class Roo.menu.MenuMgr
20322  * Provides a common registry of all menu items on a page so that they can be easily accessed by id.
20323  * @singleton
20324  */
20325 Roo.menu.MenuMgr = function(){
20326    var menus, active, groups = {}, attached = false, lastShow = new Date();
20327
20328    // private - called when first menu is created
20329    function init(){
20330        menus = {};
20331        active = new Roo.util.MixedCollection();
20332        Roo.get(document).addKeyListener(27, function(){
20333            if(active.length > 0){
20334                hideAll();
20335            }
20336        });
20337    }
20338
20339    // private
20340    function hideAll(){
20341        if(active && active.length > 0){
20342            var c = active.clone();
20343            c.each(function(m){
20344                m.hide();
20345            });
20346        }
20347    }
20348
20349    // private
20350    function onHide(m){
20351        active.remove(m);
20352        if(active.length < 1){
20353            Roo.get(document).un("mousedown", onMouseDown);
20354            attached = false;
20355        }
20356    }
20357
20358    // private
20359    function onShow(m){
20360        var last = active.last();
20361        lastShow = new Date();
20362        active.add(m);
20363        if(!attached){
20364            Roo.get(document).on("mousedown", onMouseDown);
20365            attached = true;
20366        }
20367        if(m.parentMenu){
20368           m.getEl().setZIndex(parseInt(m.parentMenu.getEl().getStyle("z-index"), 10) + 3);
20369           m.parentMenu.activeChild = m;
20370        }else if(last && last.isVisible()){
20371           m.getEl().setZIndex(parseInt(last.getEl().getStyle("z-index"), 10) + 3);
20372        }
20373    }
20374
20375    // private
20376    function onBeforeHide(m){
20377        if(m.activeChild){
20378            m.activeChild.hide();
20379        }
20380        if(m.autoHideTimer){
20381            clearTimeout(m.autoHideTimer);
20382            delete m.autoHideTimer;
20383        }
20384    }
20385
20386    // private
20387    function onBeforeShow(m){
20388        var pm = m.parentMenu;
20389        if(!pm && !m.allowOtherMenus){
20390            hideAll();
20391        }else if(pm && pm.activeChild && active != m){
20392            pm.activeChild.hide();
20393        }
20394    }
20395
20396    // private
20397    function onMouseDown(e){
20398        if(lastShow.getElapsed() > 50 && active.length > 0 && !e.getTarget(".x-menu")){
20399            hideAll();
20400        }
20401    }
20402
20403    // private
20404    function onBeforeCheck(mi, state){
20405        if(state){
20406            var g = groups[mi.group];
20407            for(var i = 0, l = g.length; i < l; i++){
20408                if(g[i] != mi){
20409                    g[i].setChecked(false);
20410                }
20411            }
20412        }
20413    }
20414
20415    return {
20416
20417        /**
20418         * Hides all menus that are currently visible
20419         */
20420        hideAll : function(){
20421             hideAll();  
20422        },
20423
20424        // private
20425        register : function(menu){
20426            if(!menus){
20427                init();
20428            }
20429            menus[menu.id] = menu;
20430            menu.on("beforehide", onBeforeHide);
20431            menu.on("hide", onHide);
20432            menu.on("beforeshow", onBeforeShow);
20433            menu.on("show", onShow);
20434            var g = menu.group;
20435            if(g && menu.events["checkchange"]){
20436                if(!groups[g]){
20437                    groups[g] = [];
20438                }
20439                groups[g].push(menu);
20440                menu.on("checkchange", onCheck);
20441            }
20442        },
20443
20444         /**
20445          * Returns a {@link Roo.menu.Menu} object
20446          * @param {String/Object} menu The string menu id, an existing menu object reference, or a Menu config that will
20447          * be used to generate and return a new Menu instance.
20448          */
20449        get : function(menu){
20450            if(typeof menu == "string"){ // menu id
20451                return menus[menu];
20452            }else if(menu.events){  // menu instance
20453                return menu;
20454            }else if(typeof menu.length == 'number'){ // array of menu items?
20455                return new Roo.menu.Menu({items:menu});
20456            }else{ // otherwise, must be a config
20457                return new Roo.menu.Menu(menu);
20458            }
20459        },
20460
20461        // private
20462        unregister : function(menu){
20463            delete menus[menu.id];
20464            menu.un("beforehide", onBeforeHide);
20465            menu.un("hide", onHide);
20466            menu.un("beforeshow", onBeforeShow);
20467            menu.un("show", onShow);
20468            var g = menu.group;
20469            if(g && menu.events["checkchange"]){
20470                groups[g].remove(menu);
20471                menu.un("checkchange", onCheck);
20472            }
20473        },
20474
20475        // private
20476        registerCheckable : function(menuItem){
20477            var g = menuItem.group;
20478            if(g){
20479                if(!groups[g]){
20480                    groups[g] = [];
20481                }
20482                groups[g].push(menuItem);
20483                menuItem.on("beforecheckchange", onBeforeCheck);
20484            }
20485        },
20486
20487        // private
20488        unregisterCheckable : function(menuItem){
20489            var g = menuItem.group;
20490            if(g){
20491                groups[g].remove(menuItem);
20492                menuItem.un("beforecheckchange", onBeforeCheck);
20493            }
20494        }
20495    };
20496 }();/*
20497  * Based on:
20498  * Ext JS Library 1.1.1
20499  * Copyright(c) 2006-2007, Ext JS, LLC.
20500  *
20501  * Originally Released Under LGPL - original licence link has changed is not relivant.
20502  *
20503  * Fork - LGPL
20504  * <script type="text/javascript">
20505  */
20506  
20507
20508 /**
20509  * @class Roo.menu.BaseItem
20510  * @extends Roo.Component
20511  * The base class for all items that render into menus.  BaseItem provides default rendering, activated state
20512  * management and base configuration options shared by all menu components.
20513  * @constructor
20514  * Creates a new BaseItem
20515  * @param {Object} config Configuration options
20516  */
20517 Roo.menu.BaseItem = function(config){
20518     Roo.menu.BaseItem.superclass.constructor.call(this, config);
20519
20520     this.addEvents({
20521         /**
20522          * @event click
20523          * Fires when this item is clicked
20524          * @param {Roo.menu.BaseItem} this
20525          * @param {Roo.EventObject} e
20526          */
20527         click: true,
20528         /**
20529          * @event activate
20530          * Fires when this item is activated
20531          * @param {Roo.menu.BaseItem} this
20532          */
20533         activate : true,
20534         /**
20535          * @event deactivate
20536          * Fires when this item is deactivated
20537          * @param {Roo.menu.BaseItem} this
20538          */
20539         deactivate : true
20540     });
20541
20542     if(this.handler){
20543         this.on("click", this.handler, this.scope, true);
20544     }
20545 };
20546
20547 Roo.extend(Roo.menu.BaseItem, Roo.Component, {
20548     /**
20549      * @cfg {Function} handler
20550      * A function that will handle the click event of this menu item (defaults to undefined)
20551      */
20552     /**
20553      * @cfg {Boolean} canActivate True if this item can be visually activated (defaults to false)
20554      */
20555     canActivate : false,
20556     
20557      /**
20558      * @cfg {Boolean} hidden True to prevent creation of this menu item (defaults to false)
20559      */
20560     hidden: false,
20561     
20562     /**
20563      * @cfg {String} activeClass The CSS class to use when the item becomes activated (defaults to "x-menu-item-active")
20564      */
20565     activeClass : "x-menu-item-active",
20566     /**
20567      * @cfg {Boolean} hideOnClick True to hide the containing menu after this item is clicked (defaults to true)
20568      */
20569     hideOnClick : true,
20570     /**
20571      * @cfg {Number} hideDelay Length of time in milliseconds to wait before hiding after a click (defaults to 100)
20572      */
20573     hideDelay : 100,
20574
20575     // private
20576     ctype: "Roo.menu.BaseItem",
20577
20578     // private
20579     actionMode : "container",
20580
20581     // private
20582     render : function(container, parentMenu){
20583         this.parentMenu = parentMenu;
20584         Roo.menu.BaseItem.superclass.render.call(this, container);
20585         this.container.menuItemId = this.id;
20586     },
20587
20588     // private
20589     onRender : function(container, position){
20590         this.el = Roo.get(this.el);
20591         container.dom.appendChild(this.el.dom);
20592     },
20593
20594     // private
20595     onClick : function(e){
20596         if(!this.disabled && this.fireEvent("click", this, e) !== false
20597                 && this.parentMenu.fireEvent("itemclick", this, e) !== false){
20598             this.handleClick(e);
20599         }else{
20600             e.stopEvent();
20601         }
20602     },
20603
20604     // private
20605     activate : function(){
20606         if(this.disabled){
20607             return false;
20608         }
20609         var li = this.container;
20610         li.addClass(this.activeClass);
20611         this.region = li.getRegion().adjust(2, 2, -2, -2);
20612         this.fireEvent("activate", this);
20613         return true;
20614     },
20615
20616     // private
20617     deactivate : function(){
20618         this.container.removeClass(this.activeClass);
20619         this.fireEvent("deactivate", this);
20620     },
20621
20622     // private
20623     shouldDeactivate : function(e){
20624         return !this.region || !this.region.contains(e.getPoint());
20625     },
20626
20627     // private
20628     handleClick : function(e){
20629         if(this.hideOnClick){
20630             this.parentMenu.hide.defer(this.hideDelay, this.parentMenu, [true]);
20631         }
20632     },
20633
20634     // private
20635     expandMenu : function(autoActivate){
20636         // do nothing
20637     },
20638
20639     // private
20640     hideMenu : function(){
20641         // do nothing
20642     }
20643 });/*
20644  * Based on:
20645  * Ext JS Library 1.1.1
20646  * Copyright(c) 2006-2007, Ext JS, LLC.
20647  *
20648  * Originally Released Under LGPL - original licence link has changed is not relivant.
20649  *
20650  * Fork - LGPL
20651  * <script type="text/javascript">
20652  */
20653  
20654 /**
20655  * @class Roo.menu.Adapter
20656  * @extends Roo.menu.BaseItem
20657  * 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.
20658  * It provides basic rendering, activation management and enable/disable logic required to work in menus.
20659  * @constructor
20660  * Creates a new Adapter
20661  * @param {Object} config Configuration options
20662  */
20663 Roo.menu.Adapter = function(component, config){
20664     Roo.menu.Adapter.superclass.constructor.call(this, config);
20665     this.component = component;
20666 };
20667 Roo.extend(Roo.menu.Adapter, Roo.menu.BaseItem, {
20668     // private
20669     canActivate : true,
20670
20671     // private
20672     onRender : function(container, position){
20673         this.component.render(container);
20674         this.el = this.component.getEl();
20675     },
20676
20677     // private
20678     activate : function(){
20679         if(this.disabled){
20680             return false;
20681         }
20682         this.component.focus();
20683         this.fireEvent("activate", this);
20684         return true;
20685     },
20686
20687     // private
20688     deactivate : function(){
20689         this.fireEvent("deactivate", this);
20690     },
20691
20692     // private
20693     disable : function(){
20694         this.component.disable();
20695         Roo.menu.Adapter.superclass.disable.call(this);
20696     },
20697
20698     // private
20699     enable : function(){
20700         this.component.enable();
20701         Roo.menu.Adapter.superclass.enable.call(this);
20702     }
20703 });/*
20704  * Based on:
20705  * Ext JS Library 1.1.1
20706  * Copyright(c) 2006-2007, Ext JS, LLC.
20707  *
20708  * Originally Released Under LGPL - original licence link has changed is not relivant.
20709  *
20710  * Fork - LGPL
20711  * <script type="text/javascript">
20712  */
20713
20714 /**
20715  * @class Roo.menu.TextItem
20716  * @extends Roo.menu.BaseItem
20717  * Adds a static text string to a menu, usually used as either a heading or group separator.
20718  * Note: old style constructor with text is still supported.
20719  * 
20720  * @constructor
20721  * Creates a new TextItem
20722  * @param {Object} cfg Configuration
20723  */
20724 Roo.menu.TextItem = function(cfg){
20725     if (typeof(cfg) == 'string') {
20726         this.text = cfg;
20727     } else {
20728         Roo.apply(this,cfg);
20729     }
20730     
20731     Roo.menu.TextItem.superclass.constructor.call(this);
20732 };
20733
20734 Roo.extend(Roo.menu.TextItem, Roo.menu.BaseItem, {
20735     /**
20736      * @cfg {Boolean} text Text to show on item.
20737      */
20738     text : '',
20739     
20740     /**
20741      * @cfg {Boolean} hideOnClick True to hide the containing menu after this item is clicked (defaults to false)
20742      */
20743     hideOnClick : false,
20744     /**
20745      * @cfg {String} itemCls The default CSS class to use for text items (defaults to "x-menu-text")
20746      */
20747     itemCls : "x-menu-text",
20748
20749     // private
20750     onRender : function(){
20751         var s = document.createElement("span");
20752         s.className = this.itemCls;
20753         s.innerHTML = this.text;
20754         this.el = s;
20755         Roo.menu.TextItem.superclass.onRender.apply(this, arguments);
20756     }
20757 });/*
20758  * Based on:
20759  * Ext JS Library 1.1.1
20760  * Copyright(c) 2006-2007, Ext JS, LLC.
20761  *
20762  * Originally Released Under LGPL - original licence link has changed is not relivant.
20763  *
20764  * Fork - LGPL
20765  * <script type="text/javascript">
20766  */
20767
20768 /**
20769  * @class Roo.menu.Separator
20770  * @extends Roo.menu.BaseItem
20771  * Adds a separator bar to a menu, used to divide logical groups of menu items. Generally you will
20772  * add one of these by using "-" in you call to add() or in your items config rather than creating one directly.
20773  * @constructor
20774  * @param {Object} config Configuration options
20775  */
20776 Roo.menu.Separator = function(config){
20777     Roo.menu.Separator.superclass.constructor.call(this, config);
20778 };
20779
20780 Roo.extend(Roo.menu.Separator, Roo.menu.BaseItem, {
20781     /**
20782      * @cfg {String} itemCls The default CSS class to use for separators (defaults to "x-menu-sep")
20783      */
20784     itemCls : "x-menu-sep",
20785     /**
20786      * @cfg {Boolean} hideOnClick True to hide the containing menu after this item is clicked (defaults to false)
20787      */
20788     hideOnClick : false,
20789
20790     // private
20791     onRender : function(li){
20792         var s = document.createElement("span");
20793         s.className = this.itemCls;
20794         s.innerHTML = "&#160;";
20795         this.el = s;
20796         li.addClass("x-menu-sep-li");
20797         Roo.menu.Separator.superclass.onRender.apply(this, arguments);
20798     }
20799 });/*
20800  * Based on:
20801  * Ext JS Library 1.1.1
20802  * Copyright(c) 2006-2007, Ext JS, LLC.
20803  *
20804  * Originally Released Under LGPL - original licence link has changed is not relivant.
20805  *
20806  * Fork - LGPL
20807  * <script type="text/javascript">
20808  */
20809 /**
20810  * @class Roo.menu.Item
20811  * @extends Roo.menu.BaseItem
20812  * A base class for all menu items that require menu-related functionality (like sub-menus) and are not static
20813  * display items.  Item extends the base functionality of {@link Roo.menu.BaseItem} by adding menu-specific
20814  * activation and click handling.
20815  * @constructor
20816  * Creates a new Item
20817  * @param {Object} config Configuration options
20818  */
20819 Roo.menu.Item = function(config){
20820     Roo.menu.Item.superclass.constructor.call(this, config);
20821     if(this.menu){
20822         this.menu = Roo.menu.MenuMgr.get(this.menu);
20823     }
20824 };
20825 Roo.extend(Roo.menu.Item, Roo.menu.BaseItem, {
20826     
20827     /**
20828      * @cfg {String} text
20829      * The text to show on the menu item.
20830      */
20831     text: '',
20832      /**
20833      * @cfg {String} HTML to render in menu
20834      * The text to show on the menu item (HTML version).
20835      */
20836     html: '',
20837     /**
20838      * @cfg {String} icon
20839      * The path to an icon to display in this menu item (defaults to Roo.BLANK_IMAGE_URL)
20840      */
20841     icon: undefined,
20842     /**
20843      * @cfg {String} itemCls The default CSS class to use for menu items (defaults to "x-menu-item")
20844      */
20845     itemCls : "x-menu-item",
20846     /**
20847      * @cfg {Boolean} canActivate True if this item can be visually activated (defaults to true)
20848      */
20849     canActivate : true,
20850     /**
20851      * @cfg {Number} showDelay Length of time in milliseconds to wait before showing this item (defaults to 200)
20852      */
20853     showDelay: 200,
20854     // doc'd in BaseItem
20855     hideDelay: 200,
20856
20857     // private
20858     ctype: "Roo.menu.Item",
20859     
20860     // private
20861     onRender : function(container, position){
20862         var el = document.createElement("a");
20863         el.hideFocus = true;
20864         el.unselectable = "on";
20865         el.href = this.href || "#";
20866         if(this.hrefTarget){
20867             el.target = this.hrefTarget;
20868         }
20869         el.className = this.itemCls + (this.menu ?  " x-menu-item-arrow" : "") + (this.cls ?  " " + this.cls : "");
20870         
20871         var html = this.html.length ? this.html  : String.format('{0}',this.text);
20872         
20873         el.innerHTML = String.format(
20874                 '<img src="{0}" class="x-menu-item-icon {1}" />' + html,
20875                 this.icon || Roo.BLANK_IMAGE_URL, this.iconCls || '');
20876         this.el = el;
20877         Roo.menu.Item.superclass.onRender.call(this, container, position);
20878     },
20879
20880     /**
20881      * Sets the text to display in this menu item
20882      * @param {String} text The text to display
20883      * @param {Boolean} isHTML true to indicate text is pure html.
20884      */
20885     setText : function(text, isHTML){
20886         if (isHTML) {
20887             this.html = text;
20888         } else {
20889             this.text = text;
20890             this.html = '';
20891         }
20892         if(this.rendered){
20893             var html = this.html.length ? this.html  : String.format('{0}',this.text);
20894      
20895             this.el.update(String.format(
20896                 '<img src="{0}" class="x-menu-item-icon {2}">' + html,
20897                 this.icon || Roo.BLANK_IMAGE_URL, this.text, this.iconCls || ''));
20898             this.parentMenu.autoWidth();
20899         }
20900     },
20901
20902     // private
20903     handleClick : function(e){
20904         if(!this.href){ // if no link defined, stop the event automatically
20905             e.stopEvent();
20906         }
20907         Roo.menu.Item.superclass.handleClick.apply(this, arguments);
20908     },
20909
20910     // private
20911     activate : function(autoExpand){
20912         if(Roo.menu.Item.superclass.activate.apply(this, arguments)){
20913             this.focus();
20914             if(autoExpand){
20915                 this.expandMenu();
20916             }
20917         }
20918         return true;
20919     },
20920
20921     // private
20922     shouldDeactivate : function(e){
20923         if(Roo.menu.Item.superclass.shouldDeactivate.call(this, e)){
20924             if(this.menu && this.menu.isVisible()){
20925                 return !this.menu.getEl().getRegion().contains(e.getPoint());
20926             }
20927             return true;
20928         }
20929         return false;
20930     },
20931
20932     // private
20933     deactivate : function(){
20934         Roo.menu.Item.superclass.deactivate.apply(this, arguments);
20935         this.hideMenu();
20936     },
20937
20938     // private
20939     expandMenu : function(autoActivate){
20940         if(!this.disabled && this.menu){
20941             clearTimeout(this.hideTimer);
20942             delete this.hideTimer;
20943             if(!this.menu.isVisible() && !this.showTimer){
20944                 this.showTimer = this.deferExpand.defer(this.showDelay, this, [autoActivate]);
20945             }else if (this.menu.isVisible() && autoActivate){
20946                 this.menu.tryActivate(0, 1);
20947             }
20948         }
20949     },
20950
20951     // private
20952     deferExpand : function(autoActivate){
20953         delete this.showTimer;
20954         this.menu.show(this.container, this.parentMenu.subMenuAlign || "tl-tr?", this.parentMenu);
20955         if(autoActivate){
20956             this.menu.tryActivate(0, 1);
20957         }
20958     },
20959
20960     // private
20961     hideMenu : function(){
20962         clearTimeout(this.showTimer);
20963         delete this.showTimer;
20964         if(!this.hideTimer && this.menu && this.menu.isVisible()){
20965             this.hideTimer = this.deferHide.defer(this.hideDelay, this);
20966         }
20967     },
20968
20969     // private
20970     deferHide : function(){
20971         delete this.hideTimer;
20972         this.menu.hide();
20973     }
20974 });/*
20975  * Based on:
20976  * Ext JS Library 1.1.1
20977  * Copyright(c) 2006-2007, Ext JS, LLC.
20978  *
20979  * Originally Released Under LGPL - original licence link has changed is not relivant.
20980  *
20981  * Fork - LGPL
20982  * <script type="text/javascript">
20983  */
20984  
20985 /**
20986  * @class Roo.menu.CheckItem
20987  * @extends Roo.menu.Item
20988  * Adds a menu item that contains a checkbox by default, but can also be part of a radio group.
20989  * @constructor
20990  * Creates a new CheckItem
20991  * @param {Object} config Configuration options
20992  */
20993 Roo.menu.CheckItem = function(config){
20994     Roo.menu.CheckItem.superclass.constructor.call(this, config);
20995     this.addEvents({
20996         /**
20997          * @event beforecheckchange
20998          * Fires before the checked value is set, providing an opportunity to cancel if needed
20999          * @param {Roo.menu.CheckItem} this
21000          * @param {Boolean} checked The new checked value that will be set
21001          */
21002         "beforecheckchange" : true,
21003         /**
21004          * @event checkchange
21005          * Fires after the checked value has been set
21006          * @param {Roo.menu.CheckItem} this
21007          * @param {Boolean} checked The checked value that was set
21008          */
21009         "checkchange" : true
21010     });
21011     if(this.checkHandler){
21012         this.on('checkchange', this.checkHandler, this.scope);
21013     }
21014 };
21015 Roo.extend(Roo.menu.CheckItem, Roo.menu.Item, {
21016     /**
21017      * @cfg {String} group
21018      * All check items with the same group name will automatically be grouped into a single-select
21019      * radio button group (defaults to '')
21020      */
21021     /**
21022      * @cfg {String} itemCls The default CSS class to use for check items (defaults to "x-menu-item x-menu-check-item")
21023      */
21024     itemCls : "x-menu-item x-menu-check-item",
21025     /**
21026      * @cfg {String} groupClass The default CSS class to use for radio group check items (defaults to "x-menu-group-item")
21027      */
21028     groupClass : "x-menu-group-item",
21029
21030     /**
21031      * @cfg {Boolean} checked True to initialize this checkbox as checked (defaults to false).  Note that
21032      * if this checkbox is part of a radio group (group = true) only the last item in the group that is
21033      * initialized with checked = true will be rendered as checked.
21034      */
21035     checked: false,
21036
21037     // private
21038     ctype: "Roo.menu.CheckItem",
21039
21040     // private
21041     onRender : function(c){
21042         Roo.menu.CheckItem.superclass.onRender.apply(this, arguments);
21043         if(this.group){
21044             this.el.addClass(this.groupClass);
21045         }
21046         Roo.menu.MenuMgr.registerCheckable(this);
21047         if(this.checked){
21048             this.checked = false;
21049             this.setChecked(true, true);
21050         }
21051     },
21052
21053     // private
21054     destroy : function(){
21055         if(this.rendered){
21056             Roo.menu.MenuMgr.unregisterCheckable(this);
21057         }
21058         Roo.menu.CheckItem.superclass.destroy.apply(this, arguments);
21059     },
21060
21061     /**
21062      * Set the checked state of this item
21063      * @param {Boolean} checked The new checked value
21064      * @param {Boolean} suppressEvent (optional) True to prevent the checkchange event from firing (defaults to false)
21065      */
21066     setChecked : function(state, suppressEvent){
21067         if(this.checked != state && this.fireEvent("beforecheckchange", this, state) !== false){
21068             if(this.container){
21069                 this.container[state ? "addClass" : "removeClass"]("x-menu-item-checked");
21070             }
21071             this.checked = state;
21072             if(suppressEvent !== true){
21073                 this.fireEvent("checkchange", this, state);
21074             }
21075         }
21076     },
21077
21078     // private
21079     handleClick : function(e){
21080        if(!this.disabled && !(this.checked && this.group)){// disable unselect on radio item
21081            this.setChecked(!this.checked);
21082        }
21083        Roo.menu.CheckItem.superclass.handleClick.apply(this, arguments);
21084     }
21085 });/*
21086  * Based on:
21087  * Ext JS Library 1.1.1
21088  * Copyright(c) 2006-2007, Ext JS, LLC.
21089  *
21090  * Originally Released Under LGPL - original licence link has changed is not relivant.
21091  *
21092  * Fork - LGPL
21093  * <script type="text/javascript">
21094  */
21095  
21096 /**
21097  * @class Roo.menu.DateItem
21098  * @extends Roo.menu.Adapter
21099  * A menu item that wraps the {@link Roo.DatPicker} component.
21100  * @constructor
21101  * Creates a new DateItem
21102  * @param {Object} config Configuration options
21103  */
21104 Roo.menu.DateItem = function(config){
21105     Roo.menu.DateItem.superclass.constructor.call(this, new Roo.DatePicker(config), config);
21106     /** The Roo.DatePicker object @type Roo.DatePicker */
21107     this.picker = this.component;
21108     this.addEvents({select: true});
21109     
21110     this.picker.on("render", function(picker){
21111         picker.getEl().swallowEvent("click");
21112         picker.container.addClass("x-menu-date-item");
21113     });
21114
21115     this.picker.on("select", this.onSelect, this);
21116 };
21117
21118 Roo.extend(Roo.menu.DateItem, Roo.menu.Adapter, {
21119     // private
21120     onSelect : function(picker, date){
21121         this.fireEvent("select", this, date, picker);
21122         Roo.menu.DateItem.superclass.handleClick.call(this);
21123     }
21124 });/*
21125  * Based on:
21126  * Ext JS Library 1.1.1
21127  * Copyright(c) 2006-2007, Ext JS, LLC.
21128  *
21129  * Originally Released Under LGPL - original licence link has changed is not relivant.
21130  *
21131  * Fork - LGPL
21132  * <script type="text/javascript">
21133  */
21134  
21135 /**
21136  * @class Roo.menu.ColorItem
21137  * @extends Roo.menu.Adapter
21138  * A menu item that wraps the {@link Roo.ColorPalette} component.
21139  * @constructor
21140  * Creates a new ColorItem
21141  * @param {Object} config Configuration options
21142  */
21143 Roo.menu.ColorItem = function(config){
21144     Roo.menu.ColorItem.superclass.constructor.call(this, new Roo.ColorPalette(config), config);
21145     /** The Roo.ColorPalette object @type Roo.ColorPalette */
21146     this.palette = this.component;
21147     this.relayEvents(this.palette, ["select"]);
21148     if(this.selectHandler){
21149         this.on('select', this.selectHandler, this.scope);
21150     }
21151 };
21152 Roo.extend(Roo.menu.ColorItem, Roo.menu.Adapter);/*
21153  * Based on:
21154  * Ext JS Library 1.1.1
21155  * Copyright(c) 2006-2007, Ext JS, LLC.
21156  *
21157  * Originally Released Under LGPL - original licence link has changed is not relivant.
21158  *
21159  * Fork - LGPL
21160  * <script type="text/javascript">
21161  */
21162  
21163
21164 /**
21165  * @class Roo.menu.DateMenu
21166  * @extends Roo.menu.Menu
21167  * A menu containing a {@link Roo.menu.DateItem} component (which provides a date picker).
21168  * @constructor
21169  * Creates a new DateMenu
21170  * @param {Object} config Configuration options
21171  */
21172 Roo.menu.DateMenu = function(config){
21173     Roo.menu.DateMenu.superclass.constructor.call(this, config);
21174     this.plain = true;
21175     var di = new Roo.menu.DateItem(config);
21176     this.add(di);
21177     /**
21178      * The {@link Roo.DatePicker} instance for this DateMenu
21179      * @type DatePicker
21180      */
21181     this.picker = di.picker;
21182     /**
21183      * @event select
21184      * @param {DatePicker} picker
21185      * @param {Date} date
21186      */
21187     this.relayEvents(di, ["select"]);
21188     this.on('beforeshow', function(){
21189         if(this.picker){
21190             this.picker.hideMonthPicker(false);
21191         }
21192     }, this);
21193 };
21194 Roo.extend(Roo.menu.DateMenu, Roo.menu.Menu, {
21195     cls:'x-date-menu'
21196 });/*
21197  * Based on:
21198  * Ext JS Library 1.1.1
21199  * Copyright(c) 2006-2007, Ext JS, LLC.
21200  *
21201  * Originally Released Under LGPL - original licence link has changed is not relivant.
21202  *
21203  * Fork - LGPL
21204  * <script type="text/javascript">
21205  */
21206  
21207
21208 /**
21209  * @class Roo.menu.ColorMenu
21210  * @extends Roo.menu.Menu
21211  * A menu containing a {@link Roo.menu.ColorItem} component (which provides a basic color picker).
21212  * @constructor
21213  * Creates a new ColorMenu
21214  * @param {Object} config Configuration options
21215  */
21216 Roo.menu.ColorMenu = function(config){
21217     Roo.menu.ColorMenu.superclass.constructor.call(this, config);
21218     this.plain = true;
21219     var ci = new Roo.menu.ColorItem(config);
21220     this.add(ci);
21221     /**
21222      * The {@link Roo.ColorPalette} instance for this ColorMenu
21223      * @type ColorPalette
21224      */
21225     this.palette = ci.palette;
21226     /**
21227      * @event select
21228      * @param {ColorPalette} palette
21229      * @param {String} color
21230      */
21231     this.relayEvents(ci, ["select"]);
21232 };
21233 Roo.extend(Roo.menu.ColorMenu, Roo.menu.Menu);/*
21234  * Based on:
21235  * Ext JS Library 1.1.1
21236  * Copyright(c) 2006-2007, Ext JS, LLC.
21237  *
21238  * Originally Released Under LGPL - original licence link has changed is not relivant.
21239  *
21240  * Fork - LGPL
21241  * <script type="text/javascript">
21242  */
21243  
21244 /**
21245  * @class Roo.form.Field
21246  * @extends Roo.BoxComponent
21247  * Base class for form fields that provides default event handling, sizing, value handling and other functionality.
21248  * @constructor
21249  * Creates a new Field
21250  * @param {Object} config Configuration options
21251  */
21252 Roo.form.Field = function(config){
21253     Roo.form.Field.superclass.constructor.call(this, config);
21254 };
21255
21256 Roo.extend(Roo.form.Field, Roo.BoxComponent,  {
21257     /**
21258      * @cfg {String} fieldLabel Label to use when rendering a form.
21259      */
21260        /**
21261      * @cfg {String} qtip Mouse over tip
21262      */
21263      
21264     /**
21265      * @cfg {String} invalidClass The CSS class to use when marking a field invalid (defaults to "x-form-invalid")
21266      */
21267     invalidClass : "x-form-invalid",
21268     /**
21269      * @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")
21270      */
21271     invalidText : "The value in this field is invalid",
21272     /**
21273      * @cfg {String} focusClass The CSS class to use when the field receives focus (defaults to "x-form-focus")
21274      */
21275     focusClass : "x-form-focus",
21276     /**
21277      * @cfg {String/Boolean} validationEvent The event that should initiate field validation. Set to false to disable
21278       automatic validation (defaults to "keyup").
21279      */
21280     validationEvent : "keyup",
21281     /**
21282      * @cfg {Boolean} validateOnBlur Whether the field should validate when it loses focus (defaults to true).
21283      */
21284     validateOnBlur : true,
21285     /**
21286      * @cfg {Number} validationDelay The length of time in milliseconds after user input begins until validation is initiated (defaults to 250)
21287      */
21288     validationDelay : 250,
21289     /**
21290      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
21291      * {tag: "input", type: "text", size: "20", autocomplete: "off"})
21292      */
21293     defaultAutoCreate : {tag: "input", type: "text", size: "20", autocomplete: "off"},
21294     /**
21295      * @cfg {String} fieldClass The default CSS class for the field (defaults to "x-form-field")
21296      */
21297     fieldClass : "x-form-field",
21298     /**
21299      * @cfg {String} msgTarget The location where error text should display.  Should be one of the following values (defaults to 'qtip'):
21300      *<pre>
21301 Value         Description
21302 -----------   ----------------------------------------------------------------------
21303 qtip          Display a quick tip when the user hovers over the field
21304 title         Display a default browser title attribute popup
21305 under         Add a block div beneath the field containing the error text
21306 side          Add an error icon to the right of the field with a popup on hover
21307 [element id]  Add the error text directly to the innerHTML of the specified element
21308 </pre>
21309      */
21310     msgTarget : 'qtip',
21311     /**
21312      * @cfg {String} msgFx <b>Experimental</b> The effect used when displaying a validation message under the field (defaults to 'normal').
21313      */
21314     msgFx : 'normal',
21315
21316     /**
21317      * @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.
21318      */
21319     readOnly : false,
21320
21321     /**
21322      * @cfg {Boolean} disabled True to disable the field (defaults to false).
21323      */
21324     disabled : false,
21325
21326     /**
21327      * @cfg {String} inputType The type attribute for input fields -- e.g. radio, text, password (defaults to "text").
21328      */
21329     inputType : undefined,
21330     
21331     /**
21332      * @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).
21333          */
21334         tabIndex : undefined,
21335         
21336     // private
21337     isFormField : true,
21338
21339     // private
21340     hasFocus : false,
21341     /**
21342      * @property {Roo.Element} fieldEl
21343      * Element Containing the rendered Field (with label etc.)
21344      */
21345     /**
21346      * @cfg {Mixed} value A value to initialize this field with.
21347      */
21348     value : undefined,
21349
21350     /**
21351      * @cfg {String} name The field's HTML name attribute.
21352      */
21353     /**
21354      * @cfg {String} cls A CSS class to apply to the field's underlying element.
21355      */
21356
21357         // private ??
21358         initComponent : function(){
21359         Roo.form.Field.superclass.initComponent.call(this);
21360         this.addEvents({
21361             /**
21362              * @event focus
21363              * Fires when this field receives input focus.
21364              * @param {Roo.form.Field} this
21365              */
21366             focus : true,
21367             /**
21368              * @event blur
21369              * Fires when this field loses input focus.
21370              * @param {Roo.form.Field} this
21371              */
21372             blur : true,
21373             /**
21374              * @event specialkey
21375              * Fires when any key related to navigation (arrows, tab, enter, esc, etc.) is pressed.  You can check
21376              * {@link Roo.EventObject#getKey} to determine which key was pressed.
21377              * @param {Roo.form.Field} this
21378              * @param {Roo.EventObject} e The event object
21379              */
21380             specialkey : true,
21381             /**
21382              * @event change
21383              * Fires just before the field blurs if the field value has changed.
21384              * @param {Roo.form.Field} this
21385              * @param {Mixed} newValue The new value
21386              * @param {Mixed} oldValue The original value
21387              */
21388             change : true,
21389             /**
21390              * @event invalid
21391              * Fires after the field has been marked as invalid.
21392              * @param {Roo.form.Field} this
21393              * @param {String} msg The validation message
21394              */
21395             invalid : true,
21396             /**
21397              * @event valid
21398              * Fires after the field has been validated with no errors.
21399              * @param {Roo.form.Field} this
21400              */
21401             valid : true,
21402              /**
21403              * @event keyup
21404              * Fires after the key up
21405              * @param {Roo.form.Field} this
21406              * @param {Roo.EventObject}  e The event Object
21407              */
21408             keyup : true
21409         });
21410     },
21411
21412     /**
21413      * Returns the name attribute of the field if available
21414      * @return {String} name The field name
21415      */
21416     getName: function(){
21417          return this.rendered && this.el.dom.name ? this.el.dom.name : (this.hiddenName || '');
21418     },
21419
21420     // private
21421     onRender : function(ct, position){
21422         Roo.form.Field.superclass.onRender.call(this, ct, position);
21423         if(!this.el){
21424             var cfg = this.getAutoCreate();
21425             if(!cfg.name){
21426                 cfg.name = typeof(this.name) == 'undefined' ? this.id : this.name;
21427             }
21428             if (!cfg.name.length) {
21429                 delete cfg.name;
21430             }
21431             if(this.inputType){
21432                 cfg.type = this.inputType;
21433             }
21434             this.el = ct.createChild(cfg, position);
21435         }
21436         var type = this.el.dom.type;
21437         if(type){
21438             if(type == 'password'){
21439                 type = 'text';
21440             }
21441             this.el.addClass('x-form-'+type);
21442         }
21443         if(this.readOnly){
21444             this.el.dom.readOnly = true;
21445         }
21446         if(this.tabIndex !== undefined){
21447             this.el.dom.setAttribute('tabIndex', this.tabIndex);
21448         }
21449
21450         this.el.addClass([this.fieldClass, this.cls]);
21451         this.initValue();
21452     },
21453
21454     /**
21455      * Apply the behaviors of this component to an existing element. <b>This is used instead of render().</b>
21456      * @param {String/HTMLElement/Element} el The id of the node, a DOM node or an existing Element
21457      * @return {Roo.form.Field} this
21458      */
21459     applyTo : function(target){
21460         this.allowDomMove = false;
21461         this.el = Roo.get(target);
21462         this.render(this.el.dom.parentNode);
21463         return this;
21464     },
21465
21466     // private
21467     initValue : function(){
21468         if(this.value !== undefined){
21469             this.setValue(this.value);
21470         }else if(this.el.dom.value.length > 0){
21471             this.setValue(this.el.dom.value);
21472         }
21473     },
21474
21475     /**
21476      * Returns true if this field has been changed since it was originally loaded and is not disabled.
21477      */
21478     isDirty : function() {
21479         if(this.disabled) {
21480             return false;
21481         }
21482         return String(this.getValue()) !== String(this.originalValue);
21483     },
21484
21485     // private
21486     afterRender : function(){
21487         Roo.form.Field.superclass.afterRender.call(this);
21488         this.initEvents();
21489     },
21490
21491     // private
21492     fireKey : function(e){
21493         //Roo.log('field ' + e.getKey());
21494         if(e.isNavKeyPress()){
21495             this.fireEvent("specialkey", this, e);
21496         }
21497     },
21498
21499     /**
21500      * Resets the current field value to the originally loaded value and clears any validation messages
21501      */
21502     reset : function(){
21503         this.setValue(this.originalValue);
21504         this.clearInvalid();
21505     },
21506
21507     // private
21508     initEvents : function(){
21509         // safari killled keypress - so keydown is now used..
21510         this.el.on("keydown" , this.fireKey,  this);
21511         this.el.on("focus", this.onFocus,  this);
21512         this.el.on("blur", this.onBlur,  this);
21513         this.el.relayEvent('keyup', this);
21514
21515         // reference to original value for reset
21516         this.originalValue = this.getValue();
21517     },
21518
21519     // private
21520     onFocus : function(){
21521         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
21522             this.el.addClass(this.focusClass);
21523         }
21524         if(!this.hasFocus){
21525             this.hasFocus = true;
21526             this.startValue = this.getValue();
21527             this.fireEvent("focus", this);
21528         }
21529     },
21530
21531     beforeBlur : Roo.emptyFn,
21532
21533     // private
21534     onBlur : function(){
21535         this.beforeBlur();
21536         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
21537             this.el.removeClass(this.focusClass);
21538         }
21539         this.hasFocus = false;
21540         if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
21541             this.validate();
21542         }
21543         var v = this.getValue();
21544         if(String(v) !== String(this.startValue)){
21545             this.fireEvent('change', this, v, this.startValue);
21546         }
21547         this.fireEvent("blur", this);
21548     },
21549
21550     /**
21551      * Returns whether or not the field value is currently valid
21552      * @param {Boolean} preventMark True to disable marking the field invalid
21553      * @return {Boolean} True if the value is valid, else false
21554      */
21555     isValid : function(preventMark){
21556         if(this.disabled){
21557             return true;
21558         }
21559         var restore = this.preventMark;
21560         this.preventMark = preventMark === true;
21561         var v = this.validateValue(this.processValue(this.getRawValue()));
21562         this.preventMark = restore;
21563         return v;
21564     },
21565
21566     /**
21567      * Validates the field value
21568      * @return {Boolean} True if the value is valid, else false
21569      */
21570     validate : function(){
21571         if(this.disabled || this.validateValue(this.processValue(this.getRawValue()))){
21572             this.clearInvalid();
21573             return true;
21574         }
21575         return false;
21576     },
21577
21578     processValue : function(value){
21579         return value;
21580     },
21581
21582     // private
21583     // Subclasses should provide the validation implementation by overriding this
21584     validateValue : function(value){
21585         return true;
21586     },
21587
21588     /**
21589      * Mark this field as invalid
21590      * @param {String} msg The validation message
21591      */
21592     markInvalid : function(msg){
21593         if(!this.rendered || this.preventMark){ // not rendered
21594             return;
21595         }
21596         this.el.addClass(this.invalidClass);
21597         msg = msg || this.invalidText;
21598         switch(this.msgTarget){
21599             case 'qtip':
21600                 this.el.dom.qtip = msg;
21601                 this.el.dom.qclass = 'x-form-invalid-tip';
21602                 if(Roo.QuickTips){ // fix for floating editors interacting with DND
21603                     Roo.QuickTips.enable();
21604                 }
21605                 break;
21606             case 'title':
21607                 this.el.dom.title = msg;
21608                 break;
21609             case 'under':
21610                 if(!this.errorEl){
21611                     var elp = this.el.findParent('.x-form-element', 5, true);
21612                     this.errorEl = elp.createChild({cls:'x-form-invalid-msg'});
21613                     this.errorEl.setWidth(elp.getWidth(true)-20);
21614                 }
21615                 this.errorEl.update(msg);
21616                 Roo.form.Field.msgFx[this.msgFx].show(this.errorEl, this);
21617                 break;
21618             case 'side':
21619                 if(!this.errorIcon){
21620                     var elp = this.el.findParent('.x-form-element', 5, true);
21621                     this.errorIcon = elp.createChild({cls:'x-form-invalid-icon'});
21622                 }
21623                 this.alignErrorIcon();
21624                 this.errorIcon.dom.qtip = msg;
21625                 this.errorIcon.dom.qclass = 'x-form-invalid-tip';
21626                 this.errorIcon.show();
21627                 this.on('resize', this.alignErrorIcon, this);
21628                 break;
21629             default:
21630                 var t = Roo.getDom(this.msgTarget);
21631                 t.innerHTML = msg;
21632                 t.style.display = this.msgDisplay;
21633                 break;
21634         }
21635         this.fireEvent('invalid', this, msg);
21636     },
21637
21638     // private
21639     alignErrorIcon : function(){
21640         this.errorIcon.alignTo(this.el, 'tl-tr', [2, 0]);
21641     },
21642
21643     /**
21644      * Clear any invalid styles/messages for this field
21645      */
21646     clearInvalid : function(){
21647         if(!this.rendered || this.preventMark){ // not rendered
21648             return;
21649         }
21650         this.el.removeClass(this.invalidClass);
21651         switch(this.msgTarget){
21652             case 'qtip':
21653                 this.el.dom.qtip = '';
21654                 break;
21655             case 'title':
21656                 this.el.dom.title = '';
21657                 break;
21658             case 'under':
21659                 if(this.errorEl){
21660                     Roo.form.Field.msgFx[this.msgFx].hide(this.errorEl, this);
21661                 }
21662                 break;
21663             case 'side':
21664                 if(this.errorIcon){
21665                     this.errorIcon.dom.qtip = '';
21666                     this.errorIcon.hide();
21667                     this.un('resize', this.alignErrorIcon, this);
21668                 }
21669                 break;
21670             default:
21671                 var t = Roo.getDom(this.msgTarget);
21672                 t.innerHTML = '';
21673                 t.style.display = 'none';
21674                 break;
21675         }
21676         this.fireEvent('valid', this);
21677     },
21678
21679     /**
21680      * Returns the raw data value which may or may not be a valid, defined value.  To return a normalized value see {@link #getValue}.
21681      * @return {Mixed} value The field value
21682      */
21683     getRawValue : function(){
21684         var v = this.el.getValue();
21685         
21686         return v;
21687     },
21688
21689     /**
21690      * Returns the normalized data value (undefined or emptyText will be returned as '').  To return the raw value see {@link #getRawValue}.
21691      * @return {Mixed} value The field value
21692      */
21693     getValue : function(){
21694         var v = this.el.getValue();
21695          
21696         return v;
21697     },
21698
21699     /**
21700      * Sets the underlying DOM field's value directly, bypassing validation.  To set the value with validation see {@link #setValue}.
21701      * @param {Mixed} value The value to set
21702      */
21703     setRawValue : function(v){
21704         return this.el.dom.value = (v === null || v === undefined ? '' : v);
21705     },
21706
21707     /**
21708      * Sets a data value into the field and validates it.  To set the value directly without validation see {@link #setRawValue}.
21709      * @param {Mixed} value The value to set
21710      */
21711     setValue : function(v){
21712         this.value = v;
21713         if(this.rendered){
21714             this.el.dom.value = (v === null || v === undefined ? '' : v);
21715              this.validate();
21716         }
21717     },
21718
21719     adjustSize : function(w, h){
21720         var s = Roo.form.Field.superclass.adjustSize.call(this, w, h);
21721         s.width = this.adjustWidth(this.el.dom.tagName, s.width);
21722         return s;
21723     },
21724
21725     adjustWidth : function(tag, w){
21726         tag = tag.toLowerCase();
21727         if(typeof w == 'number' && Roo.isStrict && !Roo.isSafari){
21728             if(Roo.isIE && (tag == 'input' || tag == 'textarea')){
21729                 if(tag == 'input'){
21730                     return w + 2;
21731                 }
21732                 if(tag == 'textarea'){
21733                     return w-2;
21734                 }
21735             }else if(Roo.isOpera){
21736                 if(tag == 'input'){
21737                     return w + 2;
21738                 }
21739                 if(tag == 'textarea'){
21740                     return w-2;
21741                 }
21742             }
21743         }
21744         return w;
21745     }
21746 });
21747
21748
21749 // anything other than normal should be considered experimental
21750 Roo.form.Field.msgFx = {
21751     normal : {
21752         show: function(msgEl, f){
21753             msgEl.setDisplayed('block');
21754         },
21755
21756         hide : function(msgEl, f){
21757             msgEl.setDisplayed(false).update('');
21758         }
21759     },
21760
21761     slide : {
21762         show: function(msgEl, f){
21763             msgEl.slideIn('t', {stopFx:true});
21764         },
21765
21766         hide : function(msgEl, f){
21767             msgEl.slideOut('t', {stopFx:true,useDisplay:true});
21768         }
21769     },
21770
21771     slideRight : {
21772         show: function(msgEl, f){
21773             msgEl.fixDisplay();
21774             msgEl.alignTo(f.el, 'tl-tr');
21775             msgEl.slideIn('l', {stopFx:true});
21776         },
21777
21778         hide : function(msgEl, f){
21779             msgEl.slideOut('l', {stopFx:true,useDisplay:true});
21780         }
21781     }
21782 };/*
21783  * Based on:
21784  * Ext JS Library 1.1.1
21785  * Copyright(c) 2006-2007, Ext JS, LLC.
21786  *
21787  * Originally Released Under LGPL - original licence link has changed is not relivant.
21788  *
21789  * Fork - LGPL
21790  * <script type="text/javascript">
21791  */
21792  
21793
21794 /**
21795  * @class Roo.form.TextField
21796  * @extends Roo.form.Field
21797  * Basic text field.  Can be used as a direct replacement for traditional text inputs, or as the base
21798  * class for more sophisticated input controls (like {@link Roo.form.TextArea} and {@link Roo.form.ComboBox}).
21799  * @constructor
21800  * Creates a new TextField
21801  * @param {Object} config Configuration options
21802  */
21803 Roo.form.TextField = function(config){
21804     Roo.form.TextField.superclass.constructor.call(this, config);
21805     this.addEvents({
21806         /**
21807          * @event autosize
21808          * Fires when the autosize function is triggered.  The field may or may not have actually changed size
21809          * according to the default logic, but this event provides a hook for the developer to apply additional
21810          * logic at runtime to resize the field if needed.
21811              * @param {Roo.form.Field} this This text field
21812              * @param {Number} width The new field width
21813              */
21814         autosize : true
21815     });
21816 };
21817
21818 Roo.extend(Roo.form.TextField, Roo.form.Field,  {
21819     /**
21820      * @cfg {Boolean} grow True if this field should automatically grow and shrink to its content
21821      */
21822     grow : false,
21823     /**
21824      * @cfg {Number} growMin The minimum width to allow when grow = true (defaults to 30)
21825      */
21826     growMin : 30,
21827     /**
21828      * @cfg {Number} growMax The maximum width to allow when grow = true (defaults to 800)
21829      */
21830     growMax : 800,
21831     /**
21832      * @cfg {String} vtype A validation type name as defined in {@link Roo.form.VTypes} (defaults to null)
21833      */
21834     vtype : null,
21835     /**
21836      * @cfg {String} maskRe An input mask regular expression that will be used to filter keystrokes that don't match (defaults to null)
21837      */
21838     maskRe : null,
21839     /**
21840      * @cfg {Boolean} disableKeyFilter True to disable input keystroke filtering (defaults to false)
21841      */
21842     disableKeyFilter : false,
21843     /**
21844      * @cfg {Boolean} allowBlank False to validate that the value length > 0 (defaults to true)
21845      */
21846     allowBlank : true,
21847     /**
21848      * @cfg {Number} minLength Minimum input field length required (defaults to 0)
21849      */
21850     minLength : 0,
21851     /**
21852      * @cfg {Number} maxLength Maximum input field length allowed (defaults to Number.MAX_VALUE)
21853      */
21854     maxLength : Number.MAX_VALUE,
21855     /**
21856      * @cfg {String} minLengthText Error text to display if the minimum length validation fails (defaults to "The minimum length for this field is {minLength}")
21857      */
21858     minLengthText : "The minimum length for this field is {0}",
21859     /**
21860      * @cfg {String} maxLengthText Error text to display if the maximum length validation fails (defaults to "The maximum length for this field is {maxLength}")
21861      */
21862     maxLengthText : "The maximum length for this field is {0}",
21863     /**
21864      * @cfg {Boolean} selectOnFocus True to automatically select any existing field text when the field receives input focus (defaults to false)
21865      */
21866     selectOnFocus : false,
21867     /**
21868      * @cfg {String} blankText Error text to display if the allow blank validation fails (defaults to "This field is required")
21869      */
21870     blankText : "This field is required",
21871     /**
21872      * @cfg {Function} validator A custom validation function to be called during field validation (defaults to null).
21873      * If available, this function will be called only after the basic validators all return true, and will be passed the
21874      * current field value and expected to return boolean true if the value is valid or a string error message if invalid.
21875      */
21876     validator : null,
21877     /**
21878      * @cfg {RegExp} regex A JavaScript RegExp object to be tested against the field value during validation (defaults to null).
21879      * If available, this regex will be evaluated only after the basic validators all return true, and will be passed the
21880      * current field value.  If the test fails, the field will be marked invalid using {@link #regexText}.
21881      */
21882     regex : null,
21883     /**
21884      * @cfg {String} regexText The error text to display if {@link #regex} is used and the test fails during validation (defaults to "")
21885      */
21886     regexText : "",
21887     /**
21888      * @cfg {String} emptyText The default text to display in an empty field - placeholder... (defaults to null).
21889      */
21890     emptyText : null,
21891    
21892
21893     // private
21894     initEvents : function()
21895     {
21896         if (this.emptyText) {
21897             this.el.attr('placeholder', this.emptyText);
21898         }
21899         
21900         Roo.form.TextField.superclass.initEvents.call(this);
21901         if(this.validationEvent == 'keyup'){
21902             this.validationTask = new Roo.util.DelayedTask(this.validate, this);
21903             this.el.on('keyup', this.filterValidation, this);
21904         }
21905         else if(this.validationEvent !== false){
21906             this.el.on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
21907         }
21908         
21909         if(this.selectOnFocus){
21910             this.on("focus", this.preFocus, this);
21911             
21912         }
21913         if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
21914             this.el.on("keypress", this.filterKeys, this);
21915         }
21916         if(this.grow){
21917             this.el.on("keyup", this.onKeyUp,  this, {buffer:50});
21918             this.el.on("click", this.autoSize,  this);
21919         }
21920         if(this.el.is('input[type=password]') && Roo.isSafari){
21921             this.el.on('keydown', this.SafariOnKeyDown, this);
21922         }
21923     },
21924
21925     processValue : function(value){
21926         if(this.stripCharsRe){
21927             var newValue = value.replace(this.stripCharsRe, '');
21928             if(newValue !== value){
21929                 this.setRawValue(newValue);
21930                 return newValue;
21931             }
21932         }
21933         return value;
21934     },
21935
21936     filterValidation : function(e){
21937         if(!e.isNavKeyPress()){
21938             this.validationTask.delay(this.validationDelay);
21939         }
21940     },
21941
21942     // private
21943     onKeyUp : function(e){
21944         if(!e.isNavKeyPress()){
21945             this.autoSize();
21946         }
21947     },
21948
21949     /**
21950      * Resets the current field value to the originally-loaded value and clears any validation messages.
21951      *  
21952      */
21953     reset : function(){
21954         Roo.form.TextField.superclass.reset.call(this);
21955        
21956     },
21957
21958     
21959     // private
21960     preFocus : function(){
21961         
21962         if(this.selectOnFocus){
21963             this.el.dom.select();
21964         }
21965     },
21966
21967     
21968     // private
21969     filterKeys : function(e){
21970         var k = e.getKey();
21971         if(!Roo.isIE && (e.isNavKeyPress() || k == e.BACKSPACE || (k == e.DELETE && e.button == -1))){
21972             return;
21973         }
21974         var c = e.getCharCode(), cc = String.fromCharCode(c);
21975         if(Roo.isIE && (e.isSpecialKey() || !cc)){
21976             return;
21977         }
21978         if(!this.maskRe.test(cc)){
21979             e.stopEvent();
21980         }
21981     },
21982
21983     setValue : function(v){
21984         
21985         Roo.form.TextField.superclass.setValue.apply(this, arguments);
21986         
21987         this.autoSize();
21988     },
21989
21990     /**
21991      * Validates a value according to the field's validation rules and marks the field as invalid
21992      * if the validation fails
21993      * @param {Mixed} value The value to validate
21994      * @return {Boolean} True if the value is valid, else false
21995      */
21996     validateValue : function(value){
21997         if(value.length < 1)  { // if it's blank
21998              if(this.allowBlank){
21999                 this.clearInvalid();
22000                 return true;
22001              }else{
22002                 this.markInvalid(this.blankText);
22003                 return false;
22004              }
22005         }
22006         if(value.length < this.minLength){
22007             this.markInvalid(String.format(this.minLengthText, this.minLength));
22008             return false;
22009         }
22010         if(value.length > this.maxLength){
22011             this.markInvalid(String.format(this.maxLengthText, this.maxLength));
22012             return false;
22013         }
22014         if(this.vtype){
22015             var vt = Roo.form.VTypes;
22016             if(!vt[this.vtype](value, this)){
22017                 this.markInvalid(this.vtypeText || vt[this.vtype +'Text']);
22018                 return false;
22019             }
22020         }
22021         if(typeof this.validator == "function"){
22022             var msg = this.validator(value);
22023             if(msg !== true){
22024                 this.markInvalid(msg);
22025                 return false;
22026             }
22027         }
22028         if(this.regex && !this.regex.test(value)){
22029             this.markInvalid(this.regexText);
22030             return false;
22031         }
22032         return true;
22033     },
22034
22035     /**
22036      * Selects text in this field
22037      * @param {Number} start (optional) The index where the selection should start (defaults to 0)
22038      * @param {Number} end (optional) The index where the selection should end (defaults to the text length)
22039      */
22040     selectText : function(start, end){
22041         var v = this.getRawValue();
22042         if(v.length > 0){
22043             start = start === undefined ? 0 : start;
22044             end = end === undefined ? v.length : end;
22045             var d = this.el.dom;
22046             if(d.setSelectionRange){
22047                 d.setSelectionRange(start, end);
22048             }else if(d.createTextRange){
22049                 var range = d.createTextRange();
22050                 range.moveStart("character", start);
22051                 range.moveEnd("character", v.length-end);
22052                 range.select();
22053             }
22054         }
22055     },
22056
22057     /**
22058      * Automatically grows the field to accomodate the width of the text up to the maximum field width allowed.
22059      * This only takes effect if grow = true, and fires the autosize event.
22060      */
22061     autoSize : function(){
22062         if(!this.grow || !this.rendered){
22063             return;
22064         }
22065         if(!this.metrics){
22066             this.metrics = Roo.util.TextMetrics.createInstance(this.el);
22067         }
22068         var el = this.el;
22069         var v = el.dom.value;
22070         var d = document.createElement('div');
22071         d.appendChild(document.createTextNode(v));
22072         v = d.innerHTML;
22073         d = null;
22074         v += "&#160;";
22075         var w = Math.min(this.growMax, Math.max(this.metrics.getWidth(v) + /* add extra padding */ 10, this.growMin));
22076         this.el.setWidth(w);
22077         this.fireEvent("autosize", this, w);
22078     },
22079     
22080     // private
22081     SafariOnKeyDown : function(event)
22082     {
22083         // this is a workaround for a password hang bug on chrome/ webkit.
22084         
22085         var isSelectAll = false;
22086         
22087         if(this.el.dom.selectionEnd > 0){
22088             isSelectAll = (this.el.dom.selectionEnd - this.el.dom.selectionStart - this.getValue().length == 0) ? true : false;
22089         }
22090         if(((event.getKey() == 8 || event.getKey() == 46) && this.getValue().length ==1)){ // backspace and delete key
22091             event.preventDefault();
22092             this.setValue('');
22093             return;
22094         }
22095         
22096         if(isSelectAll){ // backspace and delete key
22097             
22098             event.preventDefault();
22099             // this is very hacky as keydown always get's upper case.
22100             //
22101             var cc = String.fromCharCode(event.getCharCode());
22102             this.setValue( event.shiftKey ?  cc : cc.toLowerCase());
22103             
22104         }
22105         
22106         
22107     }
22108 });/*
22109  * Based on:
22110  * Ext JS Library 1.1.1
22111  * Copyright(c) 2006-2007, Ext JS, LLC.
22112  *
22113  * Originally Released Under LGPL - original licence link has changed is not relivant.
22114  *
22115  * Fork - LGPL
22116  * <script type="text/javascript">
22117  */
22118  
22119 /**
22120  * @class Roo.form.Hidden
22121  * @extends Roo.form.TextField
22122  * Simple Hidden element used on forms 
22123  * 
22124  * usage: form.add(new Roo.form.HiddenField({ 'name' : 'test1' }));
22125  * 
22126  * @constructor
22127  * Creates a new Hidden form element.
22128  * @param {Object} config Configuration options
22129  */
22130
22131
22132
22133 // easy hidden field...
22134 Roo.form.Hidden = function(config){
22135     Roo.form.Hidden.superclass.constructor.call(this, config);
22136 };
22137   
22138 Roo.extend(Roo.form.Hidden, Roo.form.TextField, {
22139     fieldLabel:      '',
22140     inputType:      'hidden',
22141     width:          50,
22142     allowBlank:     true,
22143     labelSeparator: '',
22144     hidden:         true,
22145     itemCls :       'x-form-item-display-none'
22146
22147
22148 });
22149
22150
22151 /*
22152  * Based on:
22153  * Ext JS Library 1.1.1
22154  * Copyright(c) 2006-2007, Ext JS, LLC.
22155  *
22156  * Originally Released Under LGPL - original licence link has changed is not relivant.
22157  *
22158  * Fork - LGPL
22159  * <script type="text/javascript">
22160  */
22161  
22162 /**
22163  * @class Roo.form.TriggerField
22164  * @extends Roo.form.TextField
22165  * Provides a convenient wrapper for TextFields that adds a clickable trigger button (looks like a combobox by default).
22166  * The trigger has no default action, so you must assign a function to implement the trigger click handler by
22167  * overriding {@link #onTriggerClick}. You can create a TriggerField directly, as it renders exactly like a combobox
22168  * for which you can provide a custom implementation.  For example:
22169  * <pre><code>
22170 var trigger = new Roo.form.TriggerField();
22171 trigger.onTriggerClick = myTriggerFn;
22172 trigger.applyTo('my-field');
22173 </code></pre>
22174  *
22175  * However, in general you will most likely want to use TriggerField as the base class for a reusable component.
22176  * {@link Roo.form.DateField} and {@link Roo.form.ComboBox} are perfect examples of this.
22177  * @cfg {String} triggerClass An additional CSS class used to style the trigger button.  The trigger will always get the
22178  * class 'x-form-trigger' by default and triggerClass will be <b>appended</b> if specified.
22179  * @constructor
22180  * Create a new TriggerField.
22181  * @param {Object} config Configuration options (valid {@Roo.form.TextField} config options will also be applied
22182  * to the base TextField)
22183  */
22184 Roo.form.TriggerField = function(config){
22185     this.mimicing = false;
22186     Roo.form.TriggerField.superclass.constructor.call(this, config);
22187 };
22188
22189 Roo.extend(Roo.form.TriggerField, Roo.form.TextField,  {
22190     /**
22191      * @cfg {String} triggerClass A CSS class to apply to the trigger
22192      */
22193     /**
22194      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
22195      * {tag: "input", type: "text", size: "16", autocomplete: "off"})
22196      */
22197     defaultAutoCreate : {tag: "input", type: "text", size: "16", autocomplete: "off"},
22198     /**
22199      * @cfg {Boolean} hideTrigger True to hide the trigger element and display only the base text field (defaults to false)
22200      */
22201     hideTrigger:false,
22202
22203     /** @cfg {Boolean} grow @hide */
22204     /** @cfg {Number} growMin @hide */
22205     /** @cfg {Number} growMax @hide */
22206
22207     /**
22208      * @hide 
22209      * @method
22210      */
22211     autoSize: Roo.emptyFn,
22212     // private
22213     monitorTab : true,
22214     // private
22215     deferHeight : true,
22216
22217     
22218     actionMode : 'wrap',
22219     // private
22220     onResize : function(w, h){
22221         Roo.form.TriggerField.superclass.onResize.apply(this, arguments);
22222         if(typeof w == 'number'){
22223             var x = w - this.trigger.getWidth();
22224             this.el.setWidth(this.adjustWidth('input', x));
22225             this.trigger.setStyle('left', x+'px');
22226         }
22227     },
22228
22229     // private
22230     adjustSize : Roo.BoxComponent.prototype.adjustSize,
22231
22232     // private
22233     getResizeEl : function(){
22234         return this.wrap;
22235     },
22236
22237     // private
22238     getPositionEl : function(){
22239         return this.wrap;
22240     },
22241
22242     // private
22243     alignErrorIcon : function(){
22244         this.errorIcon.alignTo(this.wrap, 'tl-tr', [2, 0]);
22245     },
22246
22247     // private
22248     onRender : function(ct, position){
22249         Roo.form.TriggerField.superclass.onRender.call(this, ct, position);
22250         this.wrap = this.el.wrap({cls: "x-form-field-wrap"});
22251         this.trigger = this.wrap.createChild(this.triggerConfig ||
22252                 {tag: "img", src: Roo.BLANK_IMAGE_URL, cls: "x-form-trigger " + this.triggerClass});
22253         if(this.hideTrigger){
22254             this.trigger.setDisplayed(false);
22255         }
22256         this.initTrigger();
22257         if(!this.width){
22258             this.wrap.setWidth(this.el.getWidth()+this.trigger.getWidth());
22259         }
22260     },
22261
22262     // private
22263     initTrigger : function(){
22264         this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
22265         this.trigger.addClassOnOver('x-form-trigger-over');
22266         this.trigger.addClassOnClick('x-form-trigger-click');
22267     },
22268
22269     // private
22270     onDestroy : function(){
22271         if(this.trigger){
22272             this.trigger.removeAllListeners();
22273             this.trigger.remove();
22274         }
22275         if(this.wrap){
22276             this.wrap.remove();
22277         }
22278         Roo.form.TriggerField.superclass.onDestroy.call(this);
22279     },
22280
22281     // private
22282     onFocus : function(){
22283         Roo.form.TriggerField.superclass.onFocus.call(this);
22284         if(!this.mimicing){
22285             this.wrap.addClass('x-trigger-wrap-focus');
22286             this.mimicing = true;
22287             Roo.get(Roo.isIE ? document.body : document).on("mousedown", this.mimicBlur, this);
22288             if(this.monitorTab){
22289                 this.el.on("keydown", this.checkTab, this);
22290             }
22291         }
22292     },
22293
22294     // private
22295     checkTab : function(e){
22296         if(e.getKey() == e.TAB){
22297             this.triggerBlur();
22298         }
22299     },
22300
22301     // private
22302     onBlur : function(){
22303         // do nothing
22304     },
22305
22306     // private
22307     mimicBlur : function(e, t){
22308         if(!this.wrap.contains(t) && this.validateBlur()){
22309             this.triggerBlur();
22310         }
22311     },
22312
22313     // private
22314     triggerBlur : function(){
22315         this.mimicing = false;
22316         Roo.get(Roo.isIE ? document.body : document).un("mousedown", this.mimicBlur);
22317         if(this.monitorTab){
22318             this.el.un("keydown", this.checkTab, this);
22319         }
22320         this.wrap.removeClass('x-trigger-wrap-focus');
22321         Roo.form.TriggerField.superclass.onBlur.call(this);
22322     },
22323
22324     // private
22325     // This should be overriden by any subclass that needs to check whether or not the field can be blurred.
22326     validateBlur : function(e, t){
22327         return true;
22328     },
22329
22330     // private
22331     onDisable : function(){
22332         Roo.form.TriggerField.superclass.onDisable.call(this);
22333         if(this.wrap){
22334             this.wrap.addClass('x-item-disabled');
22335         }
22336     },
22337
22338     // private
22339     onEnable : function(){
22340         Roo.form.TriggerField.superclass.onEnable.call(this);
22341         if(this.wrap){
22342             this.wrap.removeClass('x-item-disabled');
22343         }
22344     },
22345
22346     // private
22347     onShow : function(){
22348         var ae = this.getActionEl();
22349         
22350         if(ae){
22351             ae.dom.style.display = '';
22352             ae.dom.style.visibility = 'visible';
22353         }
22354     },
22355
22356     // private
22357     
22358     onHide : function(){
22359         var ae = this.getActionEl();
22360         ae.dom.style.display = 'none';
22361     },
22362
22363     /**
22364      * The function that should handle the trigger's click event.  This method does nothing by default until overridden
22365      * by an implementing function.
22366      * @method
22367      * @param {EventObject} e
22368      */
22369     onTriggerClick : Roo.emptyFn
22370 });
22371
22372 // TwinTriggerField is not a public class to be used directly.  It is meant as an abstract base class
22373 // to be extended by an implementing class.  For an example of implementing this class, see the custom
22374 // SearchField implementation here: http://extjs.com/deploy/ext/examples/form/custom.html
22375 Roo.form.TwinTriggerField = Roo.extend(Roo.form.TriggerField, {
22376     initComponent : function(){
22377         Roo.form.TwinTriggerField.superclass.initComponent.call(this);
22378
22379         this.triggerConfig = {
22380             tag:'span', cls:'x-form-twin-triggers', cn:[
22381             {tag: "img", src: Roo.BLANK_IMAGE_URL, cls: "x-form-trigger " + this.trigger1Class},
22382             {tag: "img", src: Roo.BLANK_IMAGE_URL, cls: "x-form-trigger " + this.trigger2Class}
22383         ]};
22384     },
22385
22386     getTrigger : function(index){
22387         return this.triggers[index];
22388     },
22389
22390     initTrigger : function(){
22391         var ts = this.trigger.select('.x-form-trigger', true);
22392         this.wrap.setStyle('overflow', 'hidden');
22393         var triggerField = this;
22394         ts.each(function(t, all, index){
22395             t.hide = function(){
22396                 var w = triggerField.wrap.getWidth();
22397                 this.dom.style.display = 'none';
22398                 triggerField.el.setWidth(w-triggerField.trigger.getWidth());
22399             };
22400             t.show = function(){
22401                 var w = triggerField.wrap.getWidth();
22402                 this.dom.style.display = '';
22403                 triggerField.el.setWidth(w-triggerField.trigger.getWidth());
22404             };
22405             var triggerIndex = 'Trigger'+(index+1);
22406
22407             if(this['hide'+triggerIndex]){
22408                 t.dom.style.display = 'none';
22409             }
22410             t.on("click", this['on'+triggerIndex+'Click'], this, {preventDefault:true});
22411             t.addClassOnOver('x-form-trigger-over');
22412             t.addClassOnClick('x-form-trigger-click');
22413         }, this);
22414         this.triggers = ts.elements;
22415     },
22416
22417     onTrigger1Click : Roo.emptyFn,
22418     onTrigger2Click : Roo.emptyFn
22419 });/*
22420  * Based on:
22421  * Ext JS Library 1.1.1
22422  * Copyright(c) 2006-2007, Ext JS, LLC.
22423  *
22424  * Originally Released Under LGPL - original licence link has changed is not relivant.
22425  *
22426  * Fork - LGPL
22427  * <script type="text/javascript">
22428  */
22429  
22430 /**
22431  * @class Roo.form.TextArea
22432  * @extends Roo.form.TextField
22433  * Multiline text field.  Can be used as a direct replacement for traditional textarea fields, plus adds
22434  * support for auto-sizing.
22435  * @constructor
22436  * Creates a new TextArea
22437  * @param {Object} config Configuration options
22438  */
22439 Roo.form.TextArea = function(config){
22440     Roo.form.TextArea.superclass.constructor.call(this, config);
22441     // these are provided exchanges for backwards compat
22442     // minHeight/maxHeight were replaced by growMin/growMax to be
22443     // compatible with TextField growing config values
22444     if(this.minHeight !== undefined){
22445         this.growMin = this.minHeight;
22446     }
22447     if(this.maxHeight !== undefined){
22448         this.growMax = this.maxHeight;
22449     }
22450 };
22451
22452 Roo.extend(Roo.form.TextArea, Roo.form.TextField,  {
22453     /**
22454      * @cfg {Number} growMin The minimum height to allow when grow = true (defaults to 60)
22455      */
22456     growMin : 60,
22457     /**
22458      * @cfg {Number} growMax The maximum height to allow when grow = true (defaults to 1000)
22459      */
22460     growMax: 1000,
22461     /**
22462      * @cfg {Boolean} preventScrollbars True to prevent scrollbars from appearing regardless of how much text is
22463      * in the field (equivalent to setting overflow: hidden, defaults to false)
22464      */
22465     preventScrollbars: false,
22466     /**
22467      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
22468      * {tag: "textarea", style: "width:300px;height:60px;", autocomplete: "off"})
22469      */
22470
22471     // private
22472     onRender : function(ct, position){
22473         if(!this.el){
22474             this.defaultAutoCreate = {
22475                 tag: "textarea",
22476                 style:"width:300px;height:60px;",
22477                 autocomplete: "off"
22478             };
22479         }
22480         Roo.form.TextArea.superclass.onRender.call(this, ct, position);
22481         if(this.grow){
22482             this.textSizeEl = Roo.DomHelper.append(document.body, {
22483                 tag: "pre", cls: "x-form-grow-sizer"
22484             });
22485             if(this.preventScrollbars){
22486                 this.el.setStyle("overflow", "hidden");
22487             }
22488             this.el.setHeight(this.growMin);
22489         }
22490     },
22491
22492     onDestroy : function(){
22493         if(this.textSizeEl){
22494             this.textSizeEl.parentNode.removeChild(this.textSizeEl);
22495         }
22496         Roo.form.TextArea.superclass.onDestroy.call(this);
22497     },
22498
22499     // private
22500     onKeyUp : function(e){
22501         if(!e.isNavKeyPress() || e.getKey() == e.ENTER){
22502             this.autoSize();
22503         }
22504     },
22505
22506     /**
22507      * Automatically grows the field to accomodate the height of the text up to the maximum field height allowed.
22508      * This only takes effect if grow = true, and fires the autosize event if the height changes.
22509      */
22510     autoSize : function(){
22511         if(!this.grow || !this.textSizeEl){
22512             return;
22513         }
22514         var el = this.el;
22515         var v = el.dom.value;
22516         var ts = this.textSizeEl;
22517
22518         ts.innerHTML = '';
22519         ts.appendChild(document.createTextNode(v));
22520         v = ts.innerHTML;
22521
22522         Roo.fly(ts).setWidth(this.el.getWidth());
22523         if(v.length < 1){
22524             v = "&#160;&#160;";
22525         }else{
22526             if(Roo.isIE){
22527                 v = v.replace(/\n/g, '<p>&#160;</p>');
22528             }
22529             v += "&#160;\n&#160;";
22530         }
22531         ts.innerHTML = v;
22532         var h = Math.min(this.growMax, Math.max(ts.offsetHeight, this.growMin));
22533         if(h != this.lastHeight){
22534             this.lastHeight = h;
22535             this.el.setHeight(h);
22536             this.fireEvent("autosize", this, h);
22537         }
22538     }
22539 });/*
22540  * Based on:
22541  * Ext JS Library 1.1.1
22542  * Copyright(c) 2006-2007, Ext JS, LLC.
22543  *
22544  * Originally Released Under LGPL - original licence link has changed is not relivant.
22545  *
22546  * Fork - LGPL
22547  * <script type="text/javascript">
22548  */
22549  
22550
22551 /**
22552  * @class Roo.form.NumberField
22553  * @extends Roo.form.TextField
22554  * Numeric text field that provides automatic keystroke filtering and numeric validation.
22555  * @constructor
22556  * Creates a new NumberField
22557  * @param {Object} config Configuration options
22558  */
22559 Roo.form.NumberField = function(config){
22560     Roo.form.NumberField.superclass.constructor.call(this, config);
22561 };
22562
22563 Roo.extend(Roo.form.NumberField, Roo.form.TextField,  {
22564     /**
22565      * @cfg {String} fieldClass The default CSS class for the field (defaults to "x-form-field x-form-num-field")
22566      */
22567     fieldClass: "x-form-field x-form-num-field",
22568     /**
22569      * @cfg {Boolean} allowDecimals False to disallow decimal values (defaults to true)
22570      */
22571     allowDecimals : true,
22572     /**
22573      * @cfg {String} decimalSeparator Character(s) to allow as the decimal separator (defaults to '.')
22574      */
22575     decimalSeparator : ".",
22576     /**
22577      * @cfg {Number} decimalPrecision The maximum precision to display after the decimal separator (defaults to 2)
22578      */
22579     decimalPrecision : 2,
22580     /**
22581      * @cfg {Boolean} allowNegative False to prevent entering a negative sign (defaults to true)
22582      */
22583     allowNegative : true,
22584     /**
22585      * @cfg {Number} minValue The minimum allowed value (defaults to Number.NEGATIVE_INFINITY)
22586      */
22587     minValue : Number.NEGATIVE_INFINITY,
22588     /**
22589      * @cfg {Number} maxValue The maximum allowed value (defaults to Number.MAX_VALUE)
22590      */
22591     maxValue : Number.MAX_VALUE,
22592     /**
22593      * @cfg {String} minText Error text to display if the minimum value validation fails (defaults to "The minimum value for this field is {minValue}")
22594      */
22595     minText : "The minimum value for this field is {0}",
22596     /**
22597      * @cfg {String} maxText Error text to display if the maximum value validation fails (defaults to "The maximum value for this field is {maxValue}")
22598      */
22599     maxText : "The maximum value for this field is {0}",
22600     /**
22601      * @cfg {String} nanText Error text to display if the value is not a valid number.  For example, this can happen
22602      * if a valid character like '.' or '-' is left in the field with no number (defaults to "{value} is not a valid number")
22603      */
22604     nanText : "{0} is not a valid number",
22605
22606     // private
22607     initEvents : function(){
22608         Roo.form.NumberField.superclass.initEvents.call(this);
22609         var allowed = "0123456789";
22610         if(this.allowDecimals){
22611             allowed += this.decimalSeparator;
22612         }
22613         if(this.allowNegative){
22614             allowed += "-";
22615         }
22616         this.stripCharsRe = new RegExp('[^'+allowed+']', 'gi');
22617         var keyPress = function(e){
22618             var k = e.getKey();
22619             if(!Roo.isIE && (e.isSpecialKey() || k == e.BACKSPACE || k == e.DELETE)){
22620                 return;
22621             }
22622             var c = e.getCharCode();
22623             if(allowed.indexOf(String.fromCharCode(c)) === -1){
22624                 e.stopEvent();
22625             }
22626         };
22627         this.el.on("keypress", keyPress, this);
22628     },
22629
22630     // private
22631     validateValue : function(value){
22632         if(!Roo.form.NumberField.superclass.validateValue.call(this, value)){
22633             return false;
22634         }
22635         if(value.length < 1){ // if it's blank and textfield didn't flag it then it's valid
22636              return true;
22637         }
22638         var num = this.parseValue(value);
22639         if(isNaN(num)){
22640             this.markInvalid(String.format(this.nanText, value));
22641             return false;
22642         }
22643         if(num < this.minValue){
22644             this.markInvalid(String.format(this.minText, this.minValue));
22645             return false;
22646         }
22647         if(num > this.maxValue){
22648             this.markInvalid(String.format(this.maxText, this.maxValue));
22649             return false;
22650         }
22651         return true;
22652     },
22653
22654     getValue : function(){
22655         return this.fixPrecision(this.parseValue(Roo.form.NumberField.superclass.getValue.call(this)));
22656     },
22657
22658     // private
22659     parseValue : function(value){
22660         value = parseFloat(String(value).replace(this.decimalSeparator, "."));
22661         return isNaN(value) ? '' : value;
22662     },
22663
22664     // private
22665     fixPrecision : function(value){
22666         var nan = isNaN(value);
22667         if(!this.allowDecimals || this.decimalPrecision == -1 || nan || !value){
22668             return nan ? '' : value;
22669         }
22670         return parseFloat(value).toFixed(this.decimalPrecision);
22671     },
22672
22673     setValue : function(v){
22674         v = this.fixPrecision(v);
22675         Roo.form.NumberField.superclass.setValue.call(this, String(v).replace(".", this.decimalSeparator));
22676     },
22677
22678     // private
22679     decimalPrecisionFcn : function(v){
22680         return Math.floor(v);
22681     },
22682
22683     beforeBlur : function(){
22684         var v = this.parseValue(this.getRawValue());
22685         if(v){
22686             this.setValue(v);
22687         }
22688     }
22689 });/*
22690  * Based on:
22691  * Ext JS Library 1.1.1
22692  * Copyright(c) 2006-2007, Ext JS, LLC.
22693  *
22694  * Originally Released Under LGPL - original licence link has changed is not relivant.
22695  *
22696  * Fork - LGPL
22697  * <script type="text/javascript">
22698  */
22699  
22700 /**
22701  * @class Roo.form.DateField
22702  * @extends Roo.form.TriggerField
22703  * Provides a date input field with a {@link Roo.DatePicker} dropdown and automatic date validation.
22704 * @constructor
22705 * Create a new DateField
22706 * @param {Object} config
22707  */
22708 Roo.form.DateField = function(config){
22709     Roo.form.DateField.superclass.constructor.call(this, config);
22710     
22711       this.addEvents({
22712          
22713         /**
22714          * @event select
22715          * Fires when a date is selected
22716              * @param {Roo.form.DateField} combo This combo box
22717              * @param {Date} date The date selected
22718              */
22719         'select' : true
22720          
22721     });
22722     
22723     
22724     if(typeof this.minValue == "string") this.minValue = this.parseDate(this.minValue);
22725     if(typeof this.maxValue == "string") this.maxValue = this.parseDate(this.maxValue);
22726     this.ddMatch = null;
22727     if(this.disabledDates){
22728         var dd = this.disabledDates;
22729         var re = "(?:";
22730         for(var i = 0; i < dd.length; i++){
22731             re += dd[i];
22732             if(i != dd.length-1) re += "|";
22733         }
22734         this.ddMatch = new RegExp(re + ")");
22735     }
22736 };
22737
22738 Roo.extend(Roo.form.DateField, Roo.form.TriggerField,  {
22739     /**
22740      * @cfg {String} format
22741      * The default date format string which can be overriden for localization support.  The format must be
22742      * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
22743      */
22744     format : "m/d/y",
22745     /**
22746      * @cfg {String} altFormats
22747      * Multiple date formats separated by "|" to try when parsing a user input value and it doesn't match the defined
22748      * format (defaults to 'm/d/Y|m-d-y|m-d-Y|m/d|m-d|d').
22749      */
22750     altFormats : "m/d/Y|m-d-y|m-d-Y|m/d|m-d|md|mdy|mdY|d",
22751     /**
22752      * @cfg {Array} disabledDays
22753      * An array of days to disable, 0 based. For example, [0, 6] disables Sunday and Saturday (defaults to null).
22754      */
22755     disabledDays : null,
22756     /**
22757      * @cfg {String} disabledDaysText
22758      * The tooltip to display when the date falls on a disabled day (defaults to 'Disabled')
22759      */
22760     disabledDaysText : "Disabled",
22761     /**
22762      * @cfg {Array} disabledDates
22763      * An array of "dates" to disable, as strings. These strings will be used to build a dynamic regular
22764      * expression so they are very powerful. Some examples:
22765      * <ul>
22766      * <li>["03/08/2003", "09/16/2003"] would disable those exact dates</li>
22767      * <li>["03/08", "09/16"] would disable those days for every year</li>
22768      * <li>["^03/08"] would only match the beginning (useful if you are using short years)</li>
22769      * <li>["03/../2006"] would disable every day in March 2006</li>
22770      * <li>["^03"] would disable every day in every March</li>
22771      * </ul>
22772      * In order to support regular expressions, if you are using a date format that has "." in it, you will have to
22773      * escape the dot when restricting dates. For example: ["03\\.08\\.03"].
22774      */
22775     disabledDates : null,
22776     /**
22777      * @cfg {String} disabledDatesText
22778      * The tooltip text to display when the date falls on a disabled date (defaults to 'Disabled')
22779      */
22780     disabledDatesText : "Disabled",
22781     /**
22782      * @cfg {Date/String} minValue
22783      * The minimum allowed date. Can be either a Javascript date object or a string date in a
22784      * valid format (defaults to null).
22785      */
22786     minValue : null,
22787     /**
22788      * @cfg {Date/String} maxValue
22789      * The maximum allowed date. Can be either a Javascript date object or a string date in a
22790      * valid format (defaults to null).
22791      */
22792     maxValue : null,
22793     /**
22794      * @cfg {String} minText
22795      * The error text to display when the date in the cell is before minValue (defaults to
22796      * 'The date in this field must be after {minValue}').
22797      */
22798     minText : "The date in this field must be equal to or after {0}",
22799     /**
22800      * @cfg {String} maxText
22801      * The error text to display when the date in the cell is after maxValue (defaults to
22802      * 'The date in this field must be before {maxValue}').
22803      */
22804     maxText : "The date in this field must be equal to or before {0}",
22805     /**
22806      * @cfg {String} invalidText
22807      * The error text to display when the date in the field is invalid (defaults to
22808      * '{value} is not a valid date - it must be in the format {format}').
22809      */
22810     invalidText : "{0} is not a valid date - it must be in the format {1}",
22811     /**
22812      * @cfg {String} triggerClass
22813      * An additional CSS class used to style the trigger button.  The trigger will always get the
22814      * class 'x-form-trigger' and triggerClass will be <b>appended</b> if specified (defaults to 'x-form-date-trigger'
22815      * which displays a calendar icon).
22816      */
22817     triggerClass : 'x-form-date-trigger',
22818     
22819
22820     /**
22821      * @cfg {Boolean} useIso
22822      * if enabled, then the date field will use a hidden field to store the 
22823      * real value as iso formated date. default (false)
22824      */ 
22825     useIso : false,
22826     /**
22827      * @cfg {String/Object} autoCreate
22828      * A DomHelper element spec, or true for a default element spec (defaults to
22829      * {tag: "input", type: "text", size: "10", autocomplete: "off"})
22830      */ 
22831     // private
22832     defaultAutoCreate : {tag: "input", type: "text", size: "10", autocomplete: "off"},
22833     
22834     // private
22835     hiddenField: false,
22836     
22837     onRender : function(ct, position)
22838     {
22839         Roo.form.DateField.superclass.onRender.call(this, ct, position);
22840         if (this.useIso) {
22841             //this.el.dom.removeAttribute('name'); 
22842             Roo.log("Changing name?");
22843             this.el.dom.setAttribute('name', this.name + '____hidden___' ); 
22844             this.hiddenField = this.el.insertSibling({ tag:'input', type:'hidden', name: this.name },
22845                     'before', true);
22846             this.hiddenField.value = this.value ? this.formatDate(this.value, 'Y-m-d') : '';
22847             // prevent input submission
22848             this.hiddenName = this.name;
22849         }
22850             
22851             
22852     },
22853     
22854     // private
22855     validateValue : function(value)
22856     {
22857         value = this.formatDate(value);
22858         if(!Roo.form.DateField.superclass.validateValue.call(this, value)){
22859             Roo.log('super failed');
22860             return false;
22861         }
22862         if(value.length < 1){ // if it's blank and textfield didn't flag it then it's valid
22863              return true;
22864         }
22865         var svalue = value;
22866         value = this.parseDate(value);
22867         if(!value){
22868             Roo.log('parse date failed' + svalue);
22869             this.markInvalid(String.format(this.invalidText, svalue, this.format));
22870             return false;
22871         }
22872         var time = value.getTime();
22873         if(this.minValue && time < this.minValue.getTime()){
22874             this.markInvalid(String.format(this.minText, this.formatDate(this.minValue)));
22875             return false;
22876         }
22877         if(this.maxValue && time > this.maxValue.getTime()){
22878             this.markInvalid(String.format(this.maxText, this.formatDate(this.maxValue)));
22879             return false;
22880         }
22881         if(this.disabledDays){
22882             var day = value.getDay();
22883             for(var i = 0; i < this.disabledDays.length; i++) {
22884                 if(day === this.disabledDays[i]){
22885                     this.markInvalid(this.disabledDaysText);
22886                     return false;
22887                 }
22888             }
22889         }
22890         var fvalue = this.formatDate(value);
22891         if(this.ddMatch && this.ddMatch.test(fvalue)){
22892             this.markInvalid(String.format(this.disabledDatesText, fvalue));
22893             return false;
22894         }
22895         return true;
22896     },
22897
22898     // private
22899     // Provides logic to override the default TriggerField.validateBlur which just returns true
22900     validateBlur : function(){
22901         return !this.menu || !this.menu.isVisible();
22902     },
22903     
22904     getName: function()
22905     {
22906         // returns hidden if it's set..
22907         if (!this.rendered) {return ''};
22908         return !this.hiddenName && this.el.dom.name  ? this.el.dom.name : (this.hiddenName || '');
22909         
22910     },
22911
22912     /**
22913      * Returns the current date value of the date field.
22914      * @return {Date} The date value
22915      */
22916     getValue : function(){
22917         
22918         return  this.hiddenField ?
22919                 this.hiddenField.value :
22920                 this.parseDate(Roo.form.DateField.superclass.getValue.call(this)) || "";
22921     },
22922
22923     /**
22924      * Sets the value of the date field.  You can pass a date object or any string that can be parsed into a valid
22925      * date, using DateField.format as the date format, according to the same rules as {@link Date#parseDate}
22926      * (the default format used is "m/d/y").
22927      * <br />Usage:
22928      * <pre><code>
22929 //All of these calls set the same date value (May 4, 2006)
22930
22931 //Pass a date object:
22932 var dt = new Date('5/4/06');
22933 dateField.setValue(dt);
22934
22935 //Pass a date string (default format):
22936 dateField.setValue('5/4/06');
22937
22938 //Pass a date string (custom format):
22939 dateField.format = 'Y-m-d';
22940 dateField.setValue('2006-5-4');
22941 </code></pre>
22942      * @param {String/Date} date The date or valid date string
22943      */
22944     setValue : function(date){
22945         if (this.hiddenField) {
22946             this.hiddenField.value = this.formatDate(this.parseDate(date), 'Y-m-d');
22947         }
22948         Roo.form.DateField.superclass.setValue.call(this, this.formatDate(this.parseDate(date)));
22949         // make sure the value field is always stored as a date..
22950         this.value = this.parseDate(date);
22951         
22952         
22953     },
22954
22955     // private
22956     parseDate : function(value){
22957         if(!value || value instanceof Date){
22958             return value;
22959         }
22960         var v = Date.parseDate(value, this.format);
22961          if (!v && this.useIso) {
22962             v = Date.parseDate(value, 'Y-m-d');
22963         }
22964         if(!v && this.altFormats){
22965             if(!this.altFormatsArray){
22966                 this.altFormatsArray = this.altFormats.split("|");
22967             }
22968             for(var i = 0, len = this.altFormatsArray.length; i < len && !v; i++){
22969                 v = Date.parseDate(value, this.altFormatsArray[i]);
22970             }
22971         }
22972         return v;
22973     },
22974
22975     // private
22976     formatDate : function(date, fmt){
22977         return (!date || !(date instanceof Date)) ?
22978                date : date.dateFormat(fmt || this.format);
22979     },
22980
22981     // private
22982     menuListeners : {
22983         select: function(m, d){
22984             
22985             this.setValue(d);
22986             this.fireEvent('select', this, d);
22987         },
22988         show : function(){ // retain focus styling
22989             this.onFocus();
22990         },
22991         hide : function(){
22992             this.focus.defer(10, this);
22993             var ml = this.menuListeners;
22994             this.menu.un("select", ml.select,  this);
22995             this.menu.un("show", ml.show,  this);
22996             this.menu.un("hide", ml.hide,  this);
22997         }
22998     },
22999
23000     // private
23001     // Implements the default empty TriggerField.onTriggerClick function to display the DatePicker
23002     onTriggerClick : function(){
23003         if(this.disabled){
23004             return;
23005         }
23006         if(this.menu == null){
23007             this.menu = new Roo.menu.DateMenu();
23008         }
23009         Roo.apply(this.menu.picker,  {
23010             showClear: this.allowBlank,
23011             minDate : this.minValue,
23012             maxDate : this.maxValue,
23013             disabledDatesRE : this.ddMatch,
23014             disabledDatesText : this.disabledDatesText,
23015             disabledDays : this.disabledDays,
23016             disabledDaysText : this.disabledDaysText,
23017             format : this.useIso ? 'Y-m-d' : this.format,
23018             minText : String.format(this.minText, this.formatDate(this.minValue)),
23019             maxText : String.format(this.maxText, this.formatDate(this.maxValue))
23020         });
23021         this.menu.on(Roo.apply({}, this.menuListeners, {
23022             scope:this
23023         }));
23024         this.menu.picker.setValue(this.getValue() || new Date());
23025         this.menu.show(this.el, "tl-bl?");
23026     },
23027
23028     beforeBlur : function(){
23029         var v = this.parseDate(this.getRawValue());
23030         if(v){
23031             this.setValue(v);
23032         }
23033     }
23034
23035     /** @cfg {Boolean} grow @hide */
23036     /** @cfg {Number} growMin @hide */
23037     /** @cfg {Number} growMax @hide */
23038     /**
23039      * @hide
23040      * @method autoSize
23041      */
23042 });/*
23043  * Based on:
23044  * Ext JS Library 1.1.1
23045  * Copyright(c) 2006-2007, Ext JS, LLC.
23046  *
23047  * Originally Released Under LGPL - original licence link has changed is not relivant.
23048  *
23049  * Fork - LGPL
23050  * <script type="text/javascript">
23051  */
23052  
23053 /**
23054  * @class Roo.form.MonthField
23055  * @extends Roo.form.TriggerField
23056  * Provides a date input field with a {@link Roo.DatePicker} dropdown and automatic date validation.
23057 * @constructor
23058 * Create a new MonthField
23059 * @param {Object} config
23060  */
23061 Roo.form.MonthField = function(config){
23062     
23063     Roo.form.MonthField.superclass.constructor.call(this, config);
23064     
23065       this.addEvents({
23066          
23067         /**
23068          * @event select
23069          * Fires when a date is selected
23070              * @param {Roo.form.MonthFieeld} combo This combo box
23071              * @param {Date} date The date selected
23072              */
23073         'select' : true
23074          
23075     });
23076     
23077     
23078     if(typeof this.minValue == "string") this.minValue = this.parseDate(this.minValue);
23079     if(typeof this.maxValue == "string") this.maxValue = this.parseDate(this.maxValue);
23080     this.ddMatch = null;
23081     if(this.disabledDates){
23082         var dd = this.disabledDates;
23083         var re = "(?:";
23084         for(var i = 0; i < dd.length; i++){
23085             re += dd[i];
23086             if(i != dd.length-1) re += "|";
23087         }
23088         this.ddMatch = new RegExp(re + ")");
23089     }
23090 };
23091
23092 Roo.extend(Roo.form.MonthField, Roo.form.TriggerField,  {
23093     /**
23094      * @cfg {String} format
23095      * The default date format string which can be overriden for localization support.  The format must be
23096      * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
23097      */
23098     format : "M Y",
23099     /**
23100      * @cfg {String} altFormats
23101      * Multiple date formats separated by "|" to try when parsing a user input value and it doesn't match the defined
23102      * format (defaults to 'm/d/Y|m-d-y|m-d-Y|m/d|m-d|d').
23103      */
23104     altFormats : "M Y|m/Y|m-y|m-Y|my|mY",
23105     /**
23106      * @cfg {Array} disabledDays
23107      * An array of days to disable, 0 based. For example, [0, 6] disables Sunday and Saturday (defaults to null).
23108      */
23109     disabledDays : [0,1,2,3,4,5,6],
23110     /**
23111      * @cfg {String} disabledDaysText
23112      * The tooltip to display when the date falls on a disabled day (defaults to 'Disabled')
23113      */
23114     disabledDaysText : "Disabled",
23115     /**
23116      * @cfg {Array} disabledDates
23117      * An array of "dates" to disable, as strings. These strings will be used to build a dynamic regular
23118      * expression so they are very powerful. Some examples:
23119      * <ul>
23120      * <li>["03/08/2003", "09/16/2003"] would disable those exact dates</li>
23121      * <li>["03/08", "09/16"] would disable those days for every year</li>
23122      * <li>["^03/08"] would only match the beginning (useful if you are using short years)</li>
23123      * <li>["03/../2006"] would disable every day in March 2006</li>
23124      * <li>["^03"] would disable every day in every March</li>
23125      * </ul>
23126      * In order to support regular expressions, if you are using a date format that has "." in it, you will have to
23127      * escape the dot when restricting dates. For example: ["03\\.08\\.03"].
23128      */
23129     disabledDates : null,
23130     /**
23131      * @cfg {String} disabledDatesText
23132      * The tooltip text to display when the date falls on a disabled date (defaults to 'Disabled')
23133      */
23134     disabledDatesText : "Disabled",
23135     /**
23136      * @cfg {Date/String} minValue
23137      * The minimum allowed date. Can be either a Javascript date object or a string date in a
23138      * valid format (defaults to null).
23139      */
23140     minValue : null,
23141     /**
23142      * @cfg {Date/String} maxValue
23143      * The maximum allowed date. Can be either a Javascript date object or a string date in a
23144      * valid format (defaults to null).
23145      */
23146     maxValue : null,
23147     /**
23148      * @cfg {String} minText
23149      * The error text to display when the date in the cell is before minValue (defaults to
23150      * 'The date in this field must be after {minValue}').
23151      */
23152     minText : "The date in this field must be equal to or after {0}",
23153     /**
23154      * @cfg {String} maxTextf
23155      * The error text to display when the date in the cell is after maxValue (defaults to
23156      * 'The date in this field must be before {maxValue}').
23157      */
23158     maxText : "The date in this field must be equal to or before {0}",
23159     /**
23160      * @cfg {String} invalidText
23161      * The error text to display when the date in the field is invalid (defaults to
23162      * '{value} is not a valid date - it must be in the format {format}').
23163      */
23164     invalidText : "{0} is not a valid date - it must be in the format {1}",
23165     /**
23166      * @cfg {String} triggerClass
23167      * An additional CSS class used to style the trigger button.  The trigger will always get the
23168      * class 'x-form-trigger' and triggerClass will be <b>appended</b> if specified (defaults to 'x-form-date-trigger'
23169      * which displays a calendar icon).
23170      */
23171     triggerClass : 'x-form-date-trigger',
23172     
23173
23174     /**
23175      * @cfg {Boolean} useIso
23176      * if enabled, then the date field will use a hidden field to store the 
23177      * real value as iso formated date. default (true)
23178      */ 
23179     useIso : true,
23180     /**
23181      * @cfg {String/Object} autoCreate
23182      * A DomHelper element spec, or true for a default element spec (defaults to
23183      * {tag: "input", type: "text", size: "10", autocomplete: "off"})
23184      */ 
23185     // private
23186     defaultAutoCreate : {tag: "input", type: "text", size: "10", autocomplete: "off"},
23187     
23188     // private
23189     hiddenField: false,
23190     
23191     hideMonthPicker : false,
23192     
23193     onRender : function(ct, position)
23194     {
23195         Roo.form.MonthField.superclass.onRender.call(this, ct, position);
23196         if (this.useIso) {
23197             this.el.dom.removeAttribute('name'); 
23198             this.hiddenField = this.el.insertSibling({ tag:'input', type:'hidden', name: this.name },
23199                     'before', true);
23200             this.hiddenField.value = this.value ? this.formatDate(this.value, 'Y-m-d') : '';
23201             // prevent input submission
23202             this.hiddenName = this.name;
23203         }
23204             
23205             
23206     },
23207     
23208     // private
23209     validateValue : function(value)
23210     {
23211         value = this.formatDate(value);
23212         if(!Roo.form.MonthField.superclass.validateValue.call(this, value)){
23213             return false;
23214         }
23215         if(value.length < 1){ // if it's blank and textfield didn't flag it then it's valid
23216              return true;
23217         }
23218         var svalue = value;
23219         value = this.parseDate(value);
23220         if(!value){
23221             this.markInvalid(String.format(this.invalidText, svalue, this.format));
23222             return false;
23223         }
23224         var time = value.getTime();
23225         if(this.minValue && time < this.minValue.getTime()){
23226             this.markInvalid(String.format(this.minText, this.formatDate(this.minValue)));
23227             return false;
23228         }
23229         if(this.maxValue && time > this.maxValue.getTime()){
23230             this.markInvalid(String.format(this.maxText, this.formatDate(this.maxValue)));
23231             return false;
23232         }
23233         /*if(this.disabledDays){
23234             var day = value.getDay();
23235             for(var i = 0; i < this.disabledDays.length; i++) {
23236                 if(day === this.disabledDays[i]){
23237                     this.markInvalid(this.disabledDaysText);
23238                     return false;
23239                 }
23240             }
23241         }
23242         */
23243         var fvalue = this.formatDate(value);
23244         /*if(this.ddMatch && this.ddMatch.test(fvalue)){
23245             this.markInvalid(String.format(this.disabledDatesText, fvalue));
23246             return false;
23247         }
23248         */
23249         return true;
23250     },
23251
23252     // private
23253     // Provides logic to override the default TriggerField.validateBlur which just returns true
23254     validateBlur : function(){
23255         return !this.menu || !this.menu.isVisible();
23256     },
23257
23258     /**
23259      * Returns the current date value of the date field.
23260      * @return {Date} The date value
23261      */
23262     getValue : function(){
23263         
23264         
23265         
23266         return  this.hiddenField ?
23267                 this.hiddenField.value :
23268                 this.parseDate(Roo.form.MonthField.superclass.getValue.call(this)) || "";
23269     },
23270
23271     /**
23272      * Sets the value of the date field.  You can pass a date object or any string that can be parsed into a valid
23273      * date, using MonthField.format as the date format, according to the same rules as {@link Date#parseDate}
23274      * (the default format used is "m/d/y").
23275      * <br />Usage:
23276      * <pre><code>
23277 //All of these calls set the same date value (May 4, 2006)
23278
23279 //Pass a date object:
23280 var dt = new Date('5/4/06');
23281 monthField.setValue(dt);
23282
23283 //Pass a date string (default format):
23284 monthField.setValue('5/4/06');
23285
23286 //Pass a date string (custom format):
23287 monthField.format = 'Y-m-d';
23288 monthField.setValue('2006-5-4');
23289 </code></pre>
23290      * @param {String/Date} date The date or valid date string
23291      */
23292     setValue : function(date){
23293         Roo.log('month setValue' + date);
23294         // can only be first of month..
23295         
23296         var val = this.parseDate(date);
23297         
23298         if (this.hiddenField) {
23299             this.hiddenField.value = this.formatDate(this.parseDate(date), 'Y-m-d');
23300         }
23301         Roo.form.MonthField.superclass.setValue.call(this, this.formatDate(this.parseDate(date)));
23302         this.value = this.parseDate(date);
23303     },
23304
23305     // private
23306     parseDate : function(value){
23307         if(!value || value instanceof Date){
23308             value = value ? Date.parseDate(value.format('Y-m') + '-01', 'Y-m-d') : null;
23309             return value;
23310         }
23311         var v = Date.parseDate(value, this.format);
23312         if (!v && this.useIso) {
23313             v = Date.parseDate(value, 'Y-m-d');
23314         }
23315         if (v) {
23316             // 
23317             v = Date.parseDate(v.format('Y-m') +'-01', 'Y-m-d');
23318         }
23319         
23320         
23321         if(!v && this.altFormats){
23322             if(!this.altFormatsArray){
23323                 this.altFormatsArray = this.altFormats.split("|");
23324             }
23325             for(var i = 0, len = this.altFormatsArray.length; i < len && !v; i++){
23326                 v = Date.parseDate(value, this.altFormatsArray[i]);
23327             }
23328         }
23329         return v;
23330     },
23331
23332     // private
23333     formatDate : function(date, fmt){
23334         return (!date || !(date instanceof Date)) ?
23335                date : date.dateFormat(fmt || this.format);
23336     },
23337
23338     // private
23339     menuListeners : {
23340         select: function(m, d){
23341             this.setValue(d);
23342             this.fireEvent('select', this, d);
23343         },
23344         show : function(){ // retain focus styling
23345             this.onFocus();
23346         },
23347         hide : function(){
23348             this.focus.defer(10, this);
23349             var ml = this.menuListeners;
23350             this.menu.un("select", ml.select,  this);
23351             this.menu.un("show", ml.show,  this);
23352             this.menu.un("hide", ml.hide,  this);
23353         }
23354     },
23355     // private
23356     // Implements the default empty TriggerField.onTriggerClick function to display the DatePicker
23357     onTriggerClick : function(){
23358         if(this.disabled){
23359             return;
23360         }
23361         if(this.menu == null){
23362             this.menu = new Roo.menu.DateMenu();
23363            
23364         }
23365         
23366         Roo.apply(this.menu.picker,  {
23367             
23368             showClear: this.allowBlank,
23369             minDate : this.minValue,
23370             maxDate : this.maxValue,
23371             disabledDatesRE : this.ddMatch,
23372             disabledDatesText : this.disabledDatesText,
23373             
23374             format : this.useIso ? 'Y-m-d' : this.format,
23375             minText : String.format(this.minText, this.formatDate(this.minValue)),
23376             maxText : String.format(this.maxText, this.formatDate(this.maxValue))
23377             
23378         });
23379          this.menu.on(Roo.apply({}, this.menuListeners, {
23380             scope:this
23381         }));
23382        
23383         
23384         var m = this.menu;
23385         var p = m.picker;
23386         
23387         // hide month picker get's called when we called by 'before hide';
23388         
23389         var ignorehide = true;
23390         p.hideMonthPicker  = function(disableAnim){
23391             if (ignorehide) {
23392                 return;
23393             }
23394              if(this.monthPicker){
23395                 Roo.log("hideMonthPicker called");
23396                 if(disableAnim === true){
23397                     this.monthPicker.hide();
23398                 }else{
23399                     this.monthPicker.slideOut('t', {duration:.2});
23400                     p.setValue(new Date(m.picker.mpSelYear, m.picker.mpSelMonth, 1));
23401                     p.fireEvent("select", this, this.value);
23402                     m.hide();
23403                 }
23404             }
23405         }
23406         
23407         Roo.log('picker set value');
23408         Roo.log(this.getValue());
23409         p.setValue(this.getValue() ? this.parseDate(this.getValue()) : new Date());
23410         m.show(this.el, 'tl-bl?');
23411         ignorehide  = false;
23412         // this will trigger hideMonthPicker..
23413         
23414         
23415         // hidden the day picker
23416         Roo.select('.x-date-picker table', true).first().dom.style.visibility = "hidden";
23417         
23418         
23419         
23420       
23421         
23422         p.showMonthPicker.defer(100, p);
23423     
23424         
23425        
23426     },
23427
23428     beforeBlur : function(){
23429         var v = this.parseDate(this.getRawValue());
23430         if(v){
23431             this.setValue(v);
23432         }
23433     }
23434
23435     /** @cfg {Boolean} grow @hide */
23436     /** @cfg {Number} growMin @hide */
23437     /** @cfg {Number} growMax @hide */
23438     /**
23439      * @hide
23440      * @method autoSize
23441      */
23442 });/*
23443  * Based on:
23444  * Ext JS Library 1.1.1
23445  * Copyright(c) 2006-2007, Ext JS, LLC.
23446  *
23447  * Originally Released Under LGPL - original licence link has changed is not relivant.
23448  *
23449  * Fork - LGPL
23450  * <script type="text/javascript">
23451  */
23452  
23453
23454 /**
23455  * @class Roo.form.ComboBox
23456  * @extends Roo.form.TriggerField
23457  * A combobox control with support for autocomplete, remote-loading, paging and many other features.
23458  * @constructor
23459  * Create a new ComboBox.
23460  * @param {Object} config Configuration options
23461  */
23462 Roo.form.ComboBox = function(config){
23463     Roo.form.ComboBox.superclass.constructor.call(this, config);
23464     this.addEvents({
23465         /**
23466          * @event expand
23467          * Fires when the dropdown list is expanded
23468              * @param {Roo.form.ComboBox} combo This combo box
23469              */
23470         'expand' : true,
23471         /**
23472          * @event collapse
23473          * Fires when the dropdown list is collapsed
23474              * @param {Roo.form.ComboBox} combo This combo box
23475              */
23476         'collapse' : true,
23477         /**
23478          * @event beforeselect
23479          * Fires before a list item is selected. Return false to cancel the selection.
23480              * @param {Roo.form.ComboBox} combo This combo box
23481              * @param {Roo.data.Record} record The data record returned from the underlying store
23482              * @param {Number} index The index of the selected item in the dropdown list
23483              */
23484         'beforeselect' : true,
23485         /**
23486          * @event select
23487          * Fires when a list item is selected
23488              * @param {Roo.form.ComboBox} combo This combo box
23489              * @param {Roo.data.Record} record The data record returned from the underlying store (or false on clear)
23490              * @param {Number} index The index of the selected item in the dropdown list
23491              */
23492         'select' : true,
23493         /**
23494          * @event beforequery
23495          * Fires before all queries are processed. Return false to cancel the query or set cancel to true.
23496          * The event object passed has these properties:
23497              * @param {Roo.form.ComboBox} combo This combo box
23498              * @param {String} query The query
23499              * @param {Boolean} forceAll true to force "all" query
23500              * @param {Boolean} cancel true to cancel the query
23501              * @param {Object} e The query event object
23502              */
23503         'beforequery': true,
23504          /**
23505          * @event add
23506          * Fires when the 'add' icon is pressed (add a listener to enable add button)
23507              * @param {Roo.form.ComboBox} combo This combo box
23508              */
23509         'add' : true,
23510         /**
23511          * @event edit
23512          * Fires when the 'edit' icon is pressed (add a listener to enable add button)
23513              * @param {Roo.form.ComboBox} combo This combo box
23514              * @param {Roo.data.Record|false} record The data record returned from the underlying store (or false on nothing selected)
23515              */
23516         'edit' : true
23517         
23518         
23519     });
23520     if(this.transform){
23521         this.allowDomMove = false;
23522         var s = Roo.getDom(this.transform);
23523         if(!this.hiddenName){
23524             this.hiddenName = s.name;
23525         }
23526         if(!this.store){
23527             this.mode = 'local';
23528             var d = [], opts = s.options;
23529             for(var i = 0, len = opts.length;i < len; i++){
23530                 var o = opts[i];
23531                 var value = (Roo.isIE ? o.getAttributeNode('value').specified : o.hasAttribute('value')) ? o.value : o.text;
23532                 if(o.selected) {
23533                     this.value = value;
23534                 }
23535                 d.push([value, o.text]);
23536             }
23537             this.store = new Roo.data.SimpleStore({
23538                 'id': 0,
23539                 fields: ['value', 'text'],
23540                 data : d
23541             });
23542             this.valueField = 'value';
23543             this.displayField = 'text';
23544         }
23545         s.name = Roo.id(); // wipe out the name in case somewhere else they have a reference
23546         if(!this.lazyRender){
23547             this.target = true;
23548             this.el = Roo.DomHelper.insertBefore(s, this.autoCreate || this.defaultAutoCreate);
23549             s.parentNode.removeChild(s); // remove it
23550             this.render(this.el.parentNode);
23551         }else{
23552             s.parentNode.removeChild(s); // remove it
23553         }
23554
23555     }
23556     if (this.store) {
23557         this.store = Roo.factory(this.store, Roo.data);
23558     }
23559     
23560     this.selectedIndex = -1;
23561     if(this.mode == 'local'){
23562         if(config.queryDelay === undefined){
23563             this.queryDelay = 10;
23564         }
23565         if(config.minChars === undefined){
23566             this.minChars = 0;
23567         }
23568     }
23569 };
23570
23571 Roo.extend(Roo.form.ComboBox, Roo.form.TriggerField, {
23572     /**
23573      * @cfg {String/HTMLElement/Element} transform The id, DOM node or element of an existing select to convert to a ComboBox
23574      */
23575     /**
23576      * @cfg {Boolean} lazyRender True to prevent the ComboBox from rendering until requested (should always be used when
23577      * rendering into an Roo.Editor, defaults to false)
23578      */
23579     /**
23580      * @cfg {Boolean/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to:
23581      * {tag: "input", type: "text", size: "24", autocomplete: "off"})
23582      */
23583     /**
23584      * @cfg {Roo.data.Store} store The data store to which this combo is bound (defaults to undefined)
23585      */
23586     /**
23587      * @cfg {String} title If supplied, a header element is created containing this text and added into the top of
23588      * the dropdown list (defaults to undefined, with no header element)
23589      */
23590
23591      /**
23592      * @cfg {String/Roo.Template} tpl The template to use to render the output
23593      */
23594      
23595     // private
23596     defaultAutoCreate : {tag: "input", type: "text", size: "24", autocomplete: "off"},
23597     /**
23598      * @cfg {Number} listWidth The width in pixels of the dropdown list (defaults to the width of the ComboBox field)
23599      */
23600     listWidth: undefined,
23601     /**
23602      * @cfg {String} displayField The underlying data field name to bind to this CombBox (defaults to undefined if
23603      * mode = 'remote' or 'text' if mode = 'local')
23604      */
23605     displayField: undefined,
23606     /**
23607      * @cfg {String} valueField The underlying data value name to bind to this CombBox (defaults to undefined if
23608      * mode = 'remote' or 'value' if mode = 'local'). 
23609      * Note: use of a valueField requires the user make a selection
23610      * in order for a value to be mapped.
23611      */
23612     valueField: undefined,
23613     
23614     
23615     /**
23616      * @cfg {String} hiddenName If specified, a hidden form field with this name is dynamically generated to store the
23617      * field's data value (defaults to the underlying DOM element's name)
23618      */
23619     hiddenName: undefined,
23620     /**
23621      * @cfg {String} listClass CSS class to apply to the dropdown list element (defaults to '')
23622      */
23623     listClass: '',
23624     /**
23625      * @cfg {String} selectedClass CSS class to apply to the selected item in the dropdown list (defaults to 'x-combo-selected')
23626      */
23627     selectedClass: 'x-combo-selected',
23628     /**
23629      * @cfg {String} triggerClass An additional CSS class used to style the trigger button.  The trigger will always get the
23630      * class 'x-form-trigger' and triggerClass will be <b>appended</b> if specified (defaults to 'x-form-arrow-trigger'
23631      * which displays a downward arrow icon).
23632      */
23633     triggerClass : 'x-form-arrow-trigger',
23634     /**
23635      * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
23636      */
23637     shadow:'sides',
23638     /**
23639      * @cfg {String} listAlign A valid anchor position value. See {@link Roo.Element#alignTo} for details on supported
23640      * anchor positions (defaults to 'tl-bl')
23641      */
23642     listAlign: 'tl-bl?',
23643     /**
23644      * @cfg {Number} maxHeight The maximum height in pixels of the dropdown list before scrollbars are shown (defaults to 300)
23645      */
23646     maxHeight: 300,
23647     /**
23648      * @cfg {String} triggerAction The action to execute when the trigger field is activated.  Use 'all' to run the
23649      * query specified by the allQuery config option (defaults to 'query')
23650      */
23651     triggerAction: 'query',
23652     /**
23653      * @cfg {Number} minChars The minimum number of characters the user must type before autocomplete and typeahead activate
23654      * (defaults to 4, does not apply if editable = false)
23655      */
23656     minChars : 4,
23657     /**
23658      * @cfg {Boolean} typeAhead True to populate and autoselect the remainder of the text being typed after a configurable
23659      * delay (typeAheadDelay) if it matches a known value (defaults to false)
23660      */
23661     typeAhead: false,
23662     /**
23663      * @cfg {Number} queryDelay The length of time in milliseconds to delay between the start of typing and sending the
23664      * query to filter the dropdown list (defaults to 500 if mode = 'remote' or 10 if mode = 'local')
23665      */
23666     queryDelay: 500,
23667     /**
23668      * @cfg {Number} pageSize If greater than 0, a paging toolbar is displayed in the footer of the dropdown list and the
23669      * filter queries will execute with page start and limit parameters.  Only applies when mode = 'remote' (defaults to 0)
23670      */
23671     pageSize: 0,
23672     /**
23673      * @cfg {Boolean} selectOnFocus True to select any existing text in the field immediately on focus.  Only applies
23674      * when editable = true (defaults to false)
23675      */
23676     selectOnFocus:false,
23677     /**
23678      * @cfg {String} queryParam Name of the query as it will be passed on the querystring (defaults to 'query')
23679      */
23680     queryParam: 'query',
23681     /**
23682      * @cfg {String} loadingText The text to display in the dropdown list while data is loading.  Only applies
23683      * when mode = 'remote' (defaults to 'Loading...')
23684      */
23685     loadingText: 'Loading...',
23686     /**
23687      * @cfg {Boolean} resizable True to add a resize handle to the bottom of the dropdown list (defaults to false)
23688      */
23689     resizable: false,
23690     /**
23691      * @cfg {Number} handleHeight The height in pixels of the dropdown list resize handle if resizable = true (defaults to 8)
23692      */
23693     handleHeight : 8,
23694     /**
23695      * @cfg {Boolean} editable False to prevent the user from typing text directly into the field, just like a
23696      * traditional select (defaults to true)
23697      */
23698     editable: true,
23699     /**
23700      * @cfg {String} allQuery The text query to send to the server to return all records for the list with no filtering (defaults to '')
23701      */
23702     allQuery: '',
23703     /**
23704      * @cfg {String} mode Set to 'local' if the ComboBox loads local data (defaults to 'remote' which loads from the server)
23705      */
23706     mode: 'remote',
23707     /**
23708      * @cfg {Number} minListWidth The minimum width of the dropdown list in pixels (defaults to 70, will be ignored if
23709      * listWidth has a higher value)
23710      */
23711     minListWidth : 70,
23712     /**
23713      * @cfg {Boolean} forceSelection True to restrict the selected value to one of the values in the list, false to
23714      * allow the user to set arbitrary text into the field (defaults to false)
23715      */
23716     forceSelection:false,
23717     /**
23718      * @cfg {Number} typeAheadDelay The length of time in milliseconds to wait until the typeahead text is displayed
23719      * if typeAhead = true (defaults to 250)
23720      */
23721     typeAheadDelay : 250,
23722     /**
23723      * @cfg {String} valueNotFoundText When using a name/value combo, if the value passed to setValue is not found in
23724      * the store, valueNotFoundText will be displayed as the field text if defined (defaults to undefined)
23725      */
23726     valueNotFoundText : undefined,
23727     /**
23728      * @cfg {Boolean} blockFocus Prevents all focus calls, so it can work with things like HTML edtor bar
23729      */
23730     blockFocus : false,
23731     
23732     /**
23733      * @cfg {Boolean} disableClear Disable showing of clear button.
23734      */
23735     disableClear : false,
23736     /**
23737      * @cfg {Boolean} alwaysQuery  Disable caching of results, and always send query
23738      */
23739     alwaysQuery : false,
23740     
23741     //private
23742     addicon : false,
23743     editicon: false,
23744     
23745     // element that contains real text value.. (when hidden is used..)
23746      
23747     // private
23748     onRender : function(ct, position){
23749         Roo.form.ComboBox.superclass.onRender.call(this, ct, position);
23750         if(this.hiddenName){
23751             this.hiddenField = this.el.insertSibling({tag:'input', type:'hidden', name: this.hiddenName, id:  (this.hiddenId||this.hiddenName)},
23752                     'before', true);
23753             this.hiddenField.value =
23754                 this.hiddenValue !== undefined ? this.hiddenValue :
23755                 this.value !== undefined ? this.value : '';
23756
23757             // prevent input submission
23758             this.el.dom.removeAttribute('name');
23759              
23760              
23761         }
23762         if(Roo.isGecko){
23763             this.el.dom.setAttribute('autocomplete', 'off');
23764         }
23765
23766         var cls = 'x-combo-list';
23767
23768         this.list = new Roo.Layer({
23769             shadow: this.shadow, cls: [cls, this.listClass].join(' '), constrain:false
23770         });
23771
23772         var lw = this.listWidth || Math.max(this.wrap.getWidth(), this.minListWidth);
23773         this.list.setWidth(lw);
23774         this.list.swallowEvent('mousewheel');
23775         this.assetHeight = 0;
23776
23777         if(this.title){
23778             this.header = this.list.createChild({cls:cls+'-hd', html: this.title});
23779             this.assetHeight += this.header.getHeight();
23780         }
23781
23782         this.innerList = this.list.createChild({cls:cls+'-inner'});
23783         this.innerList.on('mouseover', this.onViewOver, this);
23784         this.innerList.on('mousemove', this.onViewMove, this);
23785         this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
23786         
23787         if(this.allowBlank && !this.pageSize && !this.disableClear){
23788             this.footer = this.list.createChild({cls:cls+'-ft'});
23789             this.pageTb = new Roo.Toolbar(this.footer);
23790            
23791         }
23792         if(this.pageSize){
23793             this.footer = this.list.createChild({cls:cls+'-ft'});
23794             this.pageTb = new Roo.PagingToolbar(this.footer, this.store,
23795                     {pageSize: this.pageSize});
23796             
23797         }
23798         
23799         if (this.pageTb && this.allowBlank && !this.disableClear) {
23800             var _this = this;
23801             this.pageTb.add(new Roo.Toolbar.Fill(), {
23802                 cls: 'x-btn-icon x-btn-clear',
23803                 text: '&#160;',
23804                 handler: function()
23805                 {
23806                     _this.collapse();
23807                     _this.clearValue();
23808                     _this.onSelect(false, -1);
23809                 }
23810             });
23811         }
23812         if (this.footer) {
23813             this.assetHeight += this.footer.getHeight();
23814         }
23815         
23816
23817         if(!this.tpl){
23818             this.tpl = '<div class="'+cls+'-item">{' + this.displayField + '}</div>';
23819         }
23820
23821         this.view = new Roo.View(this.innerList, this.tpl, {
23822             singleSelect:true, store: this.store, selectedClass: this.selectedClass
23823         });
23824
23825         this.view.on('click', this.onViewClick, this);
23826
23827         this.store.on('beforeload', this.onBeforeLoad, this);
23828         this.store.on('load', this.onLoad, this);
23829         this.store.on('loadexception', this.onLoadException, this);
23830
23831         if(this.resizable){
23832             this.resizer = new Roo.Resizable(this.list,  {
23833                pinned:true, handles:'se'
23834             });
23835             this.resizer.on('resize', function(r, w, h){
23836                 this.maxHeight = h-this.handleHeight-this.list.getFrameWidth('tb')-this.assetHeight;
23837                 this.listWidth = w;
23838                 this.innerList.setWidth(w - this.list.getFrameWidth('lr'));
23839                 this.restrictHeight();
23840             }, this);
23841             this[this.pageSize?'footer':'innerList'].setStyle('margin-bottom', this.handleHeight+'px');
23842         }
23843         if(!this.editable){
23844             this.editable = true;
23845             this.setEditable(false);
23846         }  
23847         
23848         
23849         if (typeof(this.events.add.listeners) != 'undefined') {
23850             
23851             this.addicon = this.wrap.createChild(
23852                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-add' });  
23853        
23854             this.addicon.on('click', function(e) {
23855                 this.fireEvent('add', this);
23856             }, this);
23857         }
23858         if (typeof(this.events.edit.listeners) != 'undefined') {
23859             
23860             this.editicon = this.wrap.createChild(
23861                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-edit' });  
23862             if (this.addicon) {
23863                 this.editicon.setStyle('margin-left', '40px');
23864             }
23865             this.editicon.on('click', function(e) {
23866                 
23867                 // we fire even  if inothing is selected..
23868                 this.fireEvent('edit', this, this.lastData );
23869                 
23870             }, this);
23871         }
23872         
23873         
23874         
23875     },
23876
23877     // private
23878     initEvents : function(){
23879         Roo.form.ComboBox.superclass.initEvents.call(this);
23880
23881         this.keyNav = new Roo.KeyNav(this.el, {
23882             "up" : function(e){
23883                 this.inKeyMode = true;
23884                 this.selectPrev();
23885             },
23886
23887             "down" : function(e){
23888                 if(!this.isExpanded()){
23889                     this.onTriggerClick();
23890                 }else{
23891                     this.inKeyMode = true;
23892                     this.selectNext();
23893                 }
23894             },
23895
23896             "enter" : function(e){
23897                 this.onViewClick();
23898                 //return true;
23899             },
23900
23901             "esc" : function(e){
23902                 this.collapse();
23903             },
23904
23905             "tab" : function(e){
23906                 this.onViewClick(false);
23907                 this.fireEvent("specialkey", this, e);
23908                 return true;
23909             },
23910
23911             scope : this,
23912
23913             doRelay : function(foo, bar, hname){
23914                 if(hname == 'down' || this.scope.isExpanded()){
23915                    return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
23916                 }
23917                 return true;
23918             },
23919
23920             forceKeyDown: true
23921         });
23922         this.queryDelay = Math.max(this.queryDelay || 10,
23923                 this.mode == 'local' ? 10 : 250);
23924         this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
23925         if(this.typeAhead){
23926             this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
23927         }
23928         if(this.editable !== false){
23929             this.el.on("keyup", this.onKeyUp, this);
23930         }
23931         if(this.forceSelection){
23932             this.on('blur', this.doForce, this);
23933         }
23934     },
23935
23936     onDestroy : function(){
23937         if(this.view){
23938             this.view.setStore(null);
23939             this.view.el.removeAllListeners();
23940             this.view.el.remove();
23941             this.view.purgeListeners();
23942         }
23943         if(this.list){
23944             this.list.destroy();
23945         }
23946         if(this.store){
23947             this.store.un('beforeload', this.onBeforeLoad, this);
23948             this.store.un('load', this.onLoad, this);
23949             this.store.un('loadexception', this.onLoadException, this);
23950         }
23951         Roo.form.ComboBox.superclass.onDestroy.call(this);
23952     },
23953
23954     // private
23955     fireKey : function(e){
23956         if(e.isNavKeyPress() && !this.list.isVisible()){
23957             this.fireEvent("specialkey", this, e);
23958         }
23959     },
23960
23961     // private
23962     onResize: function(w, h){
23963         Roo.form.ComboBox.superclass.onResize.apply(this, arguments);
23964         
23965         if(typeof w != 'number'){
23966             // we do not handle it!?!?
23967             return;
23968         }
23969         var tw = this.trigger.getWidth();
23970         tw += this.addicon ? this.addicon.getWidth() : 0;
23971         tw += this.editicon ? this.editicon.getWidth() : 0;
23972         var x = w - tw;
23973         this.el.setWidth( this.adjustWidth('input', x));
23974             
23975         this.trigger.setStyle('left', x+'px');
23976         
23977         if(this.list && this.listWidth === undefined){
23978             var lw = Math.max(x + this.trigger.getWidth(), this.minListWidth);
23979             this.list.setWidth(lw);
23980             this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
23981         }
23982         
23983     
23984         
23985     },
23986
23987     /**
23988      * Allow or prevent the user from directly editing the field text.  If false is passed,
23989      * the user will only be able to select from the items defined in the dropdown list.  This method
23990      * is the runtime equivalent of setting the 'editable' config option at config time.
23991      * @param {Boolean} value True to allow the user to directly edit the field text
23992      */
23993     setEditable : function(value){
23994         if(value == this.editable){
23995             return;
23996         }
23997         this.editable = value;
23998         if(!value){
23999             this.el.dom.setAttribute('readOnly', true);
24000             this.el.on('mousedown', this.onTriggerClick,  this);
24001             this.el.addClass('x-combo-noedit');
24002         }else{
24003             this.el.dom.setAttribute('readOnly', false);
24004             this.el.un('mousedown', this.onTriggerClick,  this);
24005             this.el.removeClass('x-combo-noedit');
24006         }
24007     },
24008
24009     // private
24010     onBeforeLoad : function(){
24011         if(!this.hasFocus){
24012             return;
24013         }
24014         this.innerList.update(this.loadingText ?
24015                '<div class="loading-indicator">'+this.loadingText+'</div>' : '');
24016         this.restrictHeight();
24017         this.selectedIndex = -1;
24018     },
24019
24020     // private
24021     onLoad : function(){
24022         if(!this.hasFocus){
24023             return;
24024         }
24025         if(this.store.getCount() > 0){
24026             this.expand();
24027             this.restrictHeight();
24028             if(this.lastQuery == this.allQuery){
24029                 if(this.editable){
24030                     this.el.dom.select();
24031                 }
24032                 if(!this.selectByValue(this.value, true)){
24033                     this.select(0, true);
24034                 }
24035             }else{
24036                 this.selectNext();
24037                 if(this.typeAhead && this.lastKey != Roo.EventObject.BACKSPACE && this.lastKey != Roo.EventObject.DELETE){
24038                     this.taTask.delay(this.typeAheadDelay);
24039                 }
24040             }
24041         }else{
24042             this.onEmptyResults();
24043         }
24044         //this.el.focus();
24045     },
24046     // private
24047     onLoadException : function()
24048     {
24049         this.collapse();
24050         Roo.log(this.store.reader.jsonData);
24051         if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
24052             Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
24053         }
24054         
24055         
24056     },
24057     // private
24058     onTypeAhead : function(){
24059         if(this.store.getCount() > 0){
24060             var r = this.store.getAt(0);
24061             var newValue = r.data[this.displayField];
24062             var len = newValue.length;
24063             var selStart = this.getRawValue().length;
24064             if(selStart != len){
24065                 this.setRawValue(newValue);
24066                 this.selectText(selStart, newValue.length);
24067             }
24068         }
24069     },
24070
24071     // private
24072     onSelect : function(record, index){
24073         if(this.fireEvent('beforeselect', this, record, index) !== false){
24074             this.setFromData(index > -1 ? record.data : false);
24075             this.collapse();
24076             this.fireEvent('select', this, record, index);
24077         }
24078     },
24079
24080     /**
24081      * Returns the currently selected field value or empty string if no value is set.
24082      * @return {String} value The selected value
24083      */
24084     getValue : function(){
24085         if(this.valueField){
24086             return typeof this.value != 'undefined' ? this.value : '';
24087         }else{
24088             return Roo.form.ComboBox.superclass.getValue.call(this);
24089         }
24090     },
24091
24092     /**
24093      * Clears any text/value currently set in the field
24094      */
24095     clearValue : function(){
24096         if(this.hiddenField){
24097             this.hiddenField.value = '';
24098         }
24099         this.value = '';
24100         this.setRawValue('');
24101         this.lastSelectionText = '';
24102         
24103     },
24104
24105     /**
24106      * Sets the specified value into the field.  If the value finds a match, the corresponding record text
24107      * will be displayed in the field.  If the value does not match the data value of an existing item,
24108      * and the valueNotFoundText config option is defined, it will be displayed as the default field text.
24109      * Otherwise the field will be blank (although the value will still be set).
24110      * @param {String} value The value to match
24111      */
24112     setValue : function(v){
24113         var text = v;
24114         if(this.valueField){
24115             var r = this.findRecord(this.valueField, v);
24116             if(r){
24117                 text = r.data[this.displayField];
24118             }else if(this.valueNotFoundText !== undefined){
24119                 text = this.valueNotFoundText;
24120             }
24121         }
24122         this.lastSelectionText = text;
24123         if(this.hiddenField){
24124             this.hiddenField.value = v;
24125         }
24126         Roo.form.ComboBox.superclass.setValue.call(this, text);
24127         this.value = v;
24128     },
24129     /**
24130      * @property {Object} the last set data for the element
24131      */
24132     
24133     lastData : false,
24134     /**
24135      * Sets the value of the field based on a object which is related to the record format for the store.
24136      * @param {Object} value the value to set as. or false on reset?
24137      */
24138     setFromData : function(o){
24139         var dv = ''; // display value
24140         var vv = ''; // value value..
24141         this.lastData = o;
24142         if (this.displayField) {
24143             dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
24144         } else {
24145             // this is an error condition!!!
24146             Roo.log('no  displayField value set for '+ (this.name ? this.name : this.id));
24147         }
24148         
24149         if(this.valueField){
24150             vv = !o || typeof(o[this.valueField]) == 'undefined' ? dv : o[this.valueField];
24151         }
24152         if(this.hiddenField){
24153             this.hiddenField.value = vv;
24154             
24155             this.lastSelectionText = dv;
24156             Roo.form.ComboBox.superclass.setValue.call(this, dv);
24157             this.value = vv;
24158             return;
24159         }
24160         // no hidden field.. - we store the value in 'value', but still display
24161         // display field!!!!
24162         this.lastSelectionText = dv;
24163         Roo.form.ComboBox.superclass.setValue.call(this, dv);
24164         this.value = vv;
24165         
24166         
24167     },
24168     // private
24169     reset : function(){
24170         // overridden so that last data is reset..
24171         this.setValue(this.originalValue);
24172         this.clearInvalid();
24173         this.lastData = false;
24174         if (this.view) {
24175             this.view.clearSelections();
24176         }
24177     },
24178     // private
24179     findRecord : function(prop, value){
24180         var record;
24181         if(this.store.getCount() > 0){
24182             this.store.each(function(r){
24183                 if(r.data[prop] == value){
24184                     record = r;
24185                     return false;
24186                 }
24187                 return true;
24188             });
24189         }
24190         return record;
24191     },
24192     
24193     getName: function()
24194     {
24195         // returns hidden if it's set..
24196         if (!this.rendered) {return ''};
24197         return !this.hiddenName && this.el.dom.name  ? this.el.dom.name : (this.hiddenName || '');
24198         
24199     },
24200     // private
24201     onViewMove : function(e, t){
24202         this.inKeyMode = false;
24203     },
24204
24205     // private
24206     onViewOver : function(e, t){
24207         if(this.inKeyMode){ // prevent key nav and mouse over conflicts
24208             return;
24209         }
24210         var item = this.view.findItemFromChild(t);
24211         if(item){
24212             var index = this.view.indexOf(item);
24213             this.select(index, false);
24214         }
24215     },
24216
24217     // private
24218     onViewClick : function(doFocus)
24219     {
24220         var index = this.view.getSelectedIndexes()[0];
24221         var r = this.store.getAt(index);
24222         if(r){
24223             this.onSelect(r, index);
24224         }
24225         if(doFocus !== false && !this.blockFocus){
24226             this.el.focus();
24227         }
24228     },
24229
24230     // private
24231     restrictHeight : function(){
24232         this.innerList.dom.style.height = '';
24233         var inner = this.innerList.dom;
24234         var h = Math.max(inner.clientHeight, inner.offsetHeight, inner.scrollHeight);
24235         this.innerList.setHeight(h < this.maxHeight ? 'auto' : this.maxHeight);
24236         this.list.beginUpdate();
24237         this.list.setHeight(this.innerList.getHeight()+this.list.getFrameWidth('tb')+(this.resizable?this.handleHeight:0)+this.assetHeight);
24238         this.list.alignTo(this.el, this.listAlign);
24239         this.list.endUpdate();
24240     },
24241
24242     // private
24243     onEmptyResults : function(){
24244         this.collapse();
24245     },
24246
24247     /**
24248      * Returns true if the dropdown list is expanded, else false.
24249      */
24250     isExpanded : function(){
24251         return this.list.isVisible();
24252     },
24253
24254     /**
24255      * Select an item in the dropdown list by its data value. This function does NOT cause the select event to fire.
24256      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
24257      * @param {String} value The data value of the item to select
24258      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
24259      * selected item if it is not currently in view (defaults to true)
24260      * @return {Boolean} True if the value matched an item in the list, else false
24261      */
24262     selectByValue : function(v, scrollIntoView){
24263         if(v !== undefined && v !== null){
24264             var r = this.findRecord(this.valueField || this.displayField, v);
24265             if(r){
24266                 this.select(this.store.indexOf(r), scrollIntoView);
24267                 return true;
24268             }
24269         }
24270         return false;
24271     },
24272
24273     /**
24274      * Select an item in the dropdown list by its numeric index in the list. This function does NOT cause the select event to fire.
24275      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
24276      * @param {Number} index The zero-based index of the list item to select
24277      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
24278      * selected item if it is not currently in view (defaults to true)
24279      */
24280     select : function(index, scrollIntoView){
24281         this.selectedIndex = index;
24282         this.view.select(index);
24283         if(scrollIntoView !== false){
24284             var el = this.view.getNode(index);
24285             if(el){
24286                 this.innerList.scrollChildIntoView(el, false);
24287             }
24288         }
24289     },
24290
24291     // private
24292     selectNext : function(){
24293         var ct = this.store.getCount();
24294         if(ct > 0){
24295             if(this.selectedIndex == -1){
24296                 this.select(0);
24297             }else if(this.selectedIndex < ct-1){
24298                 this.select(this.selectedIndex+1);
24299             }
24300         }
24301     },
24302
24303     // private
24304     selectPrev : function(){
24305         var ct = this.store.getCount();
24306         if(ct > 0){
24307             if(this.selectedIndex == -1){
24308                 this.select(0);
24309             }else if(this.selectedIndex != 0){
24310                 this.select(this.selectedIndex-1);
24311             }
24312         }
24313     },
24314
24315     // private
24316     onKeyUp : function(e){
24317         if(this.editable !== false && !e.isSpecialKey()){
24318             this.lastKey = e.getKey();
24319             this.dqTask.delay(this.queryDelay);
24320         }
24321     },
24322
24323     // private
24324     validateBlur : function(){
24325         return !this.list || !this.list.isVisible();   
24326     },
24327
24328     // private
24329     initQuery : function(){
24330         this.doQuery(this.getRawValue());
24331     },
24332
24333     // private
24334     doForce : function(){
24335         if(this.el.dom.value.length > 0){
24336             this.el.dom.value =
24337                 this.lastSelectionText === undefined ? '' : this.lastSelectionText;
24338              
24339         }
24340     },
24341
24342     /**
24343      * Execute a query to filter the dropdown list.  Fires the beforequery event prior to performing the
24344      * query allowing the query action to be canceled if needed.
24345      * @param {String} query The SQL query to execute
24346      * @param {Boolean} forceAll True to force the query to execute even if there are currently fewer characters
24347      * in the field than the minimum specified by the minChars config option.  It also clears any filter previously
24348      * saved in the current store (defaults to false)
24349      */
24350     doQuery : function(q, forceAll){
24351         if(q === undefined || q === null){
24352             q = '';
24353         }
24354         var qe = {
24355             query: q,
24356             forceAll: forceAll,
24357             combo: this,
24358             cancel:false
24359         };
24360         if(this.fireEvent('beforequery', qe)===false || qe.cancel){
24361             return false;
24362         }
24363         q = qe.query;
24364         forceAll = qe.forceAll;
24365         if(forceAll === true || (q.length >= this.minChars)){
24366             if(this.lastQuery != q || this.alwaysQuery){
24367                 this.lastQuery = q;
24368                 if(this.mode == 'local'){
24369                     this.selectedIndex = -1;
24370                     if(forceAll){
24371                         this.store.clearFilter();
24372                     }else{
24373                         this.store.filter(this.displayField, q);
24374                     }
24375                     this.onLoad();
24376                 }else{
24377                     this.store.baseParams[this.queryParam] = q;
24378                     this.store.load({
24379                         params: this.getParams(q)
24380                     });
24381                     this.expand();
24382                 }
24383             }else{
24384                 this.selectedIndex = -1;
24385                 this.onLoad();   
24386             }
24387         }
24388     },
24389
24390     // private
24391     getParams : function(q){
24392         var p = {};
24393         //p[this.queryParam] = q;
24394         if(this.pageSize){
24395             p.start = 0;
24396             p.limit = this.pageSize;
24397         }
24398         return p;
24399     },
24400
24401     /**
24402      * Hides the dropdown list if it is currently expanded. Fires the 'collapse' event on completion.
24403      */
24404     collapse : function(){
24405         if(!this.isExpanded()){
24406             return;
24407         }
24408         this.list.hide();
24409         Roo.get(document).un('mousedown', this.collapseIf, this);
24410         Roo.get(document).un('mousewheel', this.collapseIf, this);
24411         if (!this.editable) {
24412             Roo.get(document).un('keydown', this.listKeyPress, this);
24413         }
24414         this.fireEvent('collapse', this);
24415     },
24416
24417     // private
24418     collapseIf : function(e){
24419         if(!e.within(this.wrap) && !e.within(this.list)){
24420             this.collapse();
24421         }
24422     },
24423
24424     /**
24425      * Expands the dropdown list if it is currently hidden. Fires the 'expand' event on completion.
24426      */
24427     expand : function(){
24428         if(this.isExpanded() || !this.hasFocus){
24429             return;
24430         }
24431         this.list.alignTo(this.el, this.listAlign);
24432         this.list.show();
24433         Roo.get(document).on('mousedown', this.collapseIf, this);
24434         Roo.get(document).on('mousewheel', this.collapseIf, this);
24435         if (!this.editable) {
24436             Roo.get(document).on('keydown', this.listKeyPress, this);
24437         }
24438         
24439         this.fireEvent('expand', this);
24440     },
24441
24442     // private
24443     // Implements the default empty TriggerField.onTriggerClick function
24444     onTriggerClick : function(){
24445         if(this.disabled){
24446             return;
24447         }
24448         if(this.isExpanded()){
24449             this.collapse();
24450             if (!this.blockFocus) {
24451                 this.el.focus();
24452             }
24453             
24454         }else {
24455             this.hasFocus = true;
24456             if(this.triggerAction == 'all') {
24457                 this.doQuery(this.allQuery, true);
24458             } else {
24459                 this.doQuery(this.getRawValue());
24460             }
24461             if (!this.blockFocus) {
24462                 this.el.focus();
24463             }
24464         }
24465     },
24466     listKeyPress : function(e)
24467     {
24468         //Roo.log('listkeypress');
24469         // scroll to first matching element based on key pres..
24470         if (e.isSpecialKey()) {
24471             return false;
24472         }
24473         var k = String.fromCharCode(e.getKey()).toUpperCase();
24474         //Roo.log(k);
24475         var match  = false;
24476         var csel = this.view.getSelectedNodes();
24477         var cselitem = false;
24478         if (csel.length) {
24479             var ix = this.view.indexOf(csel[0]);
24480             cselitem  = this.store.getAt(ix);
24481             if (!cselitem.get(this.displayField) || cselitem.get(this.displayField).substring(0,1).toUpperCase() != k) {
24482                 cselitem = false;
24483             }
24484             
24485         }
24486         
24487         this.store.each(function(v) { 
24488             if (cselitem) {
24489                 // start at existing selection.
24490                 if (cselitem.id == v.id) {
24491                     cselitem = false;
24492                 }
24493                 return;
24494             }
24495                 
24496             if (v.get(this.displayField) && v.get(this.displayField).substring(0,1).toUpperCase() == k) {
24497                 match = this.store.indexOf(v);
24498                 return false;
24499             }
24500         }, this);
24501         
24502         if (match === false) {
24503             return true; // no more action?
24504         }
24505         // scroll to?
24506         this.view.select(match);
24507         var sn = Roo.get(this.view.getSelectedNodes()[0])
24508         sn.scrollIntoView(sn.dom.parentNode, false);
24509     }
24510
24511     /** 
24512     * @cfg {Boolean} grow 
24513     * @hide 
24514     */
24515     /** 
24516     * @cfg {Number} growMin 
24517     * @hide 
24518     */
24519     /** 
24520     * @cfg {Number} growMax 
24521     * @hide 
24522     */
24523     /**
24524      * @hide
24525      * @method autoSize
24526      */
24527 });/*
24528  * Copyright(c) 2010-2012, Roo J Solutions Limited
24529  *
24530  * Licence LGPL
24531  *
24532  */
24533
24534 /**
24535  * @class Roo.form.ComboBoxArray
24536  * @extends Roo.form.TextField
24537  * A facebook style adder... for lists of email / people / countries  etc...
24538  * pick multiple items from a combo box, and shows each one.
24539  *
24540  *  Fred [x]  Brian [x]  [Pick another |v]
24541  *
24542  *
24543  *  For this to work: it needs various extra information
24544  *    - normal combo problay has
24545  *      name, hiddenName
24546  *    + displayField, valueField
24547  *
24548  *    For our purpose...
24549  *
24550  *
24551  *   If we change from 'extends' to wrapping...
24552  *   
24553  *  
24554  *
24555  
24556  
24557  * @constructor
24558  * Create a new ComboBoxArray.
24559  * @param {Object} config Configuration options
24560  */
24561  
24562
24563 Roo.form.ComboBoxArray = function(config)
24564 {
24565     
24566     Roo.form.ComboBoxArray.superclass.constructor.call(this, config);
24567     
24568     this.items = new Roo.util.MixedCollection(false);
24569     
24570     // construct the child combo...
24571     
24572     
24573     
24574     
24575    
24576     
24577 }
24578
24579  
24580 Roo.extend(Roo.form.ComboBoxArray, Roo.form.TextField,
24581
24582     /**
24583      * @cfg {Roo.form.Combo} combo The combo box that is wrapped
24584      */
24585     
24586     lastData : false,
24587     
24588     // behavies liek a hiddne field
24589     inputType:      'hidden',
24590     /**
24591      * @cfg {Number} width The width of the box that displays the selected element
24592      */ 
24593     width:          300,
24594
24595     
24596     
24597     /**
24598      * @cfg {String} name    The name of the visable items on this form (eg. titles not ids)
24599      */
24600     name : false,
24601     /**
24602      * @cfg {String} hiddenName    The hidden name of the field, often contains an comma seperated list of names
24603      */
24604     hiddenName : false,
24605     
24606     
24607     // private the array of items that are displayed..
24608     items  : false,
24609     // private - the hidden field el.
24610     hiddenEl : false,
24611     // private - the filed el..
24612     el : false,
24613     
24614     //validateValue : function() { return true; }, // all values are ok!
24615     //onAddClick: function() { },
24616     
24617     onRender : function(ct, position) 
24618     {
24619         
24620         // create the standard hidden element
24621         //Roo.form.ComboBoxArray.superclass.onRender.call(this, ct, position);
24622         
24623         
24624         // give fake names to child combo;
24625         this.combo.hiddenName = this.hiddenName ? (this.hiddenName+'-subcombo') : this.hiddenName;
24626         this.combo.name = this.name? (this.name+'-subcombo') : this.name;
24627         
24628         this.combo = Roo.factory(this.combo, Roo.form);
24629         this.combo.onRender(ct, position);
24630         if (typeof(this.combo.width) != 'undefined') {
24631             this.combo.onResize(this.combo.width,0);
24632         }
24633         
24634         this.combo.initEvents();
24635         
24636         // assigned so form know we need to do this..
24637         this.store          = this.combo.store;
24638         this.valueField     = this.combo.valueField;
24639         this.displayField   = this.combo.displayField ;
24640         
24641         
24642         this.combo.wrap.addClass('x-cbarray-grp');
24643         
24644         var cbwrap = this.combo.wrap.createChild(
24645             {tag: 'div', cls: 'x-cbarray-cb'},
24646             this.combo.el.dom
24647         );
24648         
24649              
24650         this.hiddenEl = this.combo.wrap.createChild({
24651             tag: 'input',  type:'hidden' , name: this.hiddenName, value : ''
24652         });
24653         this.el = this.combo.wrap.createChild({
24654             tag: 'input',  type:'hidden' , name: this.name, value : ''
24655         });
24656          //   this.el.dom.removeAttribute("name");
24657         
24658         
24659         this.outerWrap = this.combo.wrap;
24660         this.wrap = cbwrap;
24661         
24662         this.outerWrap.setWidth(this.width);
24663         this.outerWrap.dom.removeChild(this.el.dom);
24664         
24665         this.wrap.dom.appendChild(this.el.dom);
24666         this.outerWrap.dom.removeChild(this.combo.trigger.dom);
24667         this.combo.wrap.dom.appendChild(this.combo.trigger.dom);
24668         
24669         this.combo.trigger.setStyle('position','relative');
24670         this.combo.trigger.setStyle('left', '0px');
24671         this.combo.trigger.setStyle('top', '2px');
24672         
24673         this.combo.el.setStyle('vertical-align', 'text-bottom');
24674         
24675         //this.trigger.setStyle('vertical-align', 'top');
24676         
24677         // this should use the code from combo really... on('add' ....)
24678         if (this.adder) {
24679             
24680         
24681             this.adder = this.outerWrap.createChild(
24682                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-adder', style: 'margin-left:2px'});  
24683             var _t = this;
24684             this.adder.on('click', function(e) {
24685                 _t.fireEvent('adderclick', this, e);
24686             }, _t);
24687         }
24688         //var _t = this;
24689         //this.adder.on('click', this.onAddClick, _t);
24690         
24691         
24692         this.combo.on('select', function(cb, rec, ix) {
24693             this.addItem(rec.data);
24694             
24695             cb.setValue('');
24696             cb.el.dom.value = '';
24697             //cb.lastData = rec.data;
24698             // add to list
24699             
24700         }, this);
24701         
24702         
24703     },
24704     
24705     
24706     getName: function()
24707     {
24708         // returns hidden if it's set..
24709         if (!this.rendered) {return ''};
24710         return  this.hiddenName ? this.hiddenName : this.name;
24711         
24712     },
24713     
24714     
24715     onResize: function(w, h){
24716         
24717         return;
24718         // not sure if this is needed..
24719         //this.combo.onResize(w,h);
24720         
24721         if(typeof w != 'number'){
24722             // we do not handle it!?!?
24723             return;
24724         }
24725         var tw = this.combo.trigger.getWidth();
24726         tw += this.addicon ? this.addicon.getWidth() : 0;
24727         tw += this.editicon ? this.editicon.getWidth() : 0;
24728         var x = w - tw;
24729         this.combo.el.setWidth( this.combo.adjustWidth('input', x));
24730             
24731         this.combo.trigger.setStyle('left', '0px');
24732         
24733         if(this.list && this.listWidth === undefined){
24734             var lw = Math.max(x + this.combo.trigger.getWidth(), this.combo.minListWidth);
24735             this.list.setWidth(lw);
24736             this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
24737         }
24738         
24739     
24740         
24741     },
24742     
24743     addItem: function(rec)
24744     {
24745         var valueField = this.combo.valueField;
24746         var displayField = this.combo.displayField;
24747         if (this.items.indexOfKey(rec[valueField]) > -1) {
24748             //console.log("GOT " + rec.data.id);
24749             return;
24750         }
24751         
24752         var x = new Roo.form.ComboBoxArray.Item({
24753             //id : rec[this.idField],
24754             data : rec,
24755             displayField : displayField ,
24756             tipField : displayField ,
24757             cb : this
24758         });
24759         // use the 
24760         this.items.add(rec[valueField],x);
24761         // add it before the element..
24762         this.updateHiddenEl();
24763         x.render(this.outerWrap, this.wrap.dom);
24764         // add the image handler..
24765     },
24766     
24767     updateHiddenEl : function()
24768     {
24769         this.validate();
24770         if (!this.hiddenEl) {
24771             return;
24772         }
24773         var ar = [];
24774         var idField = this.combo.valueField;
24775         
24776         this.items.each(function(f) {
24777             ar.push(f.data[idField]);
24778            
24779         });
24780         this.hiddenEl.dom.value = ar.join(',');
24781         this.validate();
24782     },
24783     
24784     reset : function()
24785     {
24786         //Roo.form.ComboBoxArray.superclass.reset.call(this); 
24787         this.items.each(function(f) {
24788            f.remove(); 
24789         });
24790         this.el.dom.value = '';
24791         if (this.hiddenEl) {
24792             this.hiddenEl.dom.value = '';
24793         }
24794         
24795     },
24796     getValue: function()
24797     {
24798         return this.hiddenEl ? this.hiddenEl.dom.value : '';
24799     },
24800     setValue: function(v) // not a valid action - must use addItems..
24801     {
24802          
24803         this.reset();
24804         
24805         
24806         
24807         if (this.store.isLocal && (typeof(v) == 'string')) {
24808             // then we can use the store to find the values..
24809             // comma seperated at present.. this needs to allow JSON based encoding..
24810             this.hiddenEl.value  = v;
24811             var v_ar = [];
24812             Roo.each(v.split(','), function(k) {
24813                 Roo.log("CHECK " + this.valueField + ',' + k);
24814                 var li = this.store.query(this.valueField, k);
24815                 if (!li.length) {
24816                     return;
24817                 }
24818                 var add = {};
24819                 add[this.valueField] = k;
24820                 add[this.displayField] = li.item(0).data[this.displayField];
24821                 
24822                 this.addItem(add);
24823             }, this) 
24824              
24825         }
24826         if (typeof(v) == 'object') {
24827             // then let's assume it's an array of objects..
24828             Roo.each(v, function(l) {
24829                 this.addItem(l);
24830             }, this);
24831              
24832         }
24833         
24834         
24835     },
24836     setFromData: function(v)
24837     {
24838         // this recieves an object, if setValues is called.
24839         this.reset();
24840         this.el.dom.value = v[this.displayField];
24841         this.hiddenEl.dom.value = v[this.valueField];
24842         if (typeof(v[this.valueField]) != 'string' || !v[this.valueField].length) {
24843             return;
24844         }
24845         var kv = v[this.valueField];
24846         var dv = v[this.displayField];
24847         kv = typeof(kv) != 'string' ? '' : kv;
24848         dv = typeof(dv) != 'string' ? '' : dv;
24849         
24850         
24851         var keys = kv.split(',');
24852         var display = dv.split(',');
24853         for (var i = 0 ; i < keys.length; i++) {
24854             
24855             add = {};
24856             add[this.valueField] = keys[i];
24857             add[this.displayField] = display[i];
24858             this.addItem(add);
24859         }
24860       
24861         
24862     },
24863     
24864     
24865     validateValue : function(value){
24866         return Roo.form.ComboBoxArray.superclass.validateValue.call(this, this.getValue());
24867         
24868     }
24869     
24870 });
24871
24872
24873
24874 /**
24875  * @class Roo.form.ComboBoxArray.Item
24876  * @extends Roo.BoxComponent
24877  * A selected item in the list
24878  *  Fred [x]  Brian [x]  [Pick another |v]
24879  * 
24880  * @constructor
24881  * Create a new item.
24882  * @param {Object} config Configuration options
24883  */
24884  
24885 Roo.form.ComboBoxArray.Item = function(config) {
24886     config.id = Roo.id();
24887     Roo.form.ComboBoxArray.Item.superclass.constructor.call(this, config);
24888 }
24889
24890 Roo.extend(Roo.form.ComboBoxArray.Item, Roo.BoxComponent, {
24891     data : {},
24892     cb: false,
24893     displayField : false,
24894     tipField : false,
24895     
24896     
24897     defaultAutoCreate : {
24898         tag: 'div',
24899         cls: 'x-cbarray-item',
24900         cn : [ 
24901             { tag: 'div' },
24902             {
24903                 tag: 'img',
24904                 width:16,
24905                 height : 16,
24906                 src : Roo.BLANK_IMAGE_URL ,
24907                 align: 'center'
24908             }
24909         ]
24910         
24911     },
24912     
24913  
24914     onRender : function(ct, position)
24915     {
24916         Roo.form.Field.superclass.onRender.call(this, ct, position);
24917         
24918         if(!this.el){
24919             var cfg = this.getAutoCreate();
24920             this.el = ct.createChild(cfg, position);
24921         }
24922         
24923         this.el.child('img').dom.setAttribute('src', Roo.BLANK_IMAGE_URL);
24924         
24925         this.el.child('div').dom.innerHTML = this.cb.renderer ? 
24926             this.cb.renderer(this.data) :
24927             String.format('{0}',this.data[this.displayField]);
24928         
24929             
24930         this.el.child('div').dom.setAttribute('qtip',
24931                         String.format('{0}',this.data[this.tipField])
24932         );
24933         
24934         this.el.child('img').on('click', this.remove, this);
24935         
24936     },
24937    
24938     remove : function()
24939     {
24940         
24941         this.cb.items.remove(this);
24942         this.el.child('img').un('click', this.remove, this);
24943         this.el.remove();
24944         this.cb.updateHiddenEl();
24945     }
24946     
24947     
24948 });/*
24949  * Based on:
24950  * Ext JS Library 1.1.1
24951  * Copyright(c) 2006-2007, Ext JS, LLC.
24952  *
24953  * Originally Released Under LGPL - original licence link has changed is not relivant.
24954  *
24955  * Fork - LGPL
24956  * <script type="text/javascript">
24957  */
24958 /**
24959  * @class Roo.form.Checkbox
24960  * @extends Roo.form.Field
24961  * Single checkbox field.  Can be used as a direct replacement for traditional checkbox fields.
24962  * @constructor
24963  * Creates a new Checkbox
24964  * @param {Object} config Configuration options
24965  */
24966 Roo.form.Checkbox = function(config){
24967     Roo.form.Checkbox.superclass.constructor.call(this, config);
24968     this.addEvents({
24969         /**
24970          * @event check
24971          * Fires when the checkbox is checked or unchecked.
24972              * @param {Roo.form.Checkbox} this This checkbox
24973              * @param {Boolean} checked The new checked value
24974              */
24975         check : true
24976     });
24977 };
24978
24979 Roo.extend(Roo.form.Checkbox, Roo.form.Field,  {
24980     /**
24981      * @cfg {String} focusClass The CSS class to use when the checkbox receives focus (defaults to undefined)
24982      */
24983     focusClass : undefined,
24984     /**
24985      * @cfg {String} fieldClass The default CSS class for the checkbox (defaults to "x-form-field")
24986      */
24987     fieldClass: "x-form-field",
24988     /**
24989      * @cfg {Boolean} checked True if the the checkbox should render already checked (defaults to false)
24990      */
24991     checked: false,
24992     /**
24993      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
24994      * {tag: "input", type: "checkbox", autocomplete: "off"})
24995      */
24996     defaultAutoCreate : { tag: "input", type: 'hidden', autocomplete: "off"},
24997     /**
24998      * @cfg {String} boxLabel The text that appears beside the checkbox
24999      */
25000     boxLabel : "",
25001     /**
25002      * @cfg {String} inputValue The value that should go into the generated input element's value attribute
25003      */  
25004     inputValue : '1',
25005     /**
25006      * @cfg {String} valueOff The value that should go into the generated input element's value when unchecked.
25007      */
25008      valueOff: '0', // value when not checked..
25009
25010     actionMode : 'viewEl', 
25011     //
25012     // private
25013     itemCls : 'x-menu-check-item x-form-item',
25014     groupClass : 'x-menu-group-item',
25015     inputType : 'hidden',
25016     
25017     
25018     inSetChecked: false, // check that we are not calling self...
25019     
25020     inputElement: false, // real input element?
25021     basedOn: false, // ????
25022     
25023     isFormField: true, // not sure where this is needed!!!!
25024
25025     onResize : function(){
25026         Roo.form.Checkbox.superclass.onResize.apply(this, arguments);
25027         if(!this.boxLabel){
25028             this.el.alignTo(this.wrap, 'c-c');
25029         }
25030     },
25031
25032     initEvents : function(){
25033         Roo.form.Checkbox.superclass.initEvents.call(this);
25034         this.el.on("click", this.onClick,  this);
25035         this.el.on("change", this.onClick,  this);
25036     },
25037
25038
25039     getResizeEl : function(){
25040         return this.wrap;
25041     },
25042
25043     getPositionEl : function(){
25044         return this.wrap;
25045     },
25046
25047     // private
25048     onRender : function(ct, position){
25049         Roo.form.Checkbox.superclass.onRender.call(this, ct, position);
25050         /*
25051         if(this.inputValue !== undefined){
25052             this.el.dom.value = this.inputValue;
25053         }
25054         */
25055         //this.wrap = this.el.wrap({cls: "x-form-check-wrap"});
25056         this.wrap = this.el.wrap({cls: 'x-menu-check-item '});
25057         var viewEl = this.wrap.createChild({ 
25058             tag: 'img', cls: 'x-menu-item-icon', style: 'margin: 0px;' ,src : Roo.BLANK_IMAGE_URL });
25059         this.viewEl = viewEl;   
25060         this.wrap.on('click', this.onClick,  this); 
25061         
25062         this.el.on('DOMAttrModified', this.setFromHidden,  this); //ff
25063         this.el.on('propertychange', this.setFromHidden,  this);  //ie
25064         
25065         
25066         
25067         if(this.boxLabel){
25068             this.wrap.createChild({tag: 'label', htmlFor: this.el.id, cls: 'x-form-cb-label', html: this.boxLabel});
25069         //    viewEl.on('click', this.onClick,  this); 
25070         }
25071         //if(this.checked){
25072             this.setChecked(this.checked);
25073         //}else{
25074             //this.checked = this.el.dom;
25075         //}
25076
25077     },
25078
25079     // private
25080     initValue : Roo.emptyFn,
25081
25082     /**
25083      * Returns the checked state of the checkbox.
25084      * @return {Boolean} True if checked, else false
25085      */
25086     getValue : function(){
25087         if(this.el){
25088             return String(this.el.dom.value) == String(this.inputValue ) ? this.inputValue : this.valueOff;
25089         }
25090         return this.valueOff;
25091         
25092     },
25093
25094         // private
25095     onClick : function(){ 
25096         this.setChecked(!this.checked);
25097
25098         //if(this.el.dom.checked != this.checked){
25099         //    this.setValue(this.el.dom.checked);
25100        // }
25101     },
25102
25103     /**
25104      * Sets the checked state of the checkbox.
25105      * On is always based on a string comparison between inputValue and the param.
25106      * @param {Boolean/String} value - the value to set 
25107      * @param {Boolean/String} suppressEvent - whether to suppress the checkchange event.
25108      */
25109     setValue : function(v,suppressEvent){
25110         
25111         
25112         //this.checked = (v === true || v === 'true' || v == '1' || String(v).toLowerCase() == 'on');
25113         //if(this.el && this.el.dom){
25114         //    this.el.dom.checked = this.checked;
25115         //    this.el.dom.defaultChecked = this.checked;
25116         //}
25117         this.setChecked(String(v) === String(this.inputValue), suppressEvent);
25118         //this.fireEvent("check", this, this.checked);
25119     },
25120     // private..
25121     setChecked : function(state,suppressEvent)
25122     {
25123         if (this.inSetChecked) {
25124             this.checked = state;
25125             return;
25126         }
25127         
25128     
25129         if(this.wrap){
25130             this.wrap[state ? 'addClass' : 'removeClass']('x-menu-item-checked');
25131         }
25132         this.checked = state;
25133         if(suppressEvent !== true){
25134             this.fireEvent('check', this, state);
25135         }
25136         this.inSetChecked = true;
25137         this.el.dom.value = state ? this.inputValue : this.valueOff;
25138         this.inSetChecked = false;
25139         
25140     },
25141     // handle setting of hidden value by some other method!!?!?
25142     setFromHidden: function()
25143     {
25144         if(!this.el){
25145             return;
25146         }
25147         //console.log("SET FROM HIDDEN");
25148         //alert('setFrom hidden');
25149         this.setValue(this.el.dom.value);
25150     },
25151     
25152     onDestroy : function()
25153     {
25154         if(this.viewEl){
25155             Roo.get(this.viewEl).remove();
25156         }
25157          
25158         Roo.form.Checkbox.superclass.onDestroy.call(this);
25159     }
25160
25161 });/*
25162  * Based on:
25163  * Ext JS Library 1.1.1
25164  * Copyright(c) 2006-2007, Ext JS, LLC.
25165  *
25166  * Originally Released Under LGPL - original licence link has changed is not relivant.
25167  *
25168  * Fork - LGPL
25169  * <script type="text/javascript">
25170  */
25171  
25172 /**
25173  * @class Roo.form.Radio
25174  * @extends Roo.form.Checkbox
25175  * Single radio field.  Same as Checkbox, but provided as a convenience for automatically setting the input type.
25176  * Radio grouping is handled automatically by the browser if you give each radio in a group the same name.
25177  * @constructor
25178  * Creates a new Radio
25179  * @param {Object} config Configuration options
25180  */
25181 Roo.form.Radio = function(){
25182     Roo.form.Radio.superclass.constructor.apply(this, arguments);
25183 };
25184 Roo.extend(Roo.form.Radio, Roo.form.Checkbox, {
25185     inputType: 'radio',
25186
25187     /**
25188      * If this radio is part of a group, it will return the selected value
25189      * @return {String}
25190      */
25191     getGroupValue : function(){
25192         return this.el.up('form').child('input[name='+this.el.dom.name+']:checked', true).value;
25193     },
25194     
25195     
25196     onRender : function(ct, position){
25197         Roo.form.Checkbox.superclass.onRender.call(this, ct, position);
25198         
25199         if(this.inputValue !== undefined){
25200             this.el.dom.value = this.inputValue;
25201         }
25202          
25203         this.wrap = this.el.wrap({cls: "x-form-check-wrap"});
25204         //this.wrap = this.el.wrap({cls: 'x-menu-check-item '});
25205         //var viewEl = this.wrap.createChild({ 
25206         //    tag: 'img', cls: 'x-menu-item-icon', style: 'margin: 0px;' ,src : Roo.BLANK_IMAGE_URL });
25207         //this.viewEl = viewEl;   
25208         //this.wrap.on('click', this.onClick,  this); 
25209         
25210         //this.el.on('DOMAttrModified', this.setFromHidden,  this); //ff
25211         //this.el.on('propertychange', this.setFromHidden,  this);  //ie
25212         
25213         
25214         
25215         if(this.boxLabel){
25216             this.wrap.createChild({tag: 'label', htmlFor: this.el.id, cls: 'x-form-cb-label', html: this.boxLabel});
25217         //    viewEl.on('click', this.onClick,  this); 
25218         }
25219          if(this.checked){
25220             this.el.dom.checked =   'checked' ;
25221         }
25222          
25223     } 
25224     
25225     
25226 });//<script type="text/javascript">
25227
25228 /*
25229  * Ext JS Library 1.1.1
25230  * Copyright(c) 2006-2007, Ext JS, LLC.
25231  * licensing@extjs.com
25232  * 
25233  * http://www.extjs.com/license
25234  */
25235  
25236  /*
25237   * 
25238   * Known bugs:
25239   * Default CSS appears to render it as fixed text by default (should really be Sans-Serif)
25240   * - IE ? - no idea how much works there.
25241   * 
25242   * 
25243   * 
25244   */
25245  
25246
25247 /**
25248  * @class Ext.form.HtmlEditor
25249  * @extends Ext.form.Field
25250  * Provides a lightweight HTML Editor component.
25251  *
25252  * This has been tested on Fireforx / Chrome.. IE may not be so great..
25253  * 
25254  * <br><br><b>Note: The focus/blur and validation marking functionality inherited from Ext.form.Field is NOT
25255  * supported by this editor.</b><br/><br/>
25256  * An Editor is a sensitive component that can't be used in all spots standard fields can be used. Putting an Editor within
25257  * any element that has display set to 'none' can cause problems in Safari and Firefox.<br/><br/>
25258  */
25259 Roo.form.HtmlEditor = Roo.extend(Roo.form.Field, {
25260       /**
25261      * @cfg {Array} toolbars Array of toolbars. - defaults to just the Standard one
25262      */
25263     toolbars : false,
25264     /**
25265      * @cfg {String} createLinkText The default text for the create link prompt
25266      */
25267     createLinkText : 'Please enter the URL for the link:',
25268     /**
25269      * @cfg {String} defaultLinkValue The default value for the create link prompt (defaults to http:/ /)
25270      */
25271     defaultLinkValue : 'http:/'+'/',
25272    
25273      /**
25274      * @cfg {String} resizable  's' or 'se' or 'e' - wrapps the element in a
25275      *                        Roo.resizable.
25276      */
25277     resizable : false,
25278      /**
25279      * @cfg {Number} height (in pixels)
25280      */   
25281     height: 300,
25282    /**
25283      * @cfg {Number} width (in pixels)
25284      */   
25285     width: 500,
25286     
25287     /**
25288      * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
25289      * 
25290      */
25291     stylesheets: false,
25292     
25293     // id of frame..
25294     frameId: false,
25295     
25296     // private properties
25297     validationEvent : false,
25298     deferHeight: true,
25299     initialized : false,
25300     activated : false,
25301     sourceEditMode : false,
25302     onFocus : Roo.emptyFn,
25303     iframePad:3,
25304     hideMode:'offsets',
25305     
25306     defaultAutoCreate : { // modified by initCompnoent..
25307         tag: "textarea",
25308         style:"width:500px;height:300px;",
25309         autocomplete: "off"
25310     },
25311
25312     // private
25313     initComponent : function(){
25314         this.addEvents({
25315             /**
25316              * @event initialize
25317              * Fires when the editor is fully initialized (including the iframe)
25318              * @param {HtmlEditor} this
25319              */
25320             initialize: true,
25321             /**
25322              * @event activate
25323              * Fires when the editor is first receives the focus. Any insertion must wait
25324              * until after this event.
25325              * @param {HtmlEditor} this
25326              */
25327             activate: true,
25328              /**
25329              * @event beforesync
25330              * Fires before the textarea is updated with content from the editor iframe. Return false
25331              * to cancel the sync.
25332              * @param {HtmlEditor} this
25333              * @param {String} html
25334              */
25335             beforesync: true,
25336              /**
25337              * @event beforepush
25338              * Fires before the iframe editor is updated with content from the textarea. Return false
25339              * to cancel the push.
25340              * @param {HtmlEditor} this
25341              * @param {String} html
25342              */
25343             beforepush: true,
25344              /**
25345              * @event sync
25346              * Fires when the textarea is updated with content from the editor iframe.
25347              * @param {HtmlEditor} this
25348              * @param {String} html
25349              */
25350             sync: true,
25351              /**
25352              * @event push
25353              * Fires when the iframe editor is updated with content from the textarea.
25354              * @param {HtmlEditor} this
25355              * @param {String} html
25356              */
25357             push: true,
25358              /**
25359              * @event editmodechange
25360              * Fires when the editor switches edit modes
25361              * @param {HtmlEditor} this
25362              * @param {Boolean} sourceEdit True if source edit, false if standard editing.
25363              */
25364             editmodechange: true,
25365             /**
25366              * @event editorevent
25367              * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
25368              * @param {HtmlEditor} this
25369              */
25370             editorevent: true
25371         });
25372         this.defaultAutoCreate =  {
25373             tag: "textarea",
25374             style:'width: ' + this.width + 'px;height: ' + this.height + 'px;',
25375             autocomplete: "off"
25376         };
25377     },
25378
25379     /**
25380      * Protected method that will not generally be called directly. It
25381      * is called when the editor creates its toolbar. Override this method if you need to
25382      * add custom toolbar buttons.
25383      * @param {HtmlEditor} editor
25384      */
25385     createToolbar : function(editor){
25386         if (!editor.toolbars || !editor.toolbars.length) {
25387             editor.toolbars = [ new Roo.form.HtmlEditor.ToolbarStandard() ]; // can be empty?
25388         }
25389         
25390         for (var i =0 ; i < editor.toolbars.length;i++) {
25391             editor.toolbars[i] = Roo.factory(
25392                     typeof(editor.toolbars[i]) == 'string' ?
25393                         { xtype: editor.toolbars[i]} : editor.toolbars[i],
25394                 Roo.form.HtmlEditor);
25395             editor.toolbars[i].init(editor);
25396         }
25397          
25398         
25399     },
25400
25401     /**
25402      * Protected method that will not generally be called directly. It
25403      * is called when the editor initializes the iframe with HTML contents. Override this method if you
25404      * want to change the initialization markup of the iframe (e.g. to add stylesheets).
25405      */
25406     getDocMarkup : function(){
25407         // body styles..
25408         var st = '';
25409         if (this.stylesheets === false) {
25410             
25411             Roo.get(document.head).select('style').each(function(node) {
25412                 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
25413             });
25414             
25415             Roo.get(document.head).select('link').each(function(node) { 
25416                 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
25417             });
25418             
25419         } else if (!this.stylesheets.length) {
25420                 // simple..
25421                 st = '<style type="text/css">' +
25422                     'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
25423                    '</style>';
25424         } else {
25425             Roo.each(this.stylesheets, function(s) {
25426                 st += '<link rel="stylesheet" type="text/css" href="' + s +'" />'
25427             });
25428             
25429         }
25430         
25431         st +=  '<style type="text/css">' +
25432             'IMG { cursor: pointer } ' +
25433         '</style>';
25434
25435         
25436         return '<html><head>' + st  +
25437             //<style type="text/css">' +
25438             //'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
25439             //'</style>' +
25440             ' </head><body class="roo-htmleditor-body"></body></html>';
25441     },
25442
25443     // private
25444     onRender : function(ct, position)
25445     {
25446         var _t = this;
25447         Roo.form.HtmlEditor.superclass.onRender.call(this, ct, position);
25448         this.el.dom.style.border = '0 none';
25449         this.el.dom.setAttribute('tabIndex', -1);
25450         this.el.addClass('x-hidden');
25451         if(Roo.isIE){ // fix IE 1px bogus margin
25452             this.el.applyStyles('margin-top:-1px;margin-bottom:-1px;')
25453         }
25454         this.wrap = this.el.wrap({
25455             cls:'x-html-editor-wrap', cn:{cls:'x-html-editor-tb'}
25456         });
25457         
25458         if (this.resizable) {
25459             this.resizeEl = new Roo.Resizable(this.wrap, {
25460                 pinned : true,
25461                 wrap: true,
25462                 dynamic : true,
25463                 minHeight : this.height,
25464                 height: this.height,
25465                 handles : this.resizable,
25466                 width: this.width,
25467                 listeners : {
25468                     resize : function(r, w, h) {
25469                         _t.onResize(w,h); // -something
25470                     }
25471                 }
25472             });
25473             
25474         }
25475
25476         this.frameId = Roo.id();
25477         
25478         this.createToolbar(this);
25479         
25480       
25481         
25482         var iframe = this.wrap.createChild({
25483             tag: 'iframe',
25484             id: this.frameId,
25485             name: this.frameId,
25486             frameBorder : 'no',
25487             'src' : Roo.SSL_SECURE_URL ? Roo.SSL_SECURE_URL  :  "javascript:false"
25488         }, this.el
25489         );
25490         
25491        // console.log(iframe);
25492         //this.wrap.dom.appendChild(iframe);
25493
25494         this.iframe = iframe.dom;
25495
25496          this.assignDocWin();
25497         
25498         this.doc.designMode = 'on';
25499        
25500         this.doc.open();
25501         this.doc.write(this.getDocMarkup());
25502         this.doc.close();
25503
25504         
25505         var task = { // must defer to wait for browser to be ready
25506             run : function(){
25507                 //console.log("run task?" + this.doc.readyState);
25508                 this.assignDocWin();
25509                 if(this.doc.body || this.doc.readyState == 'complete'){
25510                     try {
25511                         this.doc.designMode="on";
25512                     } catch (e) {
25513                         return;
25514                     }
25515                     Roo.TaskMgr.stop(task);
25516                     this.initEditor.defer(10, this);
25517                 }
25518             },
25519             interval : 10,
25520             duration:10000,
25521             scope: this
25522         };
25523         Roo.TaskMgr.start(task);
25524
25525         if(!this.width){
25526             this.setSize(this.wrap.getSize());
25527         }
25528         if (this.resizeEl) {
25529             this.resizeEl.resizeTo.defer(100, this.resizeEl,[ this.width,this.height ] );
25530             // should trigger onReize..
25531         }
25532     },
25533
25534     // private
25535     onResize : function(w, h)
25536     {
25537         //Roo.log('resize: ' +w + ',' + h );
25538         Roo.form.HtmlEditor.superclass.onResize.apply(this, arguments);
25539         if(this.el && this.iframe){
25540             if(typeof w == 'number'){
25541                 var aw = w - this.wrap.getFrameWidth('lr');
25542                 this.el.setWidth(this.adjustWidth('textarea', aw));
25543                 this.iframe.style.width = aw + 'px';
25544             }
25545             if(typeof h == 'number'){
25546                 var tbh = 0;
25547                 for (var i =0; i < this.toolbars.length;i++) {
25548                     // fixme - ask toolbars for heights?
25549                     tbh += this.toolbars[i].tb.el.getHeight();
25550                     if (this.toolbars[i].footer) {
25551                         tbh += this.toolbars[i].footer.el.getHeight();
25552                     }
25553                 }
25554                 
25555                 
25556                 
25557                 
25558                 var ah = h - this.wrap.getFrameWidth('tb') - tbh;// this.tb.el.getHeight();
25559                 ah -= 5; // knock a few pixes off for look..
25560                 this.el.setHeight(this.adjustWidth('textarea', ah));
25561                 this.iframe.style.height = ah + 'px';
25562                 if(this.doc){
25563                     (this.doc.body || this.doc.documentElement).style.height = (ah - (this.iframePad*2)) + 'px';
25564                 }
25565             }
25566         }
25567     },
25568
25569     /**
25570      * Toggles the editor between standard and source edit mode.
25571      * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
25572      */
25573     toggleSourceEdit : function(sourceEditMode){
25574         
25575         this.sourceEditMode = sourceEditMode === true;
25576         
25577         if(this.sourceEditMode){
25578 //            Roo.log('in');
25579 //            Roo.log(this.syncValue());
25580             this.syncValue();
25581             this.iframe.className = 'x-hidden';
25582             this.el.removeClass('x-hidden');
25583             this.el.dom.removeAttribute('tabIndex');
25584             this.el.focus();
25585         }else{
25586 //            Roo.log('out')
25587 //            Roo.log(this.pushValue()); 
25588             this.pushValue();
25589             this.iframe.className = '';
25590             this.el.addClass('x-hidden');
25591             this.el.dom.setAttribute('tabIndex', -1);
25592             this.deferFocus();
25593         }
25594         this.setSize(this.wrap.getSize());
25595         this.fireEvent('editmodechange', this, this.sourceEditMode);
25596     },
25597
25598     // private used internally
25599     createLink : function(){
25600         var url = prompt(this.createLinkText, this.defaultLinkValue);
25601         if(url && url != 'http:/'+'/'){
25602             this.relayCmd('createlink', url);
25603         }
25604     },
25605
25606     // private (for BoxComponent)
25607     adjustSize : Roo.BoxComponent.prototype.adjustSize,
25608
25609     // private (for BoxComponent)
25610     getResizeEl : function(){
25611         return this.wrap;
25612     },
25613
25614     // private (for BoxComponent)
25615     getPositionEl : function(){
25616         return this.wrap;
25617     },
25618
25619     // private
25620     initEvents : function(){
25621         this.originalValue = this.getValue();
25622     },
25623
25624     /**
25625      * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
25626      * @method
25627      */
25628     markInvalid : Roo.emptyFn,
25629     /**
25630      * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
25631      * @method
25632      */
25633     clearInvalid : Roo.emptyFn,
25634
25635     setValue : function(v){
25636         Roo.form.HtmlEditor.superclass.setValue.call(this, v);
25637         this.pushValue();
25638     },
25639
25640     /**
25641      * Protected method that will not generally be called directly. If you need/want
25642      * custom HTML cleanup, this is the method you should override.
25643      * @param {String} html The HTML to be cleaned
25644      * return {String} The cleaned HTML
25645      */
25646     cleanHtml : function(html){
25647         html = String(html);
25648         if(html.length > 5){
25649             if(Roo.isSafari){ // strip safari nonsense
25650                 html = html.replace(/\sclass="(?:Apple-style-span|khtml-block-placeholder)"/gi, '');
25651             }
25652         }
25653         if(html == '&nbsp;'){
25654             html = '';
25655         }
25656         return html;
25657     },
25658
25659     /**
25660      * Protected method that will not generally be called directly. Syncs the contents
25661      * of the editor iframe with the textarea.
25662      */
25663     syncValue : function(){
25664         if(this.initialized){
25665             var bd = (this.doc.body || this.doc.documentElement);
25666             //this.cleanUpPaste(); -- this is done else where and causes havoc..
25667             var html = bd.innerHTML;
25668             if(Roo.isSafari){
25669                 var bs = bd.getAttribute('style'); // Safari puts text-align styles on the body element!
25670                 var m = bs.match(/text-align:(.*?);/i);
25671                 if(m && m[1]){
25672                     html = '<div style="'+m[0]+'">' + html + '</div>';
25673                 }
25674             }
25675             html = this.cleanHtml(html);
25676             // fix up the special chars.. normaly like back quotes in word...
25677             // however we do not want to do this with chinese..
25678             html = html.replace(/([\x80-\uffff])/g, function (a, b) {
25679                 var cc = b.charCodeAt();
25680                 if (
25681                     (cc >= 0x4E00 && cc < 0xA000 ) ||
25682                     (cc >= 0x3400 && cc < 0x4E00 ) ||
25683                     (cc >= 0xf900 && cc < 0xfb00 )
25684                 ) {
25685                         return b;
25686                 }
25687                 return "&#"+cc+";" 
25688             });
25689             if(this.fireEvent('beforesync', this, html) !== false){
25690                 this.el.dom.value = html;
25691                 this.fireEvent('sync', this, html);
25692             }
25693         }
25694     },
25695
25696     /**
25697      * Protected method that will not generally be called directly. Pushes the value of the textarea
25698      * into the iframe editor.
25699      */
25700     pushValue : function(){
25701         if(this.initialized){
25702             var v = this.el.dom.value;
25703             
25704             if(v.length < 1){
25705                 v = '&#160;';
25706             }
25707             
25708             if(this.fireEvent('beforepush', this, v) !== false){
25709                 var d = (this.doc.body || this.doc.documentElement);
25710                 d.innerHTML = v;
25711                 this.cleanUpPaste();
25712                 this.el.dom.value = d.innerHTML;
25713                 this.fireEvent('push', this, v);
25714             }
25715         }
25716     },
25717
25718     // private
25719     deferFocus : function(){
25720         this.focus.defer(10, this);
25721     },
25722
25723     // doc'ed in Field
25724     focus : function(){
25725         if(this.win && !this.sourceEditMode){
25726             this.win.focus();
25727         }else{
25728             this.el.focus();
25729         }
25730     },
25731     
25732     assignDocWin: function()
25733     {
25734         var iframe = this.iframe;
25735         
25736          if(Roo.isIE){
25737             this.doc = iframe.contentWindow.document;
25738             this.win = iframe.contentWindow;
25739         } else {
25740             if (!Roo.get(this.frameId)) {
25741                 return;
25742             }
25743             this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
25744             this.win = Roo.get(this.frameId).dom.contentWindow;
25745         }
25746     },
25747     
25748     // private
25749     initEditor : function(){
25750         //console.log("INIT EDITOR");
25751         this.assignDocWin();
25752         
25753         
25754         
25755         this.doc.designMode="on";
25756         this.doc.open();
25757         this.doc.write(this.getDocMarkup());
25758         this.doc.close();
25759         
25760         var dbody = (this.doc.body || this.doc.documentElement);
25761         //var ss = this.el.getStyles('font-size', 'font-family', 'background-image', 'background-repeat');
25762         // this copies styles from the containing element into thsi one..
25763         // not sure why we need all of this..
25764         var ss = this.el.getStyles('font-size', 'background-image', 'background-repeat');
25765         ss['background-attachment'] = 'fixed'; // w3c
25766         dbody.bgProperties = 'fixed'; // ie
25767         Roo.DomHelper.applyStyles(dbody, ss);
25768         Roo.EventManager.on(this.doc, {
25769             //'mousedown': this.onEditorEvent,
25770             'mouseup': this.onEditorEvent,
25771             'dblclick': this.onEditorEvent,
25772             'click': this.onEditorEvent,
25773             'keyup': this.onEditorEvent,
25774             buffer:100,
25775             scope: this
25776         });
25777         if(Roo.isGecko){
25778             Roo.EventManager.on(this.doc, 'keypress', this.mozKeyPress, this);
25779         }
25780         if(Roo.isIE || Roo.isSafari || Roo.isOpera){
25781             Roo.EventManager.on(this.doc, 'keydown', this.fixKeys, this);
25782         }
25783         this.initialized = true;
25784
25785         this.fireEvent('initialize', this);
25786         this.pushValue();
25787     },
25788
25789     // private
25790     onDestroy : function(){
25791         
25792         
25793         
25794         if(this.rendered){
25795             
25796             for (var i =0; i < this.toolbars.length;i++) {
25797                 // fixme - ask toolbars for heights?
25798                 this.toolbars[i].onDestroy();
25799             }
25800             
25801             this.wrap.dom.innerHTML = '';
25802             this.wrap.remove();
25803         }
25804     },
25805
25806     // private
25807     onFirstFocus : function(){
25808         
25809         this.assignDocWin();
25810         
25811         
25812         this.activated = true;
25813         for (var i =0; i < this.toolbars.length;i++) {
25814             this.toolbars[i].onFirstFocus();
25815         }
25816        
25817         if(Roo.isGecko){ // prevent silly gecko errors
25818             this.win.focus();
25819             var s = this.win.getSelection();
25820             if(!s.focusNode || s.focusNode.nodeType != 3){
25821                 var r = s.getRangeAt(0);
25822                 r.selectNodeContents((this.doc.body || this.doc.documentElement));
25823                 r.collapse(true);
25824                 this.deferFocus();
25825             }
25826             try{
25827                 this.execCmd('useCSS', true);
25828                 this.execCmd('styleWithCSS', false);
25829             }catch(e){}
25830         }
25831         this.fireEvent('activate', this);
25832     },
25833
25834     // private
25835     adjustFont: function(btn){
25836         var adjust = btn.cmd == 'increasefontsize' ? 1 : -1;
25837         //if(Roo.isSafari){ // safari
25838         //    adjust *= 2;
25839        // }
25840         var v = parseInt(this.doc.queryCommandValue('FontSize')|| 3, 10);
25841         if(Roo.isSafari){ // safari
25842             var sm = { 10 : 1, 13: 2, 16:3, 18:4, 24: 5, 32:6, 48: 7 };
25843             v =  (v < 10) ? 10 : v;
25844             v =  (v > 48) ? 48 : v;
25845             v = typeof(sm[v]) == 'undefined' ? 1 : sm[v];
25846             
25847         }
25848         
25849         
25850         v = Math.max(1, v+adjust);
25851         
25852         this.execCmd('FontSize', v  );
25853     },
25854
25855     onEditorEvent : function(e){
25856         this.fireEvent('editorevent', this, e);
25857       //  this.updateToolbar();
25858         this.syncValue(); //we can not sync so often.. sync cleans, so this breaks stuff
25859     },
25860
25861     insertTag : function(tg)
25862     {
25863         // could be a bit smarter... -> wrap the current selected tRoo..
25864         if (tg.toLowerCase() == 'span' || tg.toLowerCase() == 'code') {
25865             
25866             range = this.createRange(this.getSelection());
25867             var wrappingNode = this.doc.createElement(tg.toLowerCase());
25868             wrappingNode.appendChild(range.extractContents());
25869             range.insertNode(wrappingNode);
25870
25871             return;
25872             
25873             
25874             
25875         }
25876         this.execCmd("formatblock",   tg);
25877         
25878     },
25879     
25880     insertText : function(txt)
25881     {
25882         
25883         
25884         var range = this.createRange();
25885         range.deleteContents();
25886                //alert(Sender.getAttribute('label'));
25887                
25888         range.insertNode(this.doc.createTextNode(txt));
25889     } ,
25890     
25891     // private
25892     relayBtnCmd : function(btn){
25893         this.relayCmd(btn.cmd);
25894     },
25895
25896     /**
25897      * Executes a Midas editor command on the editor document and performs necessary focus and
25898      * toolbar updates. <b>This should only be called after the editor is initialized.</b>
25899      * @param {String} cmd The Midas command
25900      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
25901      */
25902     relayCmd : function(cmd, value){
25903         this.win.focus();
25904         this.execCmd(cmd, value);
25905         this.fireEvent('editorevent', this);
25906         //this.updateToolbar();
25907         this.deferFocus();
25908     },
25909
25910     /**
25911      * Executes a Midas editor command directly on the editor document.
25912      * For visual commands, you should use {@link #relayCmd} instead.
25913      * <b>This should only be called after the editor is initialized.</b>
25914      * @param {String} cmd The Midas command
25915      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
25916      */
25917     execCmd : function(cmd, value){
25918         this.doc.execCommand(cmd, false, value === undefined ? null : value);
25919         this.syncValue();
25920     },
25921  
25922  
25923    
25924     /**
25925      * Inserts the passed text at the current cursor position. Note: the editor must be initialized and activated
25926      * to insert tRoo.
25927      * @param {String} text | dom node.. 
25928      */
25929     insertAtCursor : function(text)
25930     {
25931         
25932         
25933         
25934         if(!this.activated){
25935             return;
25936         }
25937         /*
25938         if(Roo.isIE){
25939             this.win.focus();
25940             var r = this.doc.selection.createRange();
25941             if(r){
25942                 r.collapse(true);
25943                 r.pasteHTML(text);
25944                 this.syncValue();
25945                 this.deferFocus();
25946             
25947             }
25948             return;
25949         }
25950         */
25951         if(Roo.isGecko || Roo.isOpera || Roo.isSafari){
25952             this.win.focus();
25953             
25954             
25955             // from jquery ui (MIT licenced)
25956             var range, node;
25957             var win = this.win;
25958             
25959             if (win.getSelection && win.getSelection().getRangeAt) {
25960                 range = win.getSelection().getRangeAt(0);
25961                 node = typeof(text) == 'string' ? range.createContextualFragment(text) : text;
25962                 range.insertNode(node);
25963             } else if (win.document.selection && win.document.selection.createRange) {
25964                 // no firefox support
25965                 var txt = typeof(text) == 'string' ? text : text.outerHTML;
25966                 win.document.selection.createRange().pasteHTML(txt);
25967             } else {
25968                 // no firefox support
25969                 var txt = typeof(text) == 'string' ? text : text.outerHTML;
25970                 this.execCmd('InsertHTML', txt);
25971             } 
25972             
25973             this.syncValue();
25974             
25975             this.deferFocus();
25976         }
25977     },
25978  // private
25979     mozKeyPress : function(e){
25980         if(e.ctrlKey){
25981             var c = e.getCharCode(), cmd;
25982           
25983             if(c > 0){
25984                 c = String.fromCharCode(c).toLowerCase();
25985                 switch(c){
25986                     case 'b':
25987                         cmd = 'bold';
25988                         break;
25989                     case 'i':
25990                         cmd = 'italic';
25991                         break;
25992                     
25993                     case 'u':
25994                         cmd = 'underline';
25995                         break;
25996                     
25997                     case 'v':
25998                         this.cleanUpPaste.defer(100, this);
25999                         return;
26000                         
26001                 }
26002                 if(cmd){
26003                     this.win.focus();
26004                     this.execCmd(cmd);
26005                     this.deferFocus();
26006                     e.preventDefault();
26007                 }
26008                 
26009             }
26010         }
26011     },
26012
26013     // private
26014     fixKeys : function(){ // load time branching for fastest keydown performance
26015         if(Roo.isIE){
26016             return function(e){
26017                 var k = e.getKey(), r;
26018                 if(k == e.TAB){
26019                     e.stopEvent();
26020                     r = this.doc.selection.createRange();
26021                     if(r){
26022                         r.collapse(true);
26023                         r.pasteHTML('&#160;&#160;&#160;&#160;');
26024                         this.deferFocus();
26025                     }
26026                     return;
26027                 }
26028                 
26029                 if(k == e.ENTER){
26030                     r = this.doc.selection.createRange();
26031                     if(r){
26032                         var target = r.parentElement();
26033                         if(!target || target.tagName.toLowerCase() != 'li'){
26034                             e.stopEvent();
26035                             r.pasteHTML('<br />');
26036                             r.collapse(false);
26037                             r.select();
26038                         }
26039                     }
26040                 }
26041                 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
26042                     this.cleanUpPaste.defer(100, this);
26043                     return;
26044                 }
26045                 
26046                 
26047             };
26048         }else if(Roo.isOpera){
26049             return function(e){
26050                 var k = e.getKey();
26051                 if(k == e.TAB){
26052                     e.stopEvent();
26053                     this.win.focus();
26054                     this.execCmd('InsertHTML','&#160;&#160;&#160;&#160;');
26055                     this.deferFocus();
26056                 }
26057                 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
26058                     this.cleanUpPaste.defer(100, this);
26059                     return;
26060                 }
26061                 
26062             };
26063         }else if(Roo.isSafari){
26064             return function(e){
26065                 var k = e.getKey();
26066                 
26067                 if(k == e.TAB){
26068                     e.stopEvent();
26069                     this.execCmd('InsertText','\t');
26070                     this.deferFocus();
26071                     return;
26072                 }
26073                if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
26074                     this.cleanUpPaste.defer(100, this);
26075                     return;
26076                 }
26077                 
26078              };
26079         }
26080     }(),
26081     
26082     getAllAncestors: function()
26083     {
26084         var p = this.getSelectedNode();
26085         var a = [];
26086         if (!p) {
26087             a.push(p); // push blank onto stack..
26088             p = this.getParentElement();
26089         }
26090         
26091         
26092         while (p && (p.nodeType == 1) && (p.tagName.toLowerCase() != 'body')) {
26093             a.push(p);
26094             p = p.parentNode;
26095         }
26096         a.push(this.doc.body);
26097         return a;
26098     },
26099     lastSel : false,
26100     lastSelNode : false,
26101     
26102     
26103     getSelection : function() 
26104     {
26105         this.assignDocWin();
26106         return Roo.isIE ? this.doc.selection : this.win.getSelection();
26107     },
26108     
26109     getSelectedNode: function() 
26110     {
26111         // this may only work on Gecko!!!
26112         
26113         // should we cache this!!!!
26114         
26115         
26116         
26117          
26118         var range = this.createRange(this.getSelection()).cloneRange();
26119         
26120         if (Roo.isIE) {
26121             var parent = range.parentElement();
26122             while (true) {
26123                 var testRange = range.duplicate();
26124                 testRange.moveToElementText(parent);
26125                 if (testRange.inRange(range)) {
26126                     break;
26127                 }
26128                 if ((parent.nodeType != 1) || (parent.tagName.toLowerCase() == 'body')) {
26129                     break;
26130                 }
26131                 parent = parent.parentElement;
26132             }
26133             return parent;
26134         }
26135         
26136         // is ancestor a text element.
26137         var ac =  range.commonAncestorContainer;
26138         if (ac.nodeType == 3) {
26139             ac = ac.parentNode;
26140         }
26141         
26142         var ar = ac.childNodes;
26143          
26144         var nodes = [];
26145         var other_nodes = [];
26146         var has_other_nodes = false;
26147         for (var i=0;i<ar.length;i++) {
26148             if ((ar[i].nodeType == 3) && (!ar[i].data.length)) { // empty text ? 
26149                 continue;
26150             }
26151             // fullly contained node.
26152             
26153             if (this.rangeIntersectsNode(range,ar[i]) && this.rangeCompareNode(range,ar[i]) == 3) {
26154                 nodes.push(ar[i]);
26155                 continue;
26156             }
26157             
26158             // probably selected..
26159             if ((ar[i].nodeType == 1) && this.rangeIntersectsNode(range,ar[i]) && (this.rangeCompareNode(range,ar[i]) > 0)) {
26160                 other_nodes.push(ar[i]);
26161                 continue;
26162             }
26163             // outer..
26164             if (!this.rangeIntersectsNode(range,ar[i])|| (this.rangeCompareNode(range,ar[i]) == 0))  {
26165                 continue;
26166             }
26167             
26168             
26169             has_other_nodes = true;
26170         }
26171         if (!nodes.length && other_nodes.length) {
26172             nodes= other_nodes;
26173         }
26174         if (has_other_nodes || !nodes.length || (nodes.length > 1)) {
26175             return false;
26176         }
26177         
26178         return nodes[0];
26179     },
26180     createRange: function(sel)
26181     {
26182         // this has strange effects when using with 
26183         // top toolbar - not sure if it's a great idea.
26184         //this.editor.contentWindow.focus();
26185         if (typeof sel != "undefined") {
26186             try {
26187                 return sel.getRangeAt ? sel.getRangeAt(0) : sel.createRange();
26188             } catch(e) {
26189                 return this.doc.createRange();
26190             }
26191         } else {
26192             return this.doc.createRange();
26193         }
26194     },
26195     getParentElement: function()
26196     {
26197         
26198         this.assignDocWin();
26199         var sel = Roo.isIE ? this.doc.selection : this.win.getSelection();
26200         
26201         var range = this.createRange(sel);
26202          
26203         try {
26204             var p = range.commonAncestorContainer;
26205             while (p.nodeType == 3) { // text node
26206                 p = p.parentNode;
26207             }
26208             return p;
26209         } catch (e) {
26210             return null;
26211         }
26212     
26213     },
26214     /***
26215      *
26216      * Range intersection.. the hard stuff...
26217      *  '-1' = before
26218      *  '0' = hits..
26219      *  '1' = after.
26220      *         [ -- selected range --- ]
26221      *   [fail]                        [fail]
26222      *
26223      *    basically..
26224      *      if end is before start or  hits it. fail.
26225      *      if start is after end or hits it fail.
26226      *
26227      *   if either hits (but other is outside. - then it's not 
26228      *   
26229      *    
26230      **/
26231     
26232     
26233     // @see http://www.thismuchiknow.co.uk/?p=64.
26234     rangeIntersectsNode : function(range, node)
26235     {
26236         var nodeRange = node.ownerDocument.createRange();
26237         try {
26238             nodeRange.selectNode(node);
26239         } catch (e) {
26240             nodeRange.selectNodeContents(node);
26241         }
26242     
26243         var rangeStartRange = range.cloneRange();
26244         rangeStartRange.collapse(true);
26245     
26246         var rangeEndRange = range.cloneRange();
26247         rangeEndRange.collapse(false);
26248     
26249         var nodeStartRange = nodeRange.cloneRange();
26250         nodeStartRange.collapse(true);
26251     
26252         var nodeEndRange = nodeRange.cloneRange();
26253         nodeEndRange.collapse(false);
26254     
26255         return rangeStartRange.compareBoundaryPoints(
26256                  Range.START_TO_START, nodeEndRange) == -1 &&
26257                rangeEndRange.compareBoundaryPoints(
26258                  Range.START_TO_START, nodeStartRange) == 1;
26259         
26260          
26261     },
26262     rangeCompareNode : function(range, node)
26263     {
26264         var nodeRange = node.ownerDocument.createRange();
26265         try {
26266             nodeRange.selectNode(node);
26267         } catch (e) {
26268             nodeRange.selectNodeContents(node);
26269         }
26270         
26271         
26272         range.collapse(true);
26273     
26274         nodeRange.collapse(true);
26275      
26276         var ss = range.compareBoundaryPoints( Range.START_TO_START, nodeRange);
26277         var ee = range.compareBoundaryPoints(  Range.END_TO_END, nodeRange);
26278          
26279         //Roo.log(node.tagName + ': ss='+ss +', ee='+ee)
26280         
26281         var nodeIsBefore   =  ss == 1;
26282         var nodeIsAfter    = ee == -1;
26283         
26284         if (nodeIsBefore && nodeIsAfter)
26285             return 0; // outer
26286         if (!nodeIsBefore && nodeIsAfter)
26287             return 1; //right trailed.
26288         
26289         if (nodeIsBefore && !nodeIsAfter)
26290             return 2;  // left trailed.
26291         // fully contined.
26292         return 3;
26293     },
26294
26295     // private? - in a new class?
26296     cleanUpPaste :  function()
26297     {
26298         // cleans up the whole document..
26299          Roo.log('cleanuppaste');
26300         this.cleanUpChildren(this.doc.body);
26301         var clean = this.cleanWordChars(this.doc.body.innerHTML);
26302         if (clean != this.doc.body.innerHTML) {
26303             this.doc.body.innerHTML = clean;
26304         }
26305         
26306     },
26307     
26308     cleanWordChars : function(input) {// change the chars to hex code
26309         var he = Roo.form.HtmlEditor;
26310         
26311         var output = input;
26312         Roo.each(he.swapCodes, function(sw) { 
26313             var swapper = new RegExp("\\u" + sw[0].toString(16), "g"); // hex codes
26314             
26315             output = output.replace(swapper, sw[1]);
26316         });
26317         
26318         return output;
26319     },
26320     
26321     
26322     cleanUpChildren : function (n)
26323     {
26324         if (!n.childNodes.length) {
26325             return;
26326         }
26327         for (var i = n.childNodes.length-1; i > -1 ; i--) {
26328            this.cleanUpChild(n.childNodes[i]);
26329         }
26330     },
26331     
26332     
26333         
26334     
26335     cleanUpChild : function (node)
26336     {
26337         var ed = this;
26338         //console.log(node);
26339         if (node.nodeName == "#text") {
26340             // clean up silly Windows -- stuff?
26341             return; 
26342         }
26343         if (node.nodeName == "#comment") {
26344             node.parentNode.removeChild(node);
26345             // clean up silly Windows -- stuff?
26346             return; 
26347         }
26348         
26349         if (Roo.form.HtmlEditor.black.indexOf(node.tagName.toLowerCase()) > -1) {
26350             // remove node.
26351             node.parentNode.removeChild(node);
26352             return;
26353             
26354         }
26355         
26356         var remove_keep_children= Roo.form.HtmlEditor.remove.indexOf(node.tagName.toLowerCase()) > -1;
26357         
26358         // remove <a name=....> as rendering on yahoo mailer is borked with this.
26359         // this will have to be flaged elsewhere - perhaps ablack=name... on the mailer..
26360         
26361         //if (node.tagName.toLowerCase() == 'a' && !node.hasAttribute('href')) {
26362         //    remove_keep_children = true;
26363         //}
26364         
26365         if (remove_keep_children) {
26366             this.cleanUpChildren(node);
26367             // inserts everything just before this node...
26368             while (node.childNodes.length) {
26369                 var cn = node.childNodes[0];
26370                 node.removeChild(cn);
26371                 node.parentNode.insertBefore(cn, node);
26372             }
26373             node.parentNode.removeChild(node);
26374             return;
26375         }
26376         
26377         if (!node.attributes || !node.attributes.length) {
26378             this.cleanUpChildren(node);
26379             return;
26380         }
26381         
26382         function cleanAttr(n,v)
26383         {
26384             
26385             if (v.match(/^\./) || v.match(/^\//)) {
26386                 return;
26387             }
26388             if (v.match(/^(http|https):\/\//) || v.match(/^mailto:/)) {
26389                 return;
26390             }
26391             if (v.match(/^#/)) {
26392                 return;
26393             }
26394 //            Roo.log("(REMOVE TAG)"+ node.tagName +'.' + n + '=' + v);
26395             node.removeAttribute(n);
26396             
26397         }
26398         
26399         function cleanStyle(n,v)
26400         {
26401             if (v.match(/expression/)) { //XSS?? should we even bother..
26402                 node.removeAttribute(n);
26403                 return;
26404             }
26405             var cwhite = typeof(ed.cwhite) == 'undefined' ? Roo.form.HtmlEditor.cwhite : ed.cwhite;
26406             var cblack = typeof(ed.cblack) == 'undefined' ? Roo.form.HtmlEditor.cblack : ed.cblack;
26407             
26408             
26409             var parts = v.split(/;/);
26410             var clean = [];
26411             
26412             Roo.each(parts, function(p) {
26413                 p = p.replace(/^\s+/g,'').replace(/\s+$/g,'');
26414                 if (!p.length) {
26415                     return true;
26416                 }
26417                 var l = p.split(':').shift().replace(/\s+/g,'');
26418                 l = l.replace(/^\s+/g,'').replace(/\s+$/g,'');
26419                 
26420                 
26421                 if ( cblack.indexOf(l) > -1) {
26422 //                    Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
26423                     //node.removeAttribute(n);
26424                     return true;
26425                 }
26426                 //Roo.log()
26427                 // only allow 'c whitelisted system attributes'
26428                 if ( cwhite.length &&  cwhite.indexOf(l) < 0) {
26429 //                    Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
26430                     //node.removeAttribute(n);
26431                     return true;
26432                 }
26433                 
26434                 
26435                  
26436                 
26437                 clean.push(p);
26438                 return true;
26439             });
26440             if (clean.length) { 
26441                 node.setAttribute(n, clean.join(';'));
26442             } else {
26443                 node.removeAttribute(n);
26444             }
26445             
26446         }
26447         
26448         
26449         for (var i = node.attributes.length-1; i > -1 ; i--) {
26450             var a = node.attributes[i];
26451             //console.log(a);
26452             
26453             if (a.name.toLowerCase().substr(0,2)=='on')  {
26454                 node.removeAttribute(a.name);
26455                 continue;
26456             }
26457             if (Roo.form.HtmlEditor.ablack.indexOf(a.name.toLowerCase()) > -1) {
26458                 node.removeAttribute(a.name);
26459                 continue;
26460             }
26461             if (Roo.form.HtmlEditor.aclean.indexOf(a.name.toLowerCase()) > -1) {
26462                 cleanAttr(a.name,a.value); // fixme..
26463                 continue;
26464             }
26465             if (a.name == 'style') {
26466                 cleanStyle(a.name,a.value);
26467                 continue;
26468             }
26469             /// clean up MS crap..
26470             // tecnically this should be a list of valid class'es..
26471             
26472             
26473             if (a.name == 'class') {
26474                 if (a.value.match(/^Mso/)) {
26475                     node.className = '';
26476                 }
26477                 
26478                 if (a.value.match(/body/)) {
26479                     node.className = '';
26480                 }
26481                 continue;
26482             }
26483             
26484             // style cleanup!?
26485             // class cleanup?
26486             
26487         }
26488         
26489         
26490         this.cleanUpChildren(node);
26491         
26492         
26493     }
26494     
26495     
26496     // hide stuff that is not compatible
26497     /**
26498      * @event blur
26499      * @hide
26500      */
26501     /**
26502      * @event change
26503      * @hide
26504      */
26505     /**
26506      * @event focus
26507      * @hide
26508      */
26509     /**
26510      * @event specialkey
26511      * @hide
26512      */
26513     /**
26514      * @cfg {String} fieldClass @hide
26515      */
26516     /**
26517      * @cfg {String} focusClass @hide
26518      */
26519     /**
26520      * @cfg {String} autoCreate @hide
26521      */
26522     /**
26523      * @cfg {String} inputType @hide
26524      */
26525     /**
26526      * @cfg {String} invalidClass @hide
26527      */
26528     /**
26529      * @cfg {String} invalidText @hide
26530      */
26531     /**
26532      * @cfg {String} msgFx @hide
26533      */
26534     /**
26535      * @cfg {String} validateOnBlur @hide
26536      */
26537 });
26538
26539 Roo.form.HtmlEditor.white = [
26540         'area', 'br', 'img', 'input', 'hr', 'wbr',
26541         
26542        'address', 'blockquote', 'center', 'dd',      'dir',       'div', 
26543        'dl',      'dt',         'h1',     'h2',      'h3',        'h4', 
26544        'h5',      'h6',         'hr',     'isindex', 'listing',   'marquee', 
26545        'menu',    'multicol',   'ol',     'p',       'plaintext', 'pre', 
26546        'table',   'ul',         'xmp', 
26547        
26548        'caption', 'col', 'colgroup', 'tbody', 'td', 'tfoot', 'th', 
26549       'thead',   'tr', 
26550      
26551       'dir', 'menu', 'ol', 'ul', 'dl',
26552        
26553       'embed',  'object'
26554 ];
26555
26556
26557 Roo.form.HtmlEditor.black = [
26558     //    'embed',  'object', // enable - backend responsiblity to clean thiese
26559         'applet', // 
26560         'base',   'basefont', 'bgsound', 'blink',  'body', 
26561         'frame',  'frameset', 'head',    'html',   'ilayer', 
26562         'iframe', 'layer',  'link',     'meta',    'object',   
26563         'script', 'style' ,'title',  'xml' // clean later..
26564 ];
26565 Roo.form.HtmlEditor.clean = [
26566     'script', 'style', 'title', 'xml'
26567 ];
26568 Roo.form.HtmlEditor.remove = [
26569     'font'
26570 ];
26571 // attributes..
26572
26573 Roo.form.HtmlEditor.ablack = [
26574     'on'
26575 ];
26576     
26577 Roo.form.HtmlEditor.aclean = [ 
26578     'action', 'background', 'codebase', 'dynsrc', 'href', 'lowsrc' 
26579 ];
26580
26581 // protocols..
26582 Roo.form.HtmlEditor.pwhite= [
26583         'http',  'https',  'mailto'
26584 ];
26585
26586 // white listed style attributes.
26587 Roo.form.HtmlEditor.cwhite= [
26588       //  'text-align', /// default is to allow most things..
26589       
26590          
26591 //        'font-size'//??
26592 ];
26593
26594 // black listed style attributes.
26595 Roo.form.HtmlEditor.cblack= [
26596       //  'font-size' -- this can be set by the project 
26597 ];
26598
26599
26600 Roo.form.HtmlEditor.swapCodes   =[ 
26601     [    8211, "--" ], 
26602     [    8212, "--" ], 
26603     [    8216,  "'" ],  
26604     [    8217, "'" ],  
26605     [    8220, '"' ],  
26606     [    8221, '"' ],  
26607     [    8226, "*" ],  
26608     [    8230, "..." ]
26609 ]; 
26610
26611     // <script type="text/javascript">
26612 /*
26613  * Based on
26614  * Ext JS Library 1.1.1
26615  * Copyright(c) 2006-2007, Ext JS, LLC.
26616  *  
26617  
26618  */
26619
26620 /**
26621  * @class Roo.form.HtmlEditorToolbar1
26622  * Basic Toolbar
26623  * 
26624  * Usage:
26625  *
26626  new Roo.form.HtmlEditor({
26627     ....
26628     toolbars : [
26629         new Roo.form.HtmlEditorToolbar1({
26630             disable : { fonts: 1 , format: 1, ..., ... , ...],
26631             btns : [ .... ]
26632         })
26633     }
26634      
26635  * 
26636  * @cfg {Object} disable List of elements to disable..
26637  * @cfg {Array} btns List of additional buttons.
26638  * 
26639  * 
26640  * NEEDS Extra CSS? 
26641  * .x-html-editor-tb .x-edit-none .x-btn-text { background: none; }
26642  */
26643  
26644 Roo.form.HtmlEditor.ToolbarStandard = function(config)
26645 {
26646     
26647     Roo.apply(this, config);
26648     
26649     // default disabled, based on 'good practice'..
26650     this.disable = this.disable || {};
26651     Roo.applyIf(this.disable, {
26652         fontSize : true,
26653         colors : true,
26654         specialElements : true
26655     });
26656     
26657     
26658     //Roo.form.HtmlEditorToolbar1.superclass.constructor.call(this, editor.wrap.dom.firstChild, [], config);
26659     // dont call parent... till later.
26660 }
26661
26662 Roo.apply(Roo.form.HtmlEditor.ToolbarStandard.prototype,  {
26663     
26664     tb: false,
26665     
26666     rendered: false,
26667     
26668     editor : false,
26669     /**
26670      * @cfg {Object} disable  List of toolbar elements to disable
26671          
26672      */
26673     disable : false,
26674       /**
26675      * @cfg {Array} fontFamilies An array of available font families
26676      */
26677     fontFamilies : [
26678         'Arial',
26679         'Courier New',
26680         'Tahoma',
26681         'Times New Roman',
26682         'Verdana'
26683     ],
26684     
26685     specialChars : [
26686            "&#169;",
26687           "&#174;",     
26688           "&#8482;",    
26689           "&#163;" ,    
26690          // "&#8212;",    
26691           "&#8230;",    
26692           "&#247;" ,    
26693         //  "&#225;" ,     ?? a acute?
26694            "&#8364;"    , //Euro
26695        //   "&#8220;"    ,
26696         //  "&#8221;"    ,
26697         //  "&#8226;"    ,
26698           "&#176;"  //   , // degrees
26699
26700          // "&#233;"     , // e ecute
26701          // "&#250;"     , // u ecute?
26702     ],
26703     
26704     specialElements : [
26705         {
26706             text: "Insert Table",
26707             xtype: 'MenuItem',
26708             xns : Roo.Menu,
26709             ihtml :  '<table><tr><td>Cell</td></tr></table>' 
26710                 
26711         },
26712         {    
26713             text: "Insert Image",
26714             xtype: 'MenuItem',
26715             xns : Roo.Menu,
26716             ihtml : '<img src="about:blank"/>'
26717             
26718         }
26719         
26720          
26721     ],
26722     
26723     
26724     inputElements : [ 
26725             "form", "input:text", "input:hidden", "input:checkbox", "input:radio", "input:password", 
26726             "input:submit", "input:button", "select", "textarea", "label" ],
26727     formats : [
26728         ["p"] ,  
26729         ["h1"],["h2"],["h3"],["h4"],["h5"],["h6"], 
26730         ["pre"],[ "code"], 
26731         ["abbr"],[ "acronym"],[ "address"],[ "cite"],[ "samp"],[ "var"],
26732         ['div'],['span']
26733     ],
26734      /**
26735      * @cfg {String} defaultFont default font to use.
26736      */
26737     defaultFont: 'tahoma',
26738    
26739     fontSelect : false,
26740     
26741     
26742     formatCombo : false,
26743     
26744     init : function(editor)
26745     {
26746         this.editor = editor;
26747         
26748         
26749         var fid = editor.frameId;
26750         var etb = this;
26751         function btn(id, toggle, handler){
26752             var xid = fid + '-'+ id ;
26753             return {
26754                 id : xid,
26755                 cmd : id,
26756                 cls : 'x-btn-icon x-edit-'+id,
26757                 enableToggle:toggle !== false,
26758                 scope: editor, // was editor...
26759                 handler:handler||editor.relayBtnCmd,
26760                 clickEvent:'mousedown',
26761                 tooltip: etb.buttonTips[id] || undefined, ///tips ???
26762                 tabIndex:-1
26763             };
26764         }
26765         
26766         
26767         
26768         var tb = new Roo.Toolbar(editor.wrap.dom.firstChild);
26769         this.tb = tb;
26770          // stop form submits
26771         tb.el.on('click', function(e){
26772             e.preventDefault(); // what does this do?
26773         });
26774
26775         if(!this.disable.font) { // && !Roo.isSafari){
26776             /* why no safari for fonts 
26777             editor.fontSelect = tb.el.createChild({
26778                 tag:'select',
26779                 tabIndex: -1,
26780                 cls:'x-font-select',
26781                 html: this.createFontOptions()
26782             });
26783             
26784             editor.fontSelect.on('change', function(){
26785                 var font = editor.fontSelect.dom.value;
26786                 editor.relayCmd('fontname', font);
26787                 editor.deferFocus();
26788             }, editor);
26789             
26790             tb.add(
26791                 editor.fontSelect.dom,
26792                 '-'
26793             );
26794             */
26795             
26796         };
26797         if(!this.disable.formats){
26798             this.formatCombo = new Roo.form.ComboBox({
26799                 store: new Roo.data.SimpleStore({
26800                     id : 'tag',
26801                     fields: ['tag'],
26802                     data : this.formats // from states.js
26803                 }),
26804                 blockFocus : true,
26805                 name : '',
26806                 //autoCreate : {tag: "div",  size: "20"},
26807                 displayField:'tag',
26808                 typeAhead: false,
26809                 mode: 'local',
26810                 editable : false,
26811                 triggerAction: 'all',
26812                 emptyText:'Add tag',
26813                 selectOnFocus:true,
26814                 width:135,
26815                 listeners : {
26816                     'select': function(c, r, i) {
26817                         editor.insertTag(r.get('tag'));
26818                         editor.focus();
26819                     }
26820                 }
26821
26822             });
26823             tb.addField(this.formatCombo);
26824             
26825         }
26826         
26827         if(!this.disable.format){
26828             tb.add(
26829                 btn('bold'),
26830                 btn('italic'),
26831                 btn('underline')
26832             );
26833         };
26834         if(!this.disable.fontSize){
26835             tb.add(
26836                 '-',
26837                 
26838                 
26839                 btn('increasefontsize', false, editor.adjustFont),
26840                 btn('decreasefontsize', false, editor.adjustFont)
26841             );
26842         };
26843         
26844         
26845         if(!this.disable.colors){
26846             tb.add(
26847                 '-', {
26848                     id:editor.frameId +'-forecolor',
26849                     cls:'x-btn-icon x-edit-forecolor',
26850                     clickEvent:'mousedown',
26851                     tooltip: this.buttonTips['forecolor'] || undefined,
26852                     tabIndex:-1,
26853                     menu : new Roo.menu.ColorMenu({
26854                         allowReselect: true,
26855                         focus: Roo.emptyFn,
26856                         value:'000000',
26857                         plain:true,
26858                         selectHandler: function(cp, color){
26859                             editor.execCmd('forecolor', Roo.isSafari || Roo.isIE ? '#'+color : color);
26860                             editor.deferFocus();
26861                         },
26862                         scope: editor,
26863                         clickEvent:'mousedown'
26864                     })
26865                 }, {
26866                     id:editor.frameId +'backcolor',
26867                     cls:'x-btn-icon x-edit-backcolor',
26868                     clickEvent:'mousedown',
26869                     tooltip: this.buttonTips['backcolor'] || undefined,
26870                     tabIndex:-1,
26871                     menu : new Roo.menu.ColorMenu({
26872                         focus: Roo.emptyFn,
26873                         value:'FFFFFF',
26874                         plain:true,
26875                         allowReselect: true,
26876                         selectHandler: function(cp, color){
26877                             if(Roo.isGecko){
26878                                 editor.execCmd('useCSS', false);
26879                                 editor.execCmd('hilitecolor', color);
26880                                 editor.execCmd('useCSS', true);
26881                                 editor.deferFocus();
26882                             }else{
26883                                 editor.execCmd(Roo.isOpera ? 'hilitecolor' : 'backcolor', 
26884                                     Roo.isSafari || Roo.isIE ? '#'+color : color);
26885                                 editor.deferFocus();
26886                             }
26887                         },
26888                         scope:editor,
26889                         clickEvent:'mousedown'
26890                     })
26891                 }
26892             );
26893         };
26894         // now add all the items...
26895         
26896
26897         if(!this.disable.alignments){
26898             tb.add(
26899                 '-',
26900                 btn('justifyleft'),
26901                 btn('justifycenter'),
26902                 btn('justifyright')
26903             );
26904         };
26905
26906         //if(!Roo.isSafari){
26907             if(!this.disable.links){
26908                 tb.add(
26909                     '-',
26910                     btn('createlink', false, editor.createLink)    /// MOVE TO HERE?!!?!?!?!
26911                 );
26912             };
26913
26914             if(!this.disable.lists){
26915                 tb.add(
26916                     '-',
26917                     btn('insertorderedlist'),
26918                     btn('insertunorderedlist')
26919                 );
26920             }
26921             if(!this.disable.sourceEdit){
26922                 tb.add(
26923                     '-',
26924                     btn('sourceedit', true, function(btn){
26925                         this.toggleSourceEdit(btn.pressed);
26926                     })
26927                 );
26928             }
26929         //}
26930         
26931         var smenu = { };
26932         // special menu.. - needs to be tidied up..
26933         if (!this.disable.special) {
26934             smenu = {
26935                 text: "&#169;",
26936                 cls: 'x-edit-none',
26937                 
26938                 menu : {
26939                     items : []
26940                 }
26941             };
26942             for (var i =0; i < this.specialChars.length; i++) {
26943                 smenu.menu.items.push({
26944                     
26945                     html: this.specialChars[i],
26946                     handler: function(a,b) {
26947                         editor.insertAtCursor(String.fromCharCode(a.html.replace('&#','').replace(';', '')));
26948                         //editor.insertAtCursor(a.html);
26949                         
26950                     },
26951                     tabIndex:-1
26952                 });
26953             }
26954             
26955             
26956             tb.add(smenu);
26957             
26958             
26959         }
26960          
26961         if (!this.disable.specialElements) {
26962             var semenu = {
26963                 text: "Other;",
26964                 cls: 'x-edit-none',
26965                 menu : {
26966                     items : []
26967                 }
26968             };
26969             for (var i =0; i < this.specialElements.length; i++) {
26970                 semenu.menu.items.push(
26971                     Roo.apply({ 
26972                         handler: function(a,b) {
26973                             editor.insertAtCursor(this.ihtml);
26974                         }
26975                     }, this.specialElements[i])
26976                 );
26977                     
26978             }
26979             
26980             tb.add(semenu);
26981             
26982             
26983         }
26984          
26985         
26986         if (this.btns) {
26987             for(var i =0; i< this.btns.length;i++) {
26988                 var b = Roo.factory(this.btns[i],Roo.form);
26989                 b.cls =  'x-edit-none';
26990                 b.scope = editor;
26991                 tb.add(b);
26992             }
26993         
26994         }
26995         
26996         
26997         
26998         // disable everything...
26999         
27000         this.tb.items.each(function(item){
27001            if(item.id != editor.frameId+ '-sourceedit'){
27002                 item.disable();
27003             }
27004         });
27005         this.rendered = true;
27006         
27007         // the all the btns;
27008         editor.on('editorevent', this.updateToolbar, this);
27009         // other toolbars need to implement this..
27010         //editor.on('editmodechange', this.updateToolbar, this);
27011     },
27012     
27013     
27014     
27015     /**
27016      * Protected method that will not generally be called directly. It triggers
27017      * a toolbar update by reading the markup state of the current selection in the editor.
27018      */
27019     updateToolbar: function(){
27020
27021         if(!this.editor.activated){
27022             this.editor.onFirstFocus();
27023             return;
27024         }
27025
27026         var btns = this.tb.items.map, 
27027             doc = this.editor.doc,
27028             frameId = this.editor.frameId;
27029
27030         if(!this.disable.font && !Roo.isSafari){
27031             /*
27032             var name = (doc.queryCommandValue('FontName')||this.editor.defaultFont).toLowerCase();
27033             if(name != this.fontSelect.dom.value){
27034                 this.fontSelect.dom.value = name;
27035             }
27036             */
27037         }
27038         if(!this.disable.format){
27039             btns[frameId + '-bold'].toggle(doc.queryCommandState('bold'));
27040             btns[frameId + '-italic'].toggle(doc.queryCommandState('italic'));
27041             btns[frameId + '-underline'].toggle(doc.queryCommandState('underline'));
27042         }
27043         if(!this.disable.alignments){
27044             btns[frameId + '-justifyleft'].toggle(doc.queryCommandState('justifyleft'));
27045             btns[frameId + '-justifycenter'].toggle(doc.queryCommandState('justifycenter'));
27046             btns[frameId + '-justifyright'].toggle(doc.queryCommandState('justifyright'));
27047         }
27048         if(!Roo.isSafari && !this.disable.lists){
27049             btns[frameId + '-insertorderedlist'].toggle(doc.queryCommandState('insertorderedlist'));
27050             btns[frameId + '-insertunorderedlist'].toggle(doc.queryCommandState('insertunorderedlist'));
27051         }
27052         
27053         var ans = this.editor.getAllAncestors();
27054         if (this.formatCombo) {
27055             
27056             
27057             var store = this.formatCombo.store;
27058             this.formatCombo.setValue("");
27059             for (var i =0; i < ans.length;i++) {
27060                 if (ans[i] && store.query('tag',ans[i].tagName.toLowerCase(), false).length) {
27061                     // select it..
27062                     this.formatCombo.setValue(ans[i].tagName.toLowerCase());
27063                     break;
27064                 }
27065             }
27066         }
27067         
27068         
27069         
27070         // hides menus... - so this cant be on a menu...
27071         Roo.menu.MenuMgr.hideAll();
27072
27073         //this.editorsyncValue();
27074     },
27075    
27076     
27077     createFontOptions : function(){
27078         var buf = [], fs = this.fontFamilies, ff, lc;
27079         
27080         
27081         
27082         for(var i = 0, len = fs.length; i< len; i++){
27083             ff = fs[i];
27084             lc = ff.toLowerCase();
27085             buf.push(
27086                 '<option value="',lc,'" style="font-family:',ff,';"',
27087                     (this.defaultFont == lc ? ' selected="true">' : '>'),
27088                     ff,
27089                 '</option>'
27090             );
27091         }
27092         return buf.join('');
27093     },
27094     
27095     toggleSourceEdit : function(sourceEditMode){
27096         if(sourceEditMode === undefined){
27097             sourceEditMode = !this.sourceEditMode;
27098         }
27099         this.sourceEditMode = sourceEditMode === true;
27100         var btn = this.tb.items.get(this.editor.frameId +'-sourceedit');
27101         // just toggle the button?
27102         if(btn.pressed !== this.editor.sourceEditMode){
27103             btn.toggle(this.editor.sourceEditMode);
27104             return;
27105         }
27106         
27107         if(this.sourceEditMode){
27108             this.tb.items.each(function(item){
27109                 if(item.cmd != 'sourceedit'){
27110                     item.disable();
27111                 }
27112             });
27113           
27114         }else{
27115             if(this.initialized){
27116                 this.tb.items.each(function(item){
27117                     item.enable();
27118                 });
27119             }
27120             
27121         }
27122         // tell the editor that it's been pressed..
27123         this.editor.toggleSourceEdit(sourceEditMode);
27124        
27125     },
27126      /**
27127      * Object collection of toolbar tooltips for the buttons in the editor. The key
27128      * is the command id associated with that button and the value is a valid QuickTips object.
27129      * For example:
27130 <pre><code>
27131 {
27132     bold : {
27133         title: 'Bold (Ctrl+B)',
27134         text: 'Make the selected text bold.',
27135         cls: 'x-html-editor-tip'
27136     },
27137     italic : {
27138         title: 'Italic (Ctrl+I)',
27139         text: 'Make the selected text italic.',
27140         cls: 'x-html-editor-tip'
27141     },
27142     ...
27143 </code></pre>
27144     * @type Object
27145      */
27146     buttonTips : {
27147         bold : {
27148             title: 'Bold (Ctrl+B)',
27149             text: 'Make the selected text bold.',
27150             cls: 'x-html-editor-tip'
27151         },
27152         italic : {
27153             title: 'Italic (Ctrl+I)',
27154             text: 'Make the selected text italic.',
27155             cls: 'x-html-editor-tip'
27156         },
27157         underline : {
27158             title: 'Underline (Ctrl+U)',
27159             text: 'Underline the selected text.',
27160             cls: 'x-html-editor-tip'
27161         },
27162         increasefontsize : {
27163             title: 'Grow Text',
27164             text: 'Increase the font size.',
27165             cls: 'x-html-editor-tip'
27166         },
27167         decreasefontsize : {
27168             title: 'Shrink Text',
27169             text: 'Decrease the font size.',
27170             cls: 'x-html-editor-tip'
27171         },
27172         backcolor : {
27173             title: 'Text Highlight Color',
27174             text: 'Change the background color of the selected text.',
27175             cls: 'x-html-editor-tip'
27176         },
27177         forecolor : {
27178             title: 'Font Color',
27179             text: 'Change the color of the selected text.',
27180             cls: 'x-html-editor-tip'
27181         },
27182         justifyleft : {
27183             title: 'Align Text Left',
27184             text: 'Align text to the left.',
27185             cls: 'x-html-editor-tip'
27186         },
27187         justifycenter : {
27188             title: 'Center Text',
27189             text: 'Center text in the editor.',
27190             cls: 'x-html-editor-tip'
27191         },
27192         justifyright : {
27193             title: 'Align Text Right',
27194             text: 'Align text to the right.',
27195             cls: 'x-html-editor-tip'
27196         },
27197         insertunorderedlist : {
27198             title: 'Bullet List',
27199             text: 'Start a bulleted list.',
27200             cls: 'x-html-editor-tip'
27201         },
27202         insertorderedlist : {
27203             title: 'Numbered List',
27204             text: 'Start a numbered list.',
27205             cls: 'x-html-editor-tip'
27206         },
27207         createlink : {
27208             title: 'Hyperlink',
27209             text: 'Make the selected text a hyperlink.',
27210             cls: 'x-html-editor-tip'
27211         },
27212         sourceedit : {
27213             title: 'Source Edit',
27214             text: 'Switch to source editing mode.',
27215             cls: 'x-html-editor-tip'
27216         }
27217     },
27218     // private
27219     onDestroy : function(){
27220         if(this.rendered){
27221             
27222             this.tb.items.each(function(item){
27223                 if(item.menu){
27224                     item.menu.removeAll();
27225                     if(item.menu.el){
27226                         item.menu.el.destroy();
27227                     }
27228                 }
27229                 item.destroy();
27230             });
27231              
27232         }
27233     },
27234     onFirstFocus: function() {
27235         this.tb.items.each(function(item){
27236            item.enable();
27237         });
27238     }
27239 });
27240
27241
27242
27243
27244 // <script type="text/javascript">
27245 /*
27246  * Based on
27247  * Ext JS Library 1.1.1
27248  * Copyright(c) 2006-2007, Ext JS, LLC.
27249  *  
27250  
27251  */
27252
27253  
27254 /**
27255  * @class Roo.form.HtmlEditor.ToolbarContext
27256  * Context Toolbar
27257  * 
27258  * Usage:
27259  *
27260  new Roo.form.HtmlEditor({
27261     ....
27262     toolbars : [
27263         { xtype: 'ToolbarStandard', styles : {} }
27264         { xtype: 'ToolbarContext', disable : {} }
27265     ]
27266 })
27267
27268      
27269  * 
27270  * @config : {Object} disable List of elements to disable.. (not done yet.)
27271  * @config : {Object} styles  Map of styles available.
27272  * 
27273  */
27274
27275 Roo.form.HtmlEditor.ToolbarContext = function(config)
27276 {
27277     
27278     Roo.apply(this, config);
27279     //Roo.form.HtmlEditorToolbar1.superclass.constructor.call(this, editor.wrap.dom.firstChild, [], config);
27280     // dont call parent... till later.
27281     this.styles = this.styles || {};
27282 }
27283
27284  
27285
27286 Roo.form.HtmlEditor.ToolbarContext.types = {
27287     'IMG' : {
27288         width : {
27289             title: "Width",
27290             width: 40
27291         },
27292         height:  {
27293             title: "Height",
27294             width: 40
27295         },
27296         align: {
27297             title: "Align",
27298             opts : [ [""],[ "left"],[ "right"],[ "center"],[ "top"]],
27299             width : 80
27300             
27301         },
27302         border: {
27303             title: "Border",
27304             width: 40
27305         },
27306         alt: {
27307             title: "Alt",
27308             width: 120
27309         },
27310         src : {
27311             title: "Src",
27312             width: 220
27313         }
27314         
27315     },
27316     'A' : {
27317         name : {
27318             title: "Name",
27319             width: 50
27320         },
27321         href:  {
27322             title: "Href",
27323             width: 220
27324         } // border?
27325         
27326     },
27327     'TABLE' : {
27328         rows : {
27329             title: "Rows",
27330             width: 20
27331         },
27332         cols : {
27333             title: "Cols",
27334             width: 20
27335         },
27336         width : {
27337             title: "Width",
27338             width: 40
27339         },
27340         height : {
27341             title: "Height",
27342             width: 40
27343         },
27344         border : {
27345             title: "Border",
27346             width: 20
27347         }
27348     },
27349     'TD' : {
27350         width : {
27351             title: "Width",
27352             width: 40
27353         },
27354         height : {
27355             title: "Height",
27356             width: 40
27357         },   
27358         align: {
27359             title: "Align",
27360             opts : [[""],[ "left"],[ "center"],[ "right"],[ "justify"],[ "char"]],
27361             width: 80
27362         },
27363         valign: {
27364             title: "Valign",
27365             opts : [[""],[ "top"],[ "middle"],[ "bottom"],[ "baseline"]],
27366             width: 80
27367         },
27368         colspan: {
27369             title: "Colspan",
27370             width: 20
27371             
27372         },
27373          'font-family'  : {
27374             title : "Font",
27375             style : 'fontFamily',
27376             displayField: 'display',
27377             optname : 'font-family',
27378             width: 140
27379         }
27380     },
27381     'INPUT' : {
27382         name : {
27383             title: "name",
27384             width: 120
27385         },
27386         value : {
27387             title: "Value",
27388             width: 120
27389         },
27390         width : {
27391             title: "Width",
27392             width: 40
27393         }
27394     },
27395     'LABEL' : {
27396         'for' : {
27397             title: "For",
27398             width: 120
27399         }
27400     },
27401     'TEXTAREA' : {
27402           name : {
27403             title: "name",
27404             width: 120
27405         },
27406         rows : {
27407             title: "Rows",
27408             width: 20
27409         },
27410         cols : {
27411             title: "Cols",
27412             width: 20
27413         }
27414     },
27415     'SELECT' : {
27416         name : {
27417             title: "name",
27418             width: 120
27419         },
27420         selectoptions : {
27421             title: "Options",
27422             width: 200
27423         }
27424     },
27425     
27426     // should we really allow this??
27427     // should this just be 
27428     'BODY' : {
27429         title : {
27430             title: "Title",
27431             width: 200,
27432             disabled : true
27433         }
27434     },
27435     'SPAN' : {
27436         'font-family'  : {
27437             title : "Font",
27438             style : 'fontFamily',
27439             displayField: 'display',
27440             optname : 'font-family',
27441             width: 140
27442         }
27443     },
27444     'DIV' : {
27445         'font-family'  : {
27446             title : "Font",
27447             style : 'fontFamily',
27448             displayField: 'display',
27449             optname : 'font-family',
27450             width: 140
27451         }
27452     },
27453      'P' : {
27454         'font-family'  : {
27455             title : "Font",
27456             style : 'fontFamily',
27457             displayField: 'display',
27458             optname : 'font-family',
27459             width: 140
27460         }
27461     },
27462     
27463     '*' : {
27464         // empty..
27465     }
27466
27467 };
27468
27469 // this should be configurable.. - you can either set it up using stores, or modify options somehwere..
27470 Roo.form.HtmlEditor.ToolbarContext.stores = false;
27471
27472 Roo.form.HtmlEditor.ToolbarContext.options = {
27473         'font-family'  : [ 
27474                 [ 'Helvetica,Arial,sans-serif', 'Helvetica'],
27475                 [ 'Courier New', 'Courier New'],
27476                 [ 'Tahoma', 'Tahoma'],
27477                 [ 'Times New Roman,serif', 'Times'],
27478                 [ 'Verdana','Verdana' ]
27479         ]
27480 };
27481
27482 // fixme - these need to be configurable..
27483  
27484
27485 Roo.form.HtmlEditor.ToolbarContext.types
27486
27487
27488 Roo.apply(Roo.form.HtmlEditor.ToolbarContext.prototype,  {
27489     
27490     tb: false,
27491     
27492     rendered: false,
27493     
27494     editor : false,
27495     /**
27496      * @cfg {Object} disable  List of toolbar elements to disable
27497          
27498      */
27499     disable : false,
27500     /**
27501      * @cfg {Object} styles List of styles 
27502      *    eg. { '*' : [ 'headline' ] , 'TD' : [ 'underline', 'double-underline' ] } 
27503      *
27504      * These must be defined in the page, so they get rendered correctly..
27505      * .headline { }
27506      * TD.underline { }
27507      * 
27508      */
27509     styles : false,
27510     
27511     options: false,
27512     
27513     toolbars : false,
27514     
27515     init : function(editor)
27516     {
27517         this.editor = editor;
27518         
27519         
27520         var fid = editor.frameId;
27521         var etb = this;
27522         function btn(id, toggle, handler){
27523             var xid = fid + '-'+ id ;
27524             return {
27525                 id : xid,
27526                 cmd : id,
27527                 cls : 'x-btn-icon x-edit-'+id,
27528                 enableToggle:toggle !== false,
27529                 scope: editor, // was editor...
27530                 handler:handler||editor.relayBtnCmd,
27531                 clickEvent:'mousedown',
27532                 tooltip: etb.buttonTips[id] || undefined, ///tips ???
27533                 tabIndex:-1
27534             };
27535         }
27536         // create a new element.
27537         var wdiv = editor.wrap.createChild({
27538                 tag: 'div'
27539             }, editor.wrap.dom.firstChild.nextSibling, true);
27540         
27541         // can we do this more than once??
27542         
27543          // stop form submits
27544       
27545  
27546         // disable everything...
27547         var ty= Roo.form.HtmlEditor.ToolbarContext.types;
27548         this.toolbars = {};
27549            
27550         for (var i in  ty) {
27551           
27552             this.toolbars[i] = this.buildToolbar(ty[i],i);
27553         }
27554         this.tb = this.toolbars.BODY;
27555         this.tb.el.show();
27556         this.buildFooter();
27557         this.footer.show();
27558         editor.on('hide', function( ) { this.footer.hide() }, this);
27559         editor.on('show', function( ) { this.footer.show() }, this);
27560         
27561          
27562         this.rendered = true;
27563         
27564         // the all the btns;
27565         editor.on('editorevent', this.updateToolbar, this);
27566         // other toolbars need to implement this..
27567         //editor.on('editmodechange', this.updateToolbar, this);
27568     },
27569     
27570     
27571     
27572     /**
27573      * Protected method that will not generally be called directly. It triggers
27574      * a toolbar update by reading the markup state of the current selection in the editor.
27575      */
27576     updateToolbar: function(editor,ev,sel){
27577
27578         //Roo.log(ev);
27579         // capture mouse up - this is handy for selecting images..
27580         // perhaps should go somewhere else...
27581         if(!this.editor.activated){
27582              this.editor.onFirstFocus();
27583             return;
27584         }
27585         
27586         // http://developer.yahoo.com/yui/docs/simple-editor.js.html
27587         // selectNode - might want to handle IE?
27588         if (ev &&
27589             (ev.type == 'mouseup' || ev.type == 'click' ) &&
27590             ev.target && ev.target.tagName == 'IMG') {
27591             // they have click on an image...
27592             // let's see if we can change the selection...
27593             sel = ev.target;
27594          
27595               var nodeRange = sel.ownerDocument.createRange();
27596             try {
27597                 nodeRange.selectNode(sel);
27598             } catch (e) {
27599                 nodeRange.selectNodeContents(sel);
27600             }
27601             //nodeRange.collapse(true);
27602             var s = editor.win.getSelection();
27603             s.removeAllRanges();
27604             s.addRange(nodeRange);
27605         }  
27606         
27607       
27608         var updateFooter = sel ? false : true;
27609         
27610         
27611         var ans = this.editor.getAllAncestors();
27612         
27613         // pick
27614         var ty= Roo.form.HtmlEditor.ToolbarContext.types;
27615         
27616         if (!sel) { 
27617             sel = ans.length ? (ans[0] ?  ans[0]  : ans[1]) : this.editor.doc.body;
27618             sel = sel ? sel : this.editor.doc.body;
27619             sel = sel.tagName.length ? sel : this.editor.doc.body;
27620             
27621         }
27622         // pick a menu that exists..
27623         var tn = sel.tagName.toUpperCase();
27624         //sel = typeof(ty[tn]) != 'undefined' ? sel : this.editor.doc.body;
27625         
27626         tn = sel.tagName.toUpperCase();
27627         
27628         var lastSel = this.tb.selectedNode
27629         
27630         this.tb.selectedNode = sel;
27631         
27632         // if current menu does not match..
27633         if ((this.tb.name != tn) || (lastSel != this.tb.selectedNode)) {
27634                 
27635             this.tb.el.hide();
27636             ///console.log("show: " + tn);
27637             this.tb =  typeof(ty[tn]) != 'undefined' ? this.toolbars[tn] : this.toolbars['*'];
27638             this.tb.el.show();
27639             // update name
27640             this.tb.items.first().el.innerHTML = tn + ':&nbsp;';
27641             
27642             
27643             // update attributes
27644             if (this.tb.fields) {
27645                 this.tb.fields.each(function(e) {
27646                     if (e.stylename) {
27647                         e.setValue(sel.style[e.stylename]);
27648                         return;
27649                     } 
27650                    e.setValue(sel.getAttribute(e.attrname));
27651                 });
27652             }
27653             
27654             var hasStyles = false;
27655             for(var i in this.styles) {
27656                 hasStyles = true;
27657                 break;
27658             }
27659             
27660             // update styles
27661             if (hasStyles) { 
27662                 var st = this.tb.fields.item(0);
27663                 
27664                 st.store.removeAll();
27665                
27666                 
27667                 var cn = sel.className.split(/\s+/);
27668                 
27669                 var avs = [];
27670                 if (this.styles['*']) {
27671                     
27672                     Roo.each(this.styles['*'], function(v) {
27673                         avs.push( [ v , cn.indexOf(v) > -1 ? 1 : 0 ] );         
27674                     });
27675                 }
27676                 if (this.styles[tn]) { 
27677                     Roo.each(this.styles[tn], function(v) {
27678                         avs.push( [ v , cn.indexOf(v) > -1 ? 1 : 0 ] );         
27679                     });
27680                 }
27681                 
27682                 st.store.loadData(avs);
27683                 st.collapse();
27684                 st.setValue(cn);
27685             }
27686             // flag our selected Node.
27687             this.tb.selectedNode = sel;
27688            
27689            
27690             Roo.menu.MenuMgr.hideAll();
27691
27692         }
27693         
27694         if (!updateFooter) {
27695             //this.footDisp.dom.innerHTML = ''; 
27696             return;
27697         }
27698         // update the footer
27699         //
27700         var html = '';
27701         
27702         this.footerEls = ans.reverse();
27703         Roo.each(this.footerEls, function(a,i) {
27704             if (!a) { return; }
27705             html += html.length ? ' &gt; '  :  '';
27706             
27707             html += '<span class="x-ed-loc-' + i + '">' + a.tagName + '</span>';
27708             
27709         });
27710        
27711         // 
27712         var sz = this.footDisp.up('td').getSize();
27713         this.footDisp.dom.style.width = (sz.width -10) + 'px';
27714         this.footDisp.dom.style.marginLeft = '5px';
27715         
27716         this.footDisp.dom.style.overflow = 'hidden';
27717         
27718         this.footDisp.dom.innerHTML = html;
27719             
27720         //this.editorsyncValue();
27721     },
27722      
27723     
27724    
27725        
27726     // private
27727     onDestroy : function(){
27728         if(this.rendered){
27729             
27730             this.tb.items.each(function(item){
27731                 if(item.menu){
27732                     item.menu.removeAll();
27733                     if(item.menu.el){
27734                         item.menu.el.destroy();
27735                     }
27736                 }
27737                 item.destroy();
27738             });
27739              
27740         }
27741     },
27742     onFirstFocus: function() {
27743         // need to do this for all the toolbars..
27744         this.tb.items.each(function(item){
27745            item.enable();
27746         });
27747     },
27748     buildToolbar: function(tlist, nm)
27749     {
27750         var editor = this.editor;
27751          // create a new element.
27752         var wdiv = editor.wrap.createChild({
27753                 tag: 'div'
27754             }, editor.wrap.dom.firstChild.nextSibling, true);
27755         
27756        
27757         var tb = new Roo.Toolbar(wdiv);
27758         // add the name..
27759         
27760         tb.add(nm+ ":&nbsp;");
27761         
27762         var styles = [];
27763         for(var i in this.styles) {
27764             styles.push(i);
27765         }
27766         
27767         // styles...
27768         if (styles && styles.length) {
27769             
27770             // this needs a multi-select checkbox...
27771             tb.addField( new Roo.form.ComboBox({
27772                 store: new Roo.data.SimpleStore({
27773                     id : 'val',
27774                     fields: ['val', 'selected'],
27775                     data : [] 
27776                 }),
27777                 name : '-roo-edit-className',
27778                 attrname : 'className',
27779                 displayField: 'val',
27780                 typeAhead: false,
27781                 mode: 'local',
27782                 editable : false,
27783                 triggerAction: 'all',
27784                 emptyText:'Select Style',
27785                 selectOnFocus:true,
27786                 width: 130,
27787                 listeners : {
27788                     'select': function(c, r, i) {
27789                         // initial support only for on class per el..
27790                         tb.selectedNode.className =  r ? r.get('val') : '';
27791                         editor.syncValue();
27792                     }
27793                 }
27794     
27795             }));
27796         }
27797         
27798         var tbc = Roo.form.HtmlEditor.ToolbarContext;
27799         var tbops = tbc.options;
27800         
27801         for (var i in tlist) {
27802             
27803             var item = tlist[i];
27804             tb.add(item.title + ":&nbsp;");
27805             
27806             
27807             //optname == used so you can configure the options available..
27808             var opts = item.opts ? item.opts : false;
27809             if (item.optname) {
27810                 opts = tbops[item.optname];
27811            
27812             }
27813             
27814             if (opts) {
27815                 // opts == pulldown..
27816                 tb.addField( new Roo.form.ComboBox({
27817                     store:   typeof(tbc.stores[i]) != 'undefined' ?  Roo.factory(tbc.stores[i],Roo.data) : new Roo.data.SimpleStore({
27818                         id : 'val',
27819                         fields: ['val', 'display'],
27820                         data : opts  
27821                     }),
27822                     name : '-roo-edit-' + i,
27823                     attrname : i,
27824                     stylename : item.style ? item.style : false,
27825                     displayField: item.displayField ? item.displayField : 'val',
27826                     valueField :  'val',
27827                     typeAhead: false,
27828                     mode: typeof(tbc.stores[i]) != 'undefined'  ? 'remote' : 'local',
27829                     editable : false,
27830                     triggerAction: 'all',
27831                     emptyText:'Select',
27832                     selectOnFocus:true,
27833                     width: item.width ? item.width  : 130,
27834                     listeners : {
27835                         'select': function(c, r, i) {
27836                             if (c.stylename) {
27837                                 tb.selectedNode.style[c.stylename] =  r.get('val');
27838                                 return;
27839                             }
27840                             tb.selectedNode.setAttribute(c.attrname, r.get('val'));
27841                         }
27842                     }
27843
27844                 }));
27845                 continue;
27846                     
27847                  
27848                 
27849                 tb.addField( new Roo.form.TextField({
27850                     name: i,
27851                     width: 100,
27852                     //allowBlank:false,
27853                     value: ''
27854                 }));
27855                 continue;
27856             }
27857             tb.addField( new Roo.form.TextField({
27858                 name: '-roo-edit-' + i,
27859                 attrname : i,
27860                 
27861                 width: item.width,
27862                 //allowBlank:true,
27863                 value: '',
27864                 listeners: {
27865                     'change' : function(f, nv, ov) {
27866                         tb.selectedNode.setAttribute(f.attrname, nv);
27867                     }
27868                 }
27869             }));
27870              
27871         }
27872         tb.addFill();
27873         var _this = this;
27874         tb.addButton( {
27875             text: 'Remove Tag',
27876     
27877             listeners : {
27878                 click : function ()
27879                 {
27880                     // remove
27881                     // undo does not work.
27882                      
27883                     var sn = tb.selectedNode;
27884                     
27885                     var pn = sn.parentNode;
27886                     
27887                     var stn =  sn.childNodes[0];
27888                     var en = sn.childNodes[sn.childNodes.length - 1 ];
27889                     while (sn.childNodes.length) {
27890                         var node = sn.childNodes[0];
27891                         sn.removeChild(node);
27892                         //Roo.log(node);
27893                         pn.insertBefore(node, sn);
27894                         
27895                     }
27896                     pn.removeChild(sn);
27897                     var range = editor.createRange();
27898         
27899                     range.setStart(stn,0);
27900                     range.setEnd(en,0); //????
27901                     //range.selectNode(sel);
27902                     
27903                     
27904                     var selection = editor.getSelection();
27905                     selection.removeAllRanges();
27906                     selection.addRange(range);
27907                     
27908                     
27909                     
27910                     //_this.updateToolbar(null, null, pn);
27911                     _this.updateToolbar(null, null, null);
27912                     _this.footDisp.dom.innerHTML = ''; 
27913                 }
27914             }
27915             
27916                     
27917                 
27918             
27919         });
27920         
27921         
27922         tb.el.on('click', function(e){
27923             e.preventDefault(); // what does this do?
27924         });
27925         tb.el.setVisibilityMode( Roo.Element.DISPLAY);
27926         tb.el.hide();
27927         tb.name = nm;
27928         // dont need to disable them... as they will get hidden
27929         return tb;
27930          
27931         
27932     },
27933     buildFooter : function()
27934     {
27935         
27936         var fel = this.editor.wrap.createChild();
27937         this.footer = new Roo.Toolbar(fel);
27938         // toolbar has scrolly on left / right?
27939         var footDisp= new Roo.Toolbar.Fill();
27940         var _t = this;
27941         this.footer.add(
27942             {
27943                 text : '&lt;',
27944                 xtype: 'Button',
27945                 handler : function() {
27946                     _t.footDisp.scrollTo('left',0,true)
27947                 }
27948             }
27949         );
27950         this.footer.add( footDisp );
27951         this.footer.add( 
27952             {
27953                 text : '&gt;',
27954                 xtype: 'Button',
27955                 handler : function() {
27956                     // no animation..
27957                     _t.footDisp.select('span').last().scrollIntoView(_t.footDisp,true);
27958                 }
27959             }
27960         );
27961         var fel = Roo.get(footDisp.el);
27962         fel.addClass('x-editor-context');
27963         this.footDispWrap = fel; 
27964         this.footDispWrap.overflow  = 'hidden';
27965         
27966         this.footDisp = fel.createChild();
27967         this.footDispWrap.on('click', this.onContextClick, this)
27968         
27969         
27970     },
27971     onContextClick : function (ev,dom)
27972     {
27973         ev.preventDefault();
27974         var  cn = dom.className;
27975         //Roo.log(cn);
27976         if (!cn.match(/x-ed-loc-/)) {
27977             return;
27978         }
27979         var n = cn.split('-').pop();
27980         var ans = this.footerEls;
27981         var sel = ans[n];
27982         
27983          // pick
27984         var range = this.editor.createRange();
27985         
27986         range.selectNodeContents(sel);
27987         //range.selectNode(sel);
27988         
27989         
27990         var selection = this.editor.getSelection();
27991         selection.removeAllRanges();
27992         selection.addRange(range);
27993         
27994         
27995         
27996         this.updateToolbar(null, null, sel);
27997         
27998         
27999     }
28000     
28001     
28002     
28003     
28004     
28005 });
28006
28007
28008
28009
28010
28011 /*
28012  * Based on:
28013  * Ext JS Library 1.1.1
28014  * Copyright(c) 2006-2007, Ext JS, LLC.
28015  *
28016  * Originally Released Under LGPL - original licence link has changed is not relivant.
28017  *
28018  * Fork - LGPL
28019  * <script type="text/javascript">
28020  */
28021  
28022 /**
28023  * @class Roo.form.BasicForm
28024  * @extends Roo.util.Observable
28025  * Supplies the functionality to do "actions" on forms and initialize Roo.form.Field types on existing markup.
28026  * @constructor
28027  * @param {String/HTMLElement/Roo.Element} el The form element or its id
28028  * @param {Object} config Configuration options
28029  */
28030 Roo.form.BasicForm = function(el, config){
28031     this.allItems = [];
28032     this.childForms = [];
28033     Roo.apply(this, config);
28034     /*
28035      * The Roo.form.Field items in this form.
28036      * @type MixedCollection
28037      */
28038      
28039      
28040     this.items = new Roo.util.MixedCollection(false, function(o){
28041         return o.id || (o.id = Roo.id());
28042     });
28043     this.addEvents({
28044         /**
28045          * @event beforeaction
28046          * Fires before any action is performed. Return false to cancel the action.
28047          * @param {Form} this
28048          * @param {Action} action The action to be performed
28049          */
28050         beforeaction: true,
28051         /**
28052          * @event actionfailed
28053          * Fires when an action fails.
28054          * @param {Form} this
28055          * @param {Action} action The action that failed
28056          */
28057         actionfailed : true,
28058         /**
28059          * @event actioncomplete
28060          * Fires when an action is completed.
28061          * @param {Form} this
28062          * @param {Action} action The action that completed
28063          */
28064         actioncomplete : true
28065     });
28066     if(el){
28067         this.initEl(el);
28068     }
28069     Roo.form.BasicForm.superclass.constructor.call(this);
28070 };
28071
28072 Roo.extend(Roo.form.BasicForm, Roo.util.Observable, {
28073     /**
28074      * @cfg {String} method
28075      * The request method to use (GET or POST) for form actions if one isn't supplied in the action options.
28076      */
28077     /**
28078      * @cfg {DataReader} reader
28079      * An Roo.data.DataReader (e.g. {@link Roo.data.XmlReader}) to be used to read data when executing "load" actions.
28080      * This is optional as there is built-in support for processing JSON.
28081      */
28082     /**
28083      * @cfg {DataReader} errorReader
28084      * An Roo.data.DataReader (e.g. {@link Roo.data.XmlReader}) to be used to read data when reading validation errors on "submit" actions.
28085      * This is completely optional as there is built-in support for processing JSON.
28086      */
28087     /**
28088      * @cfg {String} url
28089      * The URL to use for form actions if one isn't supplied in the action options.
28090      */
28091     /**
28092      * @cfg {Boolean} fileUpload
28093      * Set to true if this form is a file upload.
28094      */
28095      
28096     /**
28097      * @cfg {Object} baseParams
28098      * Parameters to pass with all requests. e.g. baseParams: {id: '123', foo: 'bar'}.
28099      */
28100      /**
28101      
28102     /**
28103      * @cfg {Number} timeout Timeout for form actions in seconds (default is 30 seconds).
28104      */
28105     timeout: 30,
28106
28107     // private
28108     activeAction : null,
28109
28110     /**
28111      * @cfg {Boolean} trackResetOnLoad If set to true, form.reset() resets to the last loaded
28112      * or setValues() data instead of when the form was first created.
28113      */
28114     trackResetOnLoad : false,
28115     
28116     
28117     /**
28118      * childForms - used for multi-tab forms
28119      * @type {Array}
28120      */
28121     childForms : false,
28122     
28123     /**
28124      * allItems - full list of fields.
28125      * @type {Array}
28126      */
28127     allItems : false,
28128     
28129     /**
28130      * By default wait messages are displayed with Roo.MessageBox.wait. You can target a specific
28131      * element by passing it or its id or mask the form itself by passing in true.
28132      * @type Mixed
28133      */
28134     waitMsgTarget : false,
28135
28136     // private
28137     initEl : function(el){
28138         this.el = Roo.get(el);
28139         this.id = this.el.id || Roo.id();
28140         this.el.on('submit', this.onSubmit, this);
28141         this.el.addClass('x-form');
28142     },
28143
28144     // private
28145     onSubmit : function(e){
28146         e.stopEvent();
28147     },
28148
28149     /**
28150      * Returns true if client-side validation on the form is successful.
28151      * @return Boolean
28152      */
28153     isValid : function(){
28154         var valid = true;
28155         this.items.each(function(f){
28156            if(!f.validate()){
28157                valid = false;
28158            }
28159         });
28160         return valid;
28161     },
28162
28163     /**
28164      * Returns true if any fields in this form have changed since their original load.
28165      * @return Boolean
28166      */
28167     isDirty : function(){
28168         var dirty = false;
28169         this.items.each(function(f){
28170            if(f.isDirty()){
28171                dirty = true;
28172                return false;
28173            }
28174         });
28175         return dirty;
28176     },
28177
28178     /**
28179      * Performs a predefined action (submit or load) or custom actions you define on this form.
28180      * @param {String} actionName The name of the action type
28181      * @param {Object} options (optional) The options to pass to the action.  All of the config options listed
28182      * below are supported by both the submit and load actions unless otherwise noted (custom actions could also
28183      * accept other config options):
28184      * <pre>
28185 Property          Type             Description
28186 ----------------  ---------------  ----------------------------------------------------------------------------------
28187 url               String           The url for the action (defaults to the form's url)
28188 method            String           The form method to use (defaults to the form's method, or POST if not defined)
28189 params            String/Object    The params to pass (defaults to the form's baseParams, or none if not defined)
28190 clientValidation  Boolean          Applies to submit only.  Pass true to call form.isValid() prior to posting to
28191                                    validate the form on the client (defaults to false)
28192      * </pre>
28193      * @return {BasicForm} this
28194      */
28195     doAction : function(action, options){
28196         if(typeof action == 'string'){
28197             action = new Roo.form.Action.ACTION_TYPES[action](this, options);
28198         }
28199         if(this.fireEvent('beforeaction', this, action) !== false){
28200             this.beforeAction(action);
28201             action.run.defer(100, action);
28202         }
28203         return this;
28204     },
28205
28206     /**
28207      * Shortcut to do a submit action.
28208      * @param {Object} options The options to pass to the action (see {@link #doAction} for details)
28209      * @return {BasicForm} this
28210      */
28211     submit : function(options){
28212         this.doAction('submit', options);
28213         return this;
28214     },
28215
28216     /**
28217      * Shortcut to do a load action.
28218      * @param {Object} options The options to pass to the action (see {@link #doAction} for details)
28219      * @return {BasicForm} this
28220      */
28221     load : function(options){
28222         this.doAction('load', options);
28223         return this;
28224     },
28225
28226     /**
28227      * Persists the values in this form into the passed Roo.data.Record object in a beginEdit/endEdit block.
28228      * @param {Record} record The record to edit
28229      * @return {BasicForm} this
28230      */
28231     updateRecord : function(record){
28232         record.beginEdit();
28233         var fs = record.fields;
28234         fs.each(function(f){
28235             var field = this.findField(f.name);
28236             if(field){
28237                 record.set(f.name, field.getValue());
28238             }
28239         }, this);
28240         record.endEdit();
28241         return this;
28242     },
28243
28244     /**
28245      * Loads an Roo.data.Record into this form.
28246      * @param {Record} record The record to load
28247      * @return {BasicForm} this
28248      */
28249     loadRecord : function(record){
28250         this.setValues(record.data);
28251         return this;
28252     },
28253
28254     // private
28255     beforeAction : function(action){
28256         var o = action.options;
28257         
28258        
28259         if(this.waitMsgTarget === true){
28260             this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
28261         }else if(this.waitMsgTarget){
28262             this.waitMsgTarget = Roo.get(this.waitMsgTarget);
28263             this.waitMsgTarget.mask(o.waitMsg || "Sending", 'x-mask-loading');
28264         }else {
28265             Roo.MessageBox.wait(o.waitMsg || "Sending", o.waitTitle || this.waitTitle || 'Please Wait...');
28266         }
28267          
28268     },
28269
28270     // private
28271     afterAction : function(action, success){
28272         this.activeAction = null;
28273         var o = action.options;
28274         
28275         if(this.waitMsgTarget === true){
28276             this.el.unmask();
28277         }else if(this.waitMsgTarget){
28278             this.waitMsgTarget.unmask();
28279         }else{
28280             Roo.MessageBox.updateProgress(1);
28281             Roo.MessageBox.hide();
28282         }
28283          
28284         if(success){
28285             if(o.reset){
28286                 this.reset();
28287             }
28288             Roo.callback(o.success, o.scope, [this, action]);
28289             this.fireEvent('actioncomplete', this, action);
28290             
28291         }else{
28292             
28293             // failure condition..
28294             // we have a scenario where updates need confirming.
28295             // eg. if a locking scenario exists..
28296             // we look for { errors : { needs_confirm : true }} in the response.
28297             if (
28298                 (typeof(action.result) != 'undefined')  &&
28299                 (typeof(action.result.errors) != 'undefined')  &&
28300                 (typeof(action.result.errors.needs_confirm) != 'undefined')
28301            ){
28302                 var _t = this;
28303                 Roo.MessageBox.confirm(
28304                     "Change requires confirmation",
28305                     action.result.errorMsg,
28306                     function(r) {
28307                         if (r != 'yes') {
28308                             return;
28309                         }
28310                         _t.doAction('submit', { params :  { _submit_confirmed : 1 } }  );
28311                     }
28312                     
28313                 );
28314                 
28315                 
28316                 
28317                 return;
28318             }
28319             
28320             Roo.callback(o.failure, o.scope, [this, action]);
28321             // show an error message if no failed handler is set..
28322             if (!this.hasListener('actionfailed')) {
28323                 Roo.MessageBox.alert("Error",
28324                     (typeof(action.result) != 'undefined' && typeof(action.result.errorMsg) != 'undefined') ?
28325                         action.result.errorMsg :
28326                         "Saving Failed, please check your entries or try again"
28327                 );
28328             }
28329             
28330             this.fireEvent('actionfailed', this, action);
28331         }
28332         
28333     },
28334
28335     /**
28336      * Find a Roo.form.Field in this form by id, dataIndex, name or hiddenName
28337      * @param {String} id The value to search for
28338      * @return Field
28339      */
28340     findField : function(id){
28341         var field = this.items.get(id);
28342         if(!field){
28343             this.items.each(function(f){
28344                 if(f.isFormField && (f.dataIndex == id || f.id == id || f.getName() == id)){
28345                     field = f;
28346                     return false;
28347                 }
28348             });
28349         }
28350         return field || null;
28351     },
28352
28353     /**
28354      * Add a secondary form to this one, 
28355      * Used to provide tabbed forms. One form is primary, with hidden values 
28356      * which mirror the elements from the other forms.
28357      * 
28358      * @param {Roo.form.Form} form to add.
28359      * 
28360      */
28361     addForm : function(form)
28362     {
28363        
28364         if (this.childForms.indexOf(form) > -1) {
28365             // already added..
28366             return;
28367         }
28368         this.childForms.push(form);
28369         var n = '';
28370         Roo.each(form.allItems, function (fe) {
28371             
28372             n = typeof(fe.getName) == 'undefined' ? fe.name : fe.getName();
28373             if (this.findField(n)) { // already added..
28374                 return;
28375             }
28376             var add = new Roo.form.Hidden({
28377                 name : n
28378             });
28379             add.render(this.el);
28380             
28381             this.add( add );
28382         }, this);
28383         
28384     },
28385     /**
28386      * Mark fields in this form invalid in bulk.
28387      * @param {Array/Object} errors Either an array in the form [{id:'fieldId', msg:'The message'},...] or an object hash of {id: msg, id2: msg2}
28388      * @return {BasicForm} this
28389      */
28390     markInvalid : function(errors){
28391         if(errors instanceof Array){
28392             for(var i = 0, len = errors.length; i < len; i++){
28393                 var fieldError = errors[i];
28394                 var f = this.findField(fieldError.id);
28395                 if(f){
28396                     f.markInvalid(fieldError.msg);
28397                 }
28398             }
28399         }else{
28400             var field, id;
28401             for(id in errors){
28402                 if(typeof errors[id] != 'function' && (field = this.findField(id))){
28403                     field.markInvalid(errors[id]);
28404                 }
28405             }
28406         }
28407         Roo.each(this.childForms || [], function (f) {
28408             f.markInvalid(errors);
28409         });
28410         
28411         return this;
28412     },
28413
28414     /**
28415      * Set values for fields in this form in bulk.
28416      * @param {Array/Object} values Either an array in the form [{id:'fieldId', value:'foo'},...] or an object hash of {id: value, id2: value2}
28417      * @return {BasicForm} this
28418      */
28419     setValues : function(values){
28420         if(values instanceof Array){ // array of objects
28421             for(var i = 0, len = values.length; i < len; i++){
28422                 var v = values[i];
28423                 var f = this.findField(v.id);
28424                 if(f){
28425                     f.setValue(v.value);
28426                     if(this.trackResetOnLoad){
28427                         f.originalValue = f.getValue();
28428                     }
28429                 }
28430             }
28431         }else{ // object hash
28432             var field, id;
28433             for(id in values){
28434                 if(typeof values[id] != 'function' && (field = this.findField(id))){
28435                     
28436                     if (field.setFromData && 
28437                         field.valueField && 
28438                         field.displayField &&
28439                         // combos' with local stores can 
28440                         // be queried via setValue()
28441                         // to set their value..
28442                         (field.store && !field.store.isLocal)
28443                         ) {
28444                         // it's a combo
28445                         var sd = { };
28446                         sd[field.valueField] = typeof(values[field.hiddenName]) == 'undefined' ? '' : values[field.hiddenName];
28447                         sd[field.displayField] = typeof(values[field.name]) == 'undefined' ? '' : values[field.name];
28448                         field.setFromData(sd);
28449                         
28450                     } else {
28451                         field.setValue(values[id]);
28452                     }
28453                     
28454                     
28455                     if(this.trackResetOnLoad){
28456                         field.originalValue = field.getValue();
28457                     }
28458                 }
28459             }
28460         }
28461          
28462         Roo.each(this.childForms || [], function (f) {
28463             f.setValues(values);
28464         });
28465                 
28466         return this;
28467     },
28468
28469     /**
28470      * Returns the fields in this form as an object with key/value pairs. If multiple fields exist with the same name
28471      * they are returned as an array.
28472      * @param {Boolean} asString
28473      * @return {Object}
28474      */
28475     getValues : function(asString){
28476         if (this.childForms) {
28477             // copy values from the child forms
28478             Roo.each(this.childForms, function (f) {
28479                 this.setValues(f.getValues());
28480             }, this);
28481         }
28482         
28483         
28484         
28485         var fs = Roo.lib.Ajax.serializeForm(this.el.dom);
28486         if(asString === true){
28487             return fs;
28488         }
28489         return Roo.urlDecode(fs);
28490     },
28491     
28492     /**
28493      * Returns the fields in this form as an object with key/value pairs. 
28494      * This differs from getValues as it calls getValue on each child item, rather than using dom data.
28495      * @return {Object}
28496      */
28497     getFieldValues : function(with_hidden)
28498     {
28499         if (this.childForms) {
28500             // copy values from the child forms
28501             // should this call getFieldValues - probably not as we do not currently copy
28502             // hidden fields when we generate..
28503             Roo.each(this.childForms, function (f) {
28504                 this.setValues(f.getValues());
28505             }, this);
28506         }
28507         
28508         var ret = {};
28509         this.items.each(function(f){
28510             if (!f.getName()) {
28511                 return;
28512             }
28513             var v = f.getValue();
28514             if (f.inputType =='radio') {
28515                 if (typeof(ret[f.getName()]) == 'undefined') {
28516                     ret[f.getName()] = ''; // empty..
28517                 }
28518                 
28519                 if (!f.el.dom.checked) {
28520                     return;
28521                     
28522                 }
28523                 v = f.el.dom.value;
28524                 
28525             }
28526             
28527             // not sure if this supported any more..
28528             if ((typeof(v) == 'object') && f.getRawValue) {
28529                 v = f.getRawValue() ; // dates..
28530             }
28531             // combo boxes where name != hiddenName...
28532             if (f.name != f.getName()) {
28533                 ret[f.name] = f.getRawValue();
28534             }
28535             ret[f.getName()] = v;
28536         });
28537         
28538         return ret;
28539     },
28540
28541     /**
28542      * Clears all invalid messages in this form.
28543      * @return {BasicForm} this
28544      */
28545     clearInvalid : function(){
28546         this.items.each(function(f){
28547            f.clearInvalid();
28548         });
28549         
28550         Roo.each(this.childForms || [], function (f) {
28551             f.clearInvalid();
28552         });
28553         
28554         
28555         return this;
28556     },
28557
28558     /**
28559      * Resets this form.
28560      * @return {BasicForm} this
28561      */
28562     reset : function(){
28563         this.items.each(function(f){
28564             f.reset();
28565         });
28566         
28567         Roo.each(this.childForms || [], function (f) {
28568             f.reset();
28569         });
28570        
28571         
28572         return this;
28573     },
28574
28575     /**
28576      * Add Roo.form components to this form.
28577      * @param {Field} field1
28578      * @param {Field} field2 (optional)
28579      * @param {Field} etc (optional)
28580      * @return {BasicForm} this
28581      */
28582     add : function(){
28583         this.items.addAll(Array.prototype.slice.call(arguments, 0));
28584         return this;
28585     },
28586
28587
28588     /**
28589      * Removes a field from the items collection (does NOT remove its markup).
28590      * @param {Field} field
28591      * @return {BasicForm} this
28592      */
28593     remove : function(field){
28594         this.items.remove(field);
28595         return this;
28596     },
28597
28598     /**
28599      * Looks at the fields in this form, checks them for an id attribute,
28600      * and calls applyTo on the existing dom element with that id.
28601      * @return {BasicForm} this
28602      */
28603     render : function(){
28604         this.items.each(function(f){
28605             if(f.isFormField && !f.rendered && document.getElementById(f.id)){ // if the element exists
28606                 f.applyTo(f.id);
28607             }
28608         });
28609         return this;
28610     },
28611
28612     /**
28613      * Calls {@link Ext#apply} for all fields in this form with the passed object.
28614      * @param {Object} values
28615      * @return {BasicForm} this
28616      */
28617     applyToFields : function(o){
28618         this.items.each(function(f){
28619            Roo.apply(f, o);
28620         });
28621         return this;
28622     },
28623
28624     /**
28625      * Calls {@link Ext#applyIf} for all field in this form with the passed object.
28626      * @param {Object} values
28627      * @return {BasicForm} this
28628      */
28629     applyIfToFields : function(o){
28630         this.items.each(function(f){
28631            Roo.applyIf(f, o);
28632         });
28633         return this;
28634     }
28635 });
28636
28637 // back compat
28638 Roo.BasicForm = Roo.form.BasicForm;/*
28639  * Based on:
28640  * Ext JS Library 1.1.1
28641  * Copyright(c) 2006-2007, Ext JS, LLC.
28642  *
28643  * Originally Released Under LGPL - original licence link has changed is not relivant.
28644  *
28645  * Fork - LGPL
28646  * <script type="text/javascript">
28647  */
28648
28649 /**
28650  * @class Roo.form.Form
28651  * @extends Roo.form.BasicForm
28652  * Adds the ability to dynamically render forms with JavaScript to {@link Roo.form.BasicForm}.
28653  * @constructor
28654  * @param {Object} config Configuration options
28655  */
28656 Roo.form.Form = function(config){
28657     var xitems =  [];
28658     if (config.items) {
28659         xitems = config.items;
28660         delete config.items;
28661     }
28662    
28663     
28664     Roo.form.Form.superclass.constructor.call(this, null, config);
28665     this.url = this.url || this.action;
28666     if(!this.root){
28667         this.root = new Roo.form.Layout(Roo.applyIf({
28668             id: Roo.id()
28669         }, config));
28670     }
28671     this.active = this.root;
28672     /**
28673      * Array of all the buttons that have been added to this form via {@link addButton}
28674      * @type Array
28675      */
28676     this.buttons = [];
28677     this.allItems = [];
28678     this.addEvents({
28679         /**
28680          * @event clientvalidation
28681          * If the monitorValid config option is true, this event fires repetitively to notify of valid state
28682          * @param {Form} this
28683          * @param {Boolean} valid true if the form has passed client-side validation
28684          */
28685         clientvalidation: true,
28686         /**
28687          * @event rendered
28688          * Fires when the form is rendered
28689          * @param {Roo.form.Form} form
28690          */
28691         rendered : true
28692     });
28693     
28694     if (this.progressUrl) {
28695             // push a hidden field onto the list of fields..
28696             this.addxtype( {
28697                     xns: Roo.form, 
28698                     xtype : 'Hidden', 
28699                     name : 'UPLOAD_IDENTIFIER' 
28700             });
28701         }
28702         
28703     
28704     Roo.each(xitems, this.addxtype, this);
28705     
28706     
28707     
28708 };
28709
28710 Roo.extend(Roo.form.Form, Roo.form.BasicForm, {
28711     /**
28712      * @cfg {Number} labelWidth The width of labels. This property cascades to child containers.
28713      */
28714     /**
28715      * @cfg {String} itemCls A css class to apply to the x-form-item of fields. This property cascades to child containers.
28716      */
28717     /**
28718      * @cfg {String} buttonAlign Valid values are "left," "center" and "right" (defaults to "center")
28719      */
28720     buttonAlign:'center',
28721
28722     /**
28723      * @cfg {Number} minButtonWidth Minimum width of all buttons in pixels (defaults to 75)
28724      */
28725     minButtonWidth:75,
28726
28727     /**
28728      * @cfg {String} labelAlign Valid values are "left," "top" and "right" (defaults to "left").
28729      * This property cascades to child containers if not set.
28730      */
28731     labelAlign:'left',
28732
28733     /**
28734      * @cfg {Boolean} monitorValid If true the form monitors its valid state <b>client-side</b> and
28735      * fires a looping event with that state. This is required to bind buttons to the valid
28736      * state using the config value formBind:true on the button.
28737      */
28738     monitorValid : false,
28739
28740     /**
28741      * @cfg {Number} monitorPoll The milliseconds to poll valid state, ignored if monitorValid is not true (defaults to 200)
28742      */
28743     monitorPoll : 200,
28744     
28745     /**
28746      * @cfg {String} progressUrl - Url to return progress data 
28747      */
28748     
28749     progressUrl : false,
28750   
28751     /**
28752      * Opens a new {@link Roo.form.Column} container in the layout stack. If fields are passed after the config, the
28753      * fields are added and the column is closed. If no fields are passed the column remains open
28754      * until end() is called.
28755      * @param {Object} config The config to pass to the column
28756      * @param {Field} field1 (optional)
28757      * @param {Field} field2 (optional)
28758      * @param {Field} etc (optional)
28759      * @return Column The column container object
28760      */
28761     column : function(c){
28762         var col = new Roo.form.Column(c);
28763         this.start(col);
28764         if(arguments.length > 1){ // duplicate code required because of Opera
28765             this.add.apply(this, Array.prototype.slice.call(arguments, 1));
28766             this.end();
28767         }
28768         return col;
28769     },
28770
28771     /**
28772      * Opens a new {@link Roo.form.FieldSet} container in the layout stack. If fields are passed after the config, the
28773      * fields are added and the fieldset is closed. If no fields are passed the fieldset remains open
28774      * until end() is called.
28775      * @param {Object} config The config to pass to the fieldset
28776      * @param {Field} field1 (optional)
28777      * @param {Field} field2 (optional)
28778      * @param {Field} etc (optional)
28779      * @return FieldSet The fieldset container object
28780      */
28781     fieldset : function(c){
28782         var fs = new Roo.form.FieldSet(c);
28783         this.start(fs);
28784         if(arguments.length > 1){ // duplicate code required because of Opera
28785             this.add.apply(this, Array.prototype.slice.call(arguments, 1));
28786             this.end();
28787         }
28788         return fs;
28789     },
28790
28791     /**
28792      * Opens a new {@link Roo.form.Layout} container in the layout stack. If fields are passed after the config, the
28793      * fields are added and the container is closed. If no fields are passed the container remains open
28794      * until end() is called.
28795      * @param {Object} config The config to pass to the Layout
28796      * @param {Field} field1 (optional)
28797      * @param {Field} field2 (optional)
28798      * @param {Field} etc (optional)
28799      * @return Layout The container object
28800      */
28801     container : function(c){
28802         var l = new Roo.form.Layout(c);
28803         this.start(l);
28804         if(arguments.length > 1){ // duplicate code required because of Opera
28805             this.add.apply(this, Array.prototype.slice.call(arguments, 1));
28806             this.end();
28807         }
28808         return l;
28809     },
28810
28811     /**
28812      * Opens the passed container in the layout stack. The container can be any {@link Roo.form.Layout} or subclass.
28813      * @param {Object} container A Roo.form.Layout or subclass of Layout
28814      * @return {Form} this
28815      */
28816     start : function(c){
28817         // cascade label info
28818         Roo.applyIf(c, {'labelAlign': this.active.labelAlign, 'labelWidth': this.active.labelWidth, 'itemCls': this.active.itemCls});
28819         this.active.stack.push(c);
28820         c.ownerCt = this.active;
28821         this.active = c;
28822         return this;
28823     },
28824
28825     /**
28826      * Closes the current open container
28827      * @return {Form} this
28828      */
28829     end : function(){
28830         if(this.active == this.root){
28831             return this;
28832         }
28833         this.active = this.active.ownerCt;
28834         return this;
28835     },
28836
28837     /**
28838      * Add Roo.form components to the current open container (e.g. column, fieldset, etc.).  Fields added via this method
28839      * can also be passed with an additional property of fieldLabel, which if supplied, will provide the text to display
28840      * as the label of the field.
28841      * @param {Field} field1
28842      * @param {Field} field2 (optional)
28843      * @param {Field} etc. (optional)
28844      * @return {Form} this
28845      */
28846     add : function(){
28847         this.active.stack.push.apply(this.active.stack, arguments);
28848         this.allItems.push.apply(this.allItems,arguments);
28849         var r = [];
28850         for(var i = 0, a = arguments, len = a.length; i < len; i++) {
28851             if(a[i].isFormField){
28852                 r.push(a[i]);
28853             }
28854         }
28855         if(r.length > 0){
28856             Roo.form.Form.superclass.add.apply(this, r);
28857         }
28858         return this;
28859     },
28860     
28861
28862     
28863     
28864     
28865      /**
28866      * Find any element that has been added to a form, using it's ID or name
28867      * This can include framesets, columns etc. along with regular fields..
28868      * @param {String} id - id or name to find.
28869      
28870      * @return {Element} e - or false if nothing found.
28871      */
28872     findbyId : function(id)
28873     {
28874         var ret = false;
28875         if (!id) {
28876             return ret;
28877         }
28878         Roo.each(this.allItems, function(f){
28879             if (f.id == id || f.name == id ){
28880                 ret = f;
28881                 return false;
28882             }
28883         });
28884         return ret;
28885     },
28886
28887     
28888     
28889     /**
28890      * Render this form into the passed container. This should only be called once!
28891      * @param {String/HTMLElement/Element} container The element this component should be rendered into
28892      * @return {Form} this
28893      */
28894     render : function(ct)
28895     {
28896         
28897         
28898         
28899         ct = Roo.get(ct);
28900         var o = this.autoCreate || {
28901             tag: 'form',
28902             method : this.method || 'POST',
28903             id : this.id || Roo.id()
28904         };
28905         this.initEl(ct.createChild(o));
28906
28907         this.root.render(this.el);
28908         
28909        
28910              
28911         this.items.each(function(f){
28912             f.render('x-form-el-'+f.id);
28913         });
28914
28915         if(this.buttons.length > 0){
28916             // tables are required to maintain order and for correct IE layout
28917             var tb = this.el.createChild({cls:'x-form-btns-ct', cn: {
28918                 cls:"x-form-btns x-form-btns-"+this.buttonAlign,
28919                 html:'<table cellspacing="0"><tbody><tr></tr></tbody></table><div class="x-clear"></div>'
28920             }}, null, true);
28921             var tr = tb.getElementsByTagName('tr')[0];
28922             for(var i = 0, len = this.buttons.length; i < len; i++) {
28923                 var b = this.buttons[i];
28924                 var td = document.createElement('td');
28925                 td.className = 'x-form-btn-td';
28926                 b.render(tr.appendChild(td));
28927             }
28928         }
28929         if(this.monitorValid){ // initialize after render
28930             this.startMonitoring();
28931         }
28932         this.fireEvent('rendered', this);
28933         return this;
28934     },
28935
28936     /**
28937      * Adds a button to the footer of the form - this <b>must</b> be called before the form is rendered.
28938      * @param {String/Object} config A string becomes the button text, an object can either be a Button config
28939      * object or a valid Roo.DomHelper element config
28940      * @param {Function} handler The function called when the button is clicked
28941      * @param {Object} scope (optional) The scope of the handler function
28942      * @return {Roo.Button}
28943      */
28944     addButton : function(config, handler, scope){
28945         var bc = {
28946             handler: handler,
28947             scope: scope,
28948             minWidth: this.minButtonWidth,
28949             hideParent:true
28950         };
28951         if(typeof config == "string"){
28952             bc.text = config;
28953         }else{
28954             Roo.apply(bc, config);
28955         }
28956         var btn = new Roo.Button(null, bc);
28957         this.buttons.push(btn);
28958         return btn;
28959     },
28960
28961      /**
28962      * Adds a series of form elements (using the xtype property as the factory method.
28963      * Valid xtypes are:  TextField, TextArea .... Button, Layout, FieldSet, Column, (and 'end' to close a block)
28964      * @param {Object} config 
28965      */
28966     
28967     addxtype : function()
28968     {
28969         var ar = Array.prototype.slice.call(arguments, 0);
28970         var ret = false;
28971         for(var i = 0; i < ar.length; i++) {
28972             if (!ar[i]) {
28973                 continue; // skip -- if this happends something invalid got sent, we 
28974                 // should ignore it, as basically that interface element will not show up
28975                 // and that should be pretty obvious!!
28976             }
28977             
28978             if (Roo.form[ar[i].xtype]) {
28979                 ar[i].form = this;
28980                 var fe = Roo.factory(ar[i], Roo.form);
28981                 if (!ret) {
28982                     ret = fe;
28983                 }
28984                 fe.form = this;
28985                 if (fe.store) {
28986                     fe.store.form = this;
28987                 }
28988                 if (fe.isLayout) {  
28989                          
28990                     this.start(fe);
28991                     this.allItems.push(fe);
28992                     if (fe.items && fe.addxtype) {
28993                         fe.addxtype.apply(fe, fe.items);
28994                         delete fe.items;
28995                     }
28996                      this.end();
28997                     continue;
28998                 }
28999                 
29000                 
29001                  
29002                 this.add(fe);
29003               //  console.log('adding ' + ar[i].xtype);
29004             }
29005             if (ar[i].xtype == 'Button') {  
29006                 //console.log('adding button');
29007                 //console.log(ar[i]);
29008                 this.addButton(ar[i]);
29009                 this.allItems.push(fe);
29010                 continue;
29011             }
29012             
29013             if (ar[i].xtype == 'end') { // so we can add fieldsets... / layout etc.
29014                 alert('end is not supported on xtype any more, use items');
29015             //    this.end();
29016             //    //console.log('adding end');
29017             }
29018             
29019         }
29020         return ret;
29021     },
29022     
29023     /**
29024      * Starts monitoring of the valid state of this form. Usually this is done by passing the config
29025      * option "monitorValid"
29026      */
29027     startMonitoring : function(){
29028         if(!this.bound){
29029             this.bound = true;
29030             Roo.TaskMgr.start({
29031                 run : this.bindHandler,
29032                 interval : this.monitorPoll || 200,
29033                 scope: this
29034             });
29035         }
29036     },
29037
29038     /**
29039      * Stops monitoring of the valid state of this form
29040      */
29041     stopMonitoring : function(){
29042         this.bound = false;
29043     },
29044
29045     // private
29046     bindHandler : function(){
29047         if(!this.bound){
29048             return false; // stops binding
29049         }
29050         var valid = true;
29051         this.items.each(function(f){
29052             if(!f.isValid(true)){
29053                 valid = false;
29054                 return false;
29055             }
29056         });
29057         for(var i = 0, len = this.buttons.length; i < len; i++){
29058             var btn = this.buttons[i];
29059             if(btn.formBind === true && btn.disabled === valid){
29060                 btn.setDisabled(!valid);
29061             }
29062         }
29063         this.fireEvent('clientvalidation', this, valid);
29064     }
29065     
29066     
29067     
29068     
29069     
29070     
29071     
29072     
29073 });
29074
29075
29076 // back compat
29077 Roo.Form = Roo.form.Form;
29078 /*
29079  * Based on:
29080  * Ext JS Library 1.1.1
29081  * Copyright(c) 2006-2007, Ext JS, LLC.
29082  *
29083  * Originally Released Under LGPL - original licence link has changed is not relivant.
29084  *
29085  * Fork - LGPL
29086  * <script type="text/javascript">
29087  */
29088  
29089  /**
29090  * @class Roo.form.Action
29091  * Internal Class used to handle form actions
29092  * @constructor
29093  * @param {Roo.form.BasicForm} el The form element or its id
29094  * @param {Object} config Configuration options
29095  */
29096  
29097  
29098 // define the action interface
29099 Roo.form.Action = function(form, options){
29100     this.form = form;
29101     this.options = options || {};
29102 };
29103 /**
29104  * Client Validation Failed
29105  * @const 
29106  */
29107 Roo.form.Action.CLIENT_INVALID = 'client';
29108 /**
29109  * Server Validation Failed
29110  * @const 
29111  */
29112  Roo.form.Action.SERVER_INVALID = 'server';
29113  /**
29114  * Connect to Server Failed
29115  * @const 
29116  */
29117 Roo.form.Action.CONNECT_FAILURE = 'connect';
29118 /**
29119  * Reading Data from Server Failed
29120  * @const 
29121  */
29122 Roo.form.Action.LOAD_FAILURE = 'load';
29123
29124 Roo.form.Action.prototype = {
29125     type : 'default',
29126     failureType : undefined,
29127     response : undefined,
29128     result : undefined,
29129
29130     // interface method
29131     run : function(options){
29132
29133     },
29134
29135     // interface method
29136     success : function(response){
29137
29138     },
29139
29140     // interface method
29141     handleResponse : function(response){
29142
29143     },
29144
29145     // default connection failure
29146     failure : function(response){
29147         
29148         this.response = response;
29149         this.failureType = Roo.form.Action.CONNECT_FAILURE;
29150         this.form.afterAction(this, false);
29151     },
29152
29153     processResponse : function(response){
29154         this.response = response;
29155         if(!response.responseText){
29156             return true;
29157         }
29158         this.result = this.handleResponse(response);
29159         return this.result;
29160     },
29161
29162     // utility functions used internally
29163     getUrl : function(appendParams){
29164         var url = this.options.url || this.form.url || this.form.el.dom.action;
29165         if(appendParams){
29166             var p = this.getParams();
29167             if(p){
29168                 url += (url.indexOf('?') != -1 ? '&' : '?') + p;
29169             }
29170         }
29171         return url;
29172     },
29173
29174     getMethod : function(){
29175         return (this.options.method || this.form.method || this.form.el.dom.method || 'POST').toUpperCase();
29176     },
29177
29178     getParams : function(){
29179         var bp = this.form.baseParams;
29180         var p = this.options.params;
29181         if(p){
29182             if(typeof p == "object"){
29183                 p = Roo.urlEncode(Roo.applyIf(p, bp));
29184             }else if(typeof p == 'string' && bp){
29185                 p += '&' + Roo.urlEncode(bp);
29186             }
29187         }else if(bp){
29188             p = Roo.urlEncode(bp);
29189         }
29190         return p;
29191     },
29192
29193     createCallback : function(){
29194         return {
29195             success: this.success,
29196             failure: this.failure,
29197             scope: this,
29198             timeout: (this.form.timeout*1000),
29199             upload: this.form.fileUpload ? this.success : undefined
29200         };
29201     }
29202 };
29203
29204 Roo.form.Action.Submit = function(form, options){
29205     Roo.form.Action.Submit.superclass.constructor.call(this, form, options);
29206 };
29207
29208 Roo.extend(Roo.form.Action.Submit, Roo.form.Action, {
29209     type : 'submit',
29210
29211     haveProgress : false,
29212     uploadComplete : false,
29213     
29214     // uploadProgress indicator.
29215     uploadProgress : function()
29216     {
29217         if (!this.form.progressUrl) {
29218             return;
29219         }
29220         
29221         if (!this.haveProgress) {
29222             Roo.MessageBox.progress("Uploading", "Uploading");
29223         }
29224         if (this.uploadComplete) {
29225            Roo.MessageBox.hide();
29226            return;
29227         }
29228         
29229         this.haveProgress = true;
29230    
29231         var uid = this.form.findField('UPLOAD_IDENTIFIER').getValue();
29232         
29233         var c = new Roo.data.Connection();
29234         c.request({
29235             url : this.form.progressUrl,
29236             params: {
29237                 id : uid
29238             },
29239             method: 'GET',
29240             success : function(req){
29241                //console.log(data);
29242                 var rdata = false;
29243                 var edata;
29244                 try  {
29245                    rdata = Roo.decode(req.responseText)
29246                 } catch (e) {
29247                     Roo.log("Invalid data from server..");
29248                     Roo.log(edata);
29249                     return;
29250                 }
29251                 if (!rdata || !rdata.success) {
29252                     Roo.log(rdata);
29253                     Roo.MessageBox.alert(Roo.encode(rdata));
29254                     return;
29255                 }
29256                 var data = rdata.data;
29257                 
29258                 if (this.uploadComplete) {
29259                    Roo.MessageBox.hide();
29260                    return;
29261                 }
29262                    
29263                 if (data){
29264                     Roo.MessageBox.updateProgress(data.bytes_uploaded/data.bytes_total,
29265                        Math.floor((data.bytes_total - data.bytes_uploaded)/1000) + 'k remaining'
29266                     );
29267                 }
29268                 this.uploadProgress.defer(2000,this);
29269             },
29270        
29271             failure: function(data) {
29272                 Roo.log('progress url failed ');
29273                 Roo.log(data);
29274             },
29275             scope : this
29276         });
29277            
29278     },
29279     
29280     
29281     run : function()
29282     {
29283         // run get Values on the form, so it syncs any secondary forms.
29284         this.form.getValues();
29285         
29286         var o = this.options;
29287         var method = this.getMethod();
29288         var isPost = method == 'POST';
29289         if(o.clientValidation === false || this.form.isValid()){
29290             
29291             if (this.form.progressUrl) {
29292                 this.form.findField('UPLOAD_IDENTIFIER').setValue(
29293                     (new Date() * 1) + '' + Math.random());
29294                     
29295             } 
29296             
29297             
29298             Roo.Ajax.request(Roo.apply(this.createCallback(), {
29299                 form:this.form.el.dom,
29300                 url:this.getUrl(!isPost),
29301                 method: method,
29302                 params:isPost ? this.getParams() : null,
29303                 isUpload: this.form.fileUpload
29304             }));
29305             
29306             this.uploadProgress();
29307
29308         }else if (o.clientValidation !== false){ // client validation failed
29309             this.failureType = Roo.form.Action.CLIENT_INVALID;
29310             this.form.afterAction(this, false);
29311         }
29312     },
29313
29314     success : function(response)
29315     {
29316         this.uploadComplete= true;
29317         if (this.haveProgress) {
29318             Roo.MessageBox.hide();
29319         }
29320         
29321         
29322         var result = this.processResponse(response);
29323         if(result === true || result.success){
29324             this.form.afterAction(this, true);
29325             return;
29326         }
29327         if(result.errors){
29328             this.form.markInvalid(result.errors);
29329             this.failureType = Roo.form.Action.SERVER_INVALID;
29330         }
29331         this.form.afterAction(this, false);
29332     },
29333     failure : function(response)
29334     {
29335         this.uploadComplete= true;
29336         if (this.haveProgress) {
29337             Roo.MessageBox.hide();
29338         }
29339         
29340         this.response = response;
29341         this.failureType = Roo.form.Action.CONNECT_FAILURE;
29342         this.form.afterAction(this, false);
29343     },
29344     
29345     handleResponse : function(response){
29346         if(this.form.errorReader){
29347             var rs = this.form.errorReader.read(response);
29348             var errors = [];
29349             if(rs.records){
29350                 for(var i = 0, len = rs.records.length; i < len; i++) {
29351                     var r = rs.records[i];
29352                     errors[i] = r.data;
29353                 }
29354             }
29355             if(errors.length < 1){
29356                 errors = null;
29357             }
29358             return {
29359                 success : rs.success,
29360                 errors : errors
29361             };
29362         }
29363         var ret = false;
29364         try {
29365             ret = Roo.decode(response.responseText);
29366         } catch (e) {
29367             ret = {
29368                 success: false,
29369                 errorMsg: "Failed to read server message: " + (response ? response.responseText : ' - no message'),
29370                 errors : []
29371             };
29372         }
29373         return ret;
29374         
29375     }
29376 });
29377
29378
29379 Roo.form.Action.Load = function(form, options){
29380     Roo.form.Action.Load.superclass.constructor.call(this, form, options);
29381     this.reader = this.form.reader;
29382 };
29383
29384 Roo.extend(Roo.form.Action.Load, Roo.form.Action, {
29385     type : 'load',
29386
29387     run : function(){
29388         
29389         Roo.Ajax.request(Roo.apply(
29390                 this.createCallback(), {
29391                     method:this.getMethod(),
29392                     url:this.getUrl(false),
29393                     params:this.getParams()
29394         }));
29395     },
29396
29397     success : function(response){
29398         
29399         var result = this.processResponse(response);
29400         if(result === true || !result.success || !result.data){
29401             this.failureType = Roo.form.Action.LOAD_FAILURE;
29402             this.form.afterAction(this, false);
29403             return;
29404         }
29405         this.form.clearInvalid();
29406         this.form.setValues(result.data);
29407         this.form.afterAction(this, true);
29408     },
29409
29410     handleResponse : function(response){
29411         if(this.form.reader){
29412             var rs = this.form.reader.read(response);
29413             var data = rs.records && rs.records[0] ? rs.records[0].data : null;
29414             return {
29415                 success : rs.success,
29416                 data : data
29417             };
29418         }
29419         return Roo.decode(response.responseText);
29420     }
29421 });
29422
29423 Roo.form.Action.ACTION_TYPES = {
29424     'load' : Roo.form.Action.Load,
29425     'submit' : Roo.form.Action.Submit
29426 };/*
29427  * Based on:
29428  * Ext JS Library 1.1.1
29429  * Copyright(c) 2006-2007, Ext JS, LLC.
29430  *
29431  * Originally Released Under LGPL - original licence link has changed is not relivant.
29432  *
29433  * Fork - LGPL
29434  * <script type="text/javascript">
29435  */
29436  
29437 /**
29438  * @class Roo.form.Layout
29439  * @extends Roo.Component
29440  * Creates a container for layout and rendering of fields in an {@link Roo.form.Form}.
29441  * @constructor
29442  * @param {Object} config Configuration options
29443  */
29444 Roo.form.Layout = function(config){
29445     var xitems = [];
29446     if (config.items) {
29447         xitems = config.items;
29448         delete config.items;
29449     }
29450     Roo.form.Layout.superclass.constructor.call(this, config);
29451     this.stack = [];
29452     Roo.each(xitems, this.addxtype, this);
29453      
29454 };
29455
29456 Roo.extend(Roo.form.Layout, Roo.Component, {
29457     /**
29458      * @cfg {String/Object} autoCreate
29459      * A DomHelper element spec used to autocreate the layout (defaults to {tag: 'div', cls: 'x-form-ct'})
29460      */
29461     /**
29462      * @cfg {String/Object/Function} style
29463      * A style specification string, e.g. "width:100px", or object in the form {width:"100px"}, or
29464      * a function which returns such a specification.
29465      */
29466     /**
29467      * @cfg {String} labelAlign
29468      * Valid values are "left," "top" and "right" (defaults to "left")
29469      */
29470     /**
29471      * @cfg {Number} labelWidth
29472      * Fixed width in pixels of all field labels (defaults to undefined)
29473      */
29474     /**
29475      * @cfg {Boolean} clear
29476      * True to add a clearing element at the end of this layout, equivalent to CSS clear: both (defaults to true)
29477      */
29478     clear : true,
29479     /**
29480      * @cfg {String} labelSeparator
29481      * The separator to use after field labels (defaults to ':')
29482      */
29483     labelSeparator : ':',
29484     /**
29485      * @cfg {Boolean} hideLabels
29486      * True to suppress the display of field labels in this layout (defaults to false)
29487      */
29488     hideLabels : false,
29489
29490     // private
29491     defaultAutoCreate : {tag: 'div', cls: 'x-form-ct'},
29492     
29493     isLayout : true,
29494     
29495     // private
29496     onRender : function(ct, position){
29497         if(this.el){ // from markup
29498             this.el = Roo.get(this.el);
29499         }else {  // generate
29500             var cfg = this.getAutoCreate();
29501             this.el = ct.createChild(cfg, position);
29502         }
29503         if(this.style){
29504             this.el.applyStyles(this.style);
29505         }
29506         if(this.labelAlign){
29507             this.el.addClass('x-form-label-'+this.labelAlign);
29508         }
29509         if(this.hideLabels){
29510             this.labelStyle = "display:none";
29511             this.elementStyle = "padding-left:0;";
29512         }else{
29513             if(typeof this.labelWidth == 'number'){
29514                 this.labelStyle = "width:"+this.labelWidth+"px;";
29515                 this.elementStyle = "padding-left:"+((this.labelWidth+(typeof this.labelPad == 'number' ? this.labelPad : 5))+'px')+";";
29516             }
29517             if(this.labelAlign == 'top'){
29518                 this.labelStyle = "width:auto;";
29519                 this.elementStyle = "padding-left:0;";
29520             }
29521         }
29522         var stack = this.stack;
29523         var slen = stack.length;
29524         if(slen > 0){
29525             if(!this.fieldTpl){
29526                 var t = new Roo.Template(
29527                     '<div class="x-form-item {5}">',
29528                         '<label for="{0}" style="{2}">{1}{4}</label>',
29529                         '<div class="x-form-element" id="x-form-el-{0}" style="{3}">',
29530                         '</div>',
29531                     '</div><div class="x-form-clear-left"></div>'
29532                 );
29533                 t.disableFormats = true;
29534                 t.compile();
29535                 Roo.form.Layout.prototype.fieldTpl = t;
29536             }
29537             for(var i = 0; i < slen; i++) {
29538                 if(stack[i].isFormField){
29539                     this.renderField(stack[i]);
29540                 }else{
29541                     this.renderComponent(stack[i]);
29542                 }
29543             }
29544         }
29545         if(this.clear){
29546             this.el.createChild({cls:'x-form-clear'});
29547         }
29548     },
29549
29550     // private
29551     renderField : function(f){
29552         f.fieldEl = Roo.get(this.fieldTpl.append(this.el, [
29553                f.id, //0
29554                f.fieldLabel, //1
29555                f.labelStyle||this.labelStyle||'', //2
29556                this.elementStyle||'', //3
29557                typeof f.labelSeparator == 'undefined' ? this.labelSeparator : f.labelSeparator, //4
29558                f.itemCls||this.itemCls||''  //5
29559        ], true).getPrevSibling());
29560     },
29561
29562     // private
29563     renderComponent : function(c){
29564         c.render(c.isLayout ? this.el : this.el.createChild());    
29565     },
29566     /**
29567      * Adds a object form elements (using the xtype property as the factory method.)
29568      * Valid xtypes are:  TextField, TextArea .... Button, Layout, FieldSet, Column
29569      * @param {Object} config 
29570      */
29571     addxtype : function(o)
29572     {
29573         // create the lement.
29574         o.form = this.form;
29575         var fe = Roo.factory(o, Roo.form);
29576         this.form.allItems.push(fe);
29577         this.stack.push(fe);
29578         
29579         if (fe.isFormField) {
29580             this.form.items.add(fe);
29581         }
29582          
29583         return fe;
29584     }
29585 });
29586
29587 /**
29588  * @class Roo.form.Column
29589  * @extends Roo.form.Layout
29590  * Creates a column container for layout and rendering of fields in an {@link Roo.form.Form}.
29591  * @constructor
29592  * @param {Object} config Configuration options
29593  */
29594 Roo.form.Column = function(config){
29595     Roo.form.Column.superclass.constructor.call(this, config);
29596 };
29597
29598 Roo.extend(Roo.form.Column, Roo.form.Layout, {
29599     /**
29600      * @cfg {Number/String} width
29601      * The fixed width of the column in pixels or CSS value (defaults to "auto")
29602      */
29603     /**
29604      * @cfg {String/Object} autoCreate
29605      * A DomHelper element spec used to autocreate the column (defaults to {tag: 'div', cls: 'x-form-ct x-form-column'})
29606      */
29607
29608     // private
29609     defaultAutoCreate : {tag: 'div', cls: 'x-form-ct x-form-column'},
29610
29611     // private
29612     onRender : function(ct, position){
29613         Roo.form.Column.superclass.onRender.call(this, ct, position);
29614         if(this.width){
29615             this.el.setWidth(this.width);
29616         }
29617     }
29618 });
29619
29620
29621 /**
29622  * @class Roo.form.Row
29623  * @extends Roo.form.Layout
29624  * Creates a row container for layout and rendering of fields in an {@link Roo.form.Form}.
29625  * @constructor
29626  * @param {Object} config Configuration options
29627  */
29628
29629  
29630 Roo.form.Row = function(config){
29631     Roo.form.Row.superclass.constructor.call(this, config);
29632 };
29633  
29634 Roo.extend(Roo.form.Row, Roo.form.Layout, {
29635       /**
29636      * @cfg {Number/String} width
29637      * The fixed width of the column in pixels or CSS value (defaults to "auto")
29638      */
29639     /**
29640      * @cfg {Number/String} height
29641      * The fixed height of the column in pixels or CSS value (defaults to "auto")
29642      */
29643     defaultAutoCreate : {tag: 'div', cls: 'x-form-ct x-form-row'},
29644     
29645     padWidth : 20,
29646     // private
29647     onRender : function(ct, position){
29648         //console.log('row render');
29649         if(!this.rowTpl){
29650             var t = new Roo.Template(
29651                 '<div class="x-form-item {5}" style="float:left;width:{6}px">',
29652                     '<label for="{0}" style="{2}">{1}{4}</label>',
29653                     '<div class="x-form-element" id="x-form-el-{0}" style="{3}">',
29654                     '</div>',
29655                 '</div>'
29656             );
29657             t.disableFormats = true;
29658             t.compile();
29659             Roo.form.Layout.prototype.rowTpl = t;
29660         }
29661         this.fieldTpl = this.rowTpl;
29662         
29663         //console.log('lw' + this.labelWidth +', la:' + this.labelAlign);
29664         var labelWidth = 100;
29665         
29666         if ((this.labelAlign != 'top')) {
29667             if (typeof this.labelWidth == 'number') {
29668                 labelWidth = this.labelWidth
29669             }
29670             this.padWidth =  20 + labelWidth;
29671             
29672         }
29673         
29674         Roo.form.Column.superclass.onRender.call(this, ct, position);
29675         if(this.width){
29676             this.el.setWidth(this.width);
29677         }
29678         if(this.height){
29679             this.el.setHeight(this.height);
29680         }
29681     },
29682     
29683     // private
29684     renderField : function(f){
29685         f.fieldEl = this.fieldTpl.append(this.el, [
29686                f.id, f.fieldLabel,
29687                f.labelStyle||this.labelStyle||'',
29688                this.elementStyle||'',
29689                typeof f.labelSeparator == 'undefined' ? this.labelSeparator : f.labelSeparator,
29690                f.itemCls||this.itemCls||'',
29691                f.width ? f.width + this.padWidth : 160 + this.padWidth
29692        ],true);
29693     }
29694 });
29695  
29696
29697 /**
29698  * @class Roo.form.FieldSet
29699  * @extends Roo.form.Layout
29700  * Creates a fieldset container for layout and rendering of fields in an {@link Roo.form.Form}.
29701  * @constructor
29702  * @param {Object} config Configuration options
29703  */
29704 Roo.form.FieldSet = function(config){
29705     Roo.form.FieldSet.superclass.constructor.call(this, config);
29706 };
29707
29708 Roo.extend(Roo.form.FieldSet, Roo.form.Layout, {
29709     /**
29710      * @cfg {String} legend
29711      * The text to display as the legend for the FieldSet (defaults to '')
29712      */
29713     /**
29714      * @cfg {String/Object} autoCreate
29715      * A DomHelper element spec used to autocreate the fieldset (defaults to {tag: 'fieldset', cn: {tag:'legend'}})
29716      */
29717
29718     // private
29719     defaultAutoCreate : {tag: 'fieldset', cn: {tag:'legend'}},
29720
29721     // private
29722     onRender : function(ct, position){
29723         Roo.form.FieldSet.superclass.onRender.call(this, ct, position);
29724         if(this.legend){
29725             this.setLegend(this.legend);
29726         }
29727     },
29728
29729     // private
29730     setLegend : function(text){
29731         if(this.rendered){
29732             this.el.child('legend').update(text);
29733         }
29734     }
29735 });/*
29736  * Based on:
29737  * Ext JS Library 1.1.1
29738  * Copyright(c) 2006-2007, Ext JS, LLC.
29739  *
29740  * Originally Released Under LGPL - original licence link has changed is not relivant.
29741  *
29742  * Fork - LGPL
29743  * <script type="text/javascript">
29744  */
29745 /**
29746  * @class Roo.form.VTypes
29747  * Overridable validation definitions. The validations provided are basic and intended to be easily customizable and extended.
29748  * @singleton
29749  */
29750 Roo.form.VTypes = function(){
29751     // closure these in so they are only created once.
29752     var alpha = /^[a-zA-Z_]+$/;
29753     var alphanum = /^[a-zA-Z0-9_]+$/;
29754     var email = /^([\w]+)(.[\w]+)*@([\w-]+\.){1,5}([A-Za-z]){2,4}$/;
29755     var url = /(((https?)|(ftp)):\/\/([\-\w]+\.)+\w{2,3}(\/[%\-\w]+(\.\w{2,})?)*(([\w\-\.\?\\\/+@&#;`~=%!]*)(\.\w{2,})?)*\/?)/i;
29756
29757     // All these messages and functions are configurable
29758     return {
29759         /**
29760          * The function used to validate email addresses
29761          * @param {String} value The email address
29762          */
29763         'email' : function(v){
29764             return email.test(v);
29765         },
29766         /**
29767          * The error text to display when the email validation function returns false
29768          * @type String
29769          */
29770         'emailText' : 'This field should be an e-mail address in the format "user@domain.com"',
29771         /**
29772          * The keystroke filter mask to be applied on email input
29773          * @type RegExp
29774          */
29775         'emailMask' : /[a-z0-9_\.\-@]/i,
29776
29777         /**
29778          * The function used to validate URLs
29779          * @param {String} value The URL
29780          */
29781         'url' : function(v){
29782             return url.test(v);
29783         },
29784         /**
29785          * The error text to display when the url validation function returns false
29786          * @type String
29787          */
29788         'urlText' : 'This field should be a URL in the format "http:/'+'/www.domain.com"',
29789         
29790         /**
29791          * The function used to validate alpha values
29792          * @param {String} value The value
29793          */
29794         'alpha' : function(v){
29795             return alpha.test(v);
29796         },
29797         /**
29798          * The error text to display when the alpha validation function returns false
29799          * @type String
29800          */
29801         'alphaText' : 'This field should only contain letters and _',
29802         /**
29803          * The keystroke filter mask to be applied on alpha input
29804          * @type RegExp
29805          */
29806         'alphaMask' : /[a-z_]/i,
29807
29808         /**
29809          * The function used to validate alphanumeric values
29810          * @param {String} value The value
29811          */
29812         'alphanum' : function(v){
29813             return alphanum.test(v);
29814         },
29815         /**
29816          * The error text to display when the alphanumeric validation function returns false
29817          * @type String
29818          */
29819         'alphanumText' : 'This field should only contain letters, numbers and _',
29820         /**
29821          * The keystroke filter mask to be applied on alphanumeric input
29822          * @type RegExp
29823          */
29824         'alphanumMask' : /[a-z0-9_]/i
29825     };
29826 }();//<script type="text/javascript">
29827
29828 /**
29829  * @class Roo.form.FCKeditor
29830  * @extends Roo.form.TextArea
29831  * Wrapper around the FCKEditor http://www.fckeditor.net
29832  * @constructor
29833  * Creates a new FCKeditor
29834  * @param {Object} config Configuration options
29835  */
29836 Roo.form.FCKeditor = function(config){
29837     Roo.form.FCKeditor.superclass.constructor.call(this, config);
29838     this.addEvents({
29839          /**
29840          * @event editorinit
29841          * Fired when the editor is initialized - you can add extra handlers here..
29842          * @param {FCKeditor} this
29843          * @param {Object} the FCK object.
29844          */
29845         editorinit : true
29846     });
29847     
29848     
29849 };
29850 Roo.form.FCKeditor.editors = { };
29851 Roo.extend(Roo.form.FCKeditor, Roo.form.TextArea,
29852 {
29853     //defaultAutoCreate : {
29854     //    tag : "textarea",style   : "width:100px;height:60px;" ,autocomplete    : "off"
29855     //},
29856     // private
29857     /**
29858      * @cfg {Object} fck options - see fck manual for details.
29859      */
29860     fckconfig : false,
29861     
29862     /**
29863      * @cfg {Object} fck toolbar set (Basic or Default)
29864      */
29865     toolbarSet : 'Basic',
29866     /**
29867      * @cfg {Object} fck BasePath
29868      */ 
29869     basePath : '/fckeditor/',
29870     
29871     
29872     frame : false,
29873     
29874     value : '',
29875     
29876    
29877     onRender : function(ct, position)
29878     {
29879         if(!this.el){
29880             this.defaultAutoCreate = {
29881                 tag: "textarea",
29882                 style:"width:300px;height:60px;",
29883                 autocomplete: "off"
29884             };
29885         }
29886         Roo.form.FCKeditor.superclass.onRender.call(this, ct, position);
29887         /*
29888         if(this.grow){
29889             this.textSizeEl = Roo.DomHelper.append(document.body, {tag: "pre", cls: "x-form-grow-sizer"});
29890             if(this.preventScrollbars){
29891                 this.el.setStyle("overflow", "hidden");
29892             }
29893             this.el.setHeight(this.growMin);
29894         }
29895         */
29896         //console.log('onrender' + this.getId() );
29897         Roo.form.FCKeditor.editors[this.getId()] = this;
29898          
29899
29900         this.replaceTextarea() ;
29901         
29902     },
29903     
29904     getEditor : function() {
29905         return this.fckEditor;
29906     },
29907     /**
29908      * Sets a data value into the field and validates it.  To set the value directly without validation see {@link #setRawValue}.
29909      * @param {Mixed} value The value to set
29910      */
29911     
29912     
29913     setValue : function(value)
29914     {
29915         //console.log('setValue: ' + value);
29916         
29917         if(typeof(value) == 'undefined') { // not sure why this is happending...
29918             return;
29919         }
29920         Roo.form.FCKeditor.superclass.setValue.apply(this,[value]);
29921         
29922         //if(!this.el || !this.getEditor()) {
29923         //    this.value = value;
29924             //this.setValue.defer(100,this,[value]);    
29925         //    return;
29926         //} 
29927         
29928         if(!this.getEditor()) {
29929             return;
29930         }
29931         
29932         this.getEditor().SetData(value);
29933         
29934         //
29935
29936     },
29937
29938     /**
29939      * Returns the normalized data value (undefined or emptyText will be returned as '').  To return the raw value see {@link #getRawValue}.
29940      * @return {Mixed} value The field value
29941      */
29942     getValue : function()
29943     {
29944         
29945         if (this.frame && this.frame.dom.style.display == 'none') {
29946             return Roo.form.FCKeditor.superclass.getValue.call(this);
29947         }
29948         
29949         if(!this.el || !this.getEditor()) {
29950            
29951            // this.getValue.defer(100,this); 
29952             return this.value;
29953         }
29954        
29955         
29956         var value=this.getEditor().GetData();
29957         Roo.form.FCKeditor.superclass.setValue.apply(this,[value]);
29958         return Roo.form.FCKeditor.superclass.getValue.call(this);
29959         
29960
29961     },
29962
29963     /**
29964      * Returns the raw data value which may or may not be a valid, defined value.  To return a normalized value see {@link #getValue}.
29965      * @return {Mixed} value The field value
29966      */
29967     getRawValue : function()
29968     {
29969         if (this.frame && this.frame.dom.style.display == 'none') {
29970             return Roo.form.FCKeditor.superclass.getRawValue.call(this);
29971         }
29972         
29973         if(!this.el || !this.getEditor()) {
29974             //this.getRawValue.defer(100,this); 
29975             return this.value;
29976             return;
29977         }
29978         
29979         
29980         
29981         var value=this.getEditor().GetData();
29982         Roo.form.FCKeditor.superclass.setRawValue.apply(this,[value]);
29983         return Roo.form.FCKeditor.superclass.getRawValue.call(this);
29984          
29985     },
29986     
29987     setSize : function(w,h) {
29988         
29989         
29990         
29991         //if (this.frame && this.frame.dom.style.display == 'none') {
29992         //    Roo.form.FCKeditor.superclass.setSize.apply(this, [w, h]);
29993         //    return;
29994         //}
29995         //if(!this.el || !this.getEditor()) {
29996         //    this.setSize.defer(100,this, [w,h]); 
29997         //    return;
29998         //}
29999         
30000         
30001         
30002         Roo.form.FCKeditor.superclass.setSize.apply(this, [w, h]);
30003         
30004         this.frame.dom.setAttribute('width', w);
30005         this.frame.dom.setAttribute('height', h);
30006         this.frame.setSize(w,h);
30007         
30008     },
30009     
30010     toggleSourceEdit : function(value) {
30011         
30012       
30013          
30014         this.el.dom.style.display = value ? '' : 'none';
30015         this.frame.dom.style.display = value ?  'none' : '';
30016         
30017     },
30018     
30019     
30020     focus: function(tag)
30021     {
30022         if (this.frame.dom.style.display == 'none') {
30023             return Roo.form.FCKeditor.superclass.focus.call(this);
30024         }
30025         if(!this.el || !this.getEditor()) {
30026             this.focus.defer(100,this, [tag]); 
30027             return;
30028         }
30029         
30030         
30031         
30032         
30033         var tgs = this.getEditor().EditorDocument.getElementsByTagName(tag);
30034         this.getEditor().Focus();
30035         if (tgs.length) {
30036             if (!this.getEditor().Selection.GetSelection()) {
30037                 this.focus.defer(100,this, [tag]); 
30038                 return;
30039             }
30040             
30041             
30042             var r = this.getEditor().EditorDocument.createRange();
30043             r.setStart(tgs[0],0);
30044             r.setEnd(tgs[0],0);
30045             this.getEditor().Selection.GetSelection().removeAllRanges();
30046             this.getEditor().Selection.GetSelection().addRange(r);
30047             this.getEditor().Focus();
30048         }
30049         
30050     },
30051     
30052     
30053     
30054     replaceTextarea : function()
30055     {
30056         if ( document.getElementById( this.getId() + '___Frame' ) )
30057             return ;
30058         //if ( !this.checkBrowser || this._isCompatibleBrowser() )
30059         //{
30060             // We must check the elements firstly using the Id and then the name.
30061         var oTextarea = document.getElementById( this.getId() );
30062         
30063         var colElementsByName = document.getElementsByName( this.getId() ) ;
30064          
30065         oTextarea.style.display = 'none' ;
30066
30067         if ( oTextarea.tabIndex ) {            
30068             this.TabIndex = oTextarea.tabIndex ;
30069         }
30070         
30071         this._insertHtmlBefore( this._getConfigHtml(), oTextarea ) ;
30072         this._insertHtmlBefore( this._getIFrameHtml(), oTextarea ) ;
30073         this.frame = Roo.get(this.getId() + '___Frame')
30074     },
30075     
30076     _getConfigHtml : function()
30077     {
30078         var sConfig = '' ;
30079
30080         for ( var o in this.fckconfig ) {
30081             sConfig += sConfig.length > 0  ? '&amp;' : '';
30082             sConfig += encodeURIComponent( o ) + '=' + encodeURIComponent( this.fckconfig[o] ) ;
30083         }
30084
30085         return '<input type="hidden" id="' + this.getId() + '___Config" value="' + sConfig + '" style="display:none" />' ;
30086     },
30087     
30088     
30089     _getIFrameHtml : function()
30090     {
30091         var sFile = 'fckeditor.html' ;
30092         /* no idea what this is about..
30093         try
30094         {
30095             if ( (/fcksource=true/i).test( window.top.location.search ) )
30096                 sFile = 'fckeditor.original.html' ;
30097         }
30098         catch (e) { 
30099         */
30100
30101         var sLink = this.basePath + 'editor/' + sFile + '?InstanceName=' + encodeURIComponent( this.getId() ) ;
30102         sLink += this.toolbarSet ? ( '&amp;Toolbar=' + this.toolbarSet)  : '';
30103         
30104         
30105         var html = '<iframe id="' + this.getId() +
30106             '___Frame" src="' + sLink +
30107             '" width="' + this.width +
30108             '" height="' + this.height + '"' +
30109             (this.tabIndex ?  ' tabindex="' + this.tabIndex + '"' :'' ) +
30110             ' frameborder="0" scrolling="no"></iframe>' ;
30111
30112         return html ;
30113     },
30114     
30115     _insertHtmlBefore : function( html, element )
30116     {
30117         if ( element.insertAdjacentHTML )       {
30118             // IE
30119             element.insertAdjacentHTML( 'beforeBegin', html ) ;
30120         } else { // Gecko
30121             var oRange = document.createRange() ;
30122             oRange.setStartBefore( element ) ;
30123             var oFragment = oRange.createContextualFragment( html );
30124             element.parentNode.insertBefore( oFragment, element ) ;
30125         }
30126     }
30127     
30128     
30129   
30130     
30131     
30132     
30133     
30134
30135 });
30136
30137 //Roo.reg('fckeditor', Roo.form.FCKeditor);
30138
30139 function FCKeditor_OnComplete(editorInstance){
30140     var f = Roo.form.FCKeditor.editors[editorInstance.Name];
30141     f.fckEditor = editorInstance;
30142     //console.log("loaded");
30143     f.fireEvent('editorinit', f, editorInstance);
30144
30145   
30146
30147  
30148
30149
30150
30151
30152
30153
30154
30155
30156
30157
30158
30159
30160
30161
30162
30163 //<script type="text/javascript">
30164 /**
30165  * @class Roo.form.GridField
30166  * @extends Roo.form.Field
30167  * Embed a grid (or editable grid into a form)
30168  * STATUS ALPHA
30169  * 
30170  * This embeds a grid in a form, the value of the field should be the json encoded array of rows
30171  * it needs 
30172  * xgrid.store = Roo.data.Store
30173  * xgrid.store.proxy = Roo.data.MemoryProxy (data = [] )
30174  * xgrid.store.reader = Roo.data.JsonReader 
30175  * 
30176  * 
30177  * @constructor
30178  * Creates a new GridField
30179  * @param {Object} config Configuration options
30180  */
30181 Roo.form.GridField = function(config){
30182     Roo.form.GridField.superclass.constructor.call(this, config);
30183      
30184 };
30185
30186 Roo.extend(Roo.form.GridField, Roo.form.Field,  {
30187     /**
30188      * @cfg {Number} width  - used to restrict width of grid..
30189      */
30190     width : 100,
30191     /**
30192      * @cfg {Number} height - used to restrict height of grid..
30193      */
30194     height : 50,
30195      /**
30196      * @cfg {Object} xgrid (xtype'd description of grid) { xtype : 'Grid', dataSource: .... }
30197          * 
30198          *}
30199      */
30200     xgrid : false, 
30201     /**
30202      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
30203      * {tag: "input", type: "checkbox", autocomplete: "off"})
30204      */
30205    // defaultAutoCreate : { tag: 'div' },
30206     defaultAutoCreate : { tag: 'input', type: 'hidden', autocomplete: 'off'},
30207     /**
30208      * @cfg {String} addTitle Text to include for adding a title.
30209      */
30210     addTitle : false,
30211     //
30212     onResize : function(){
30213         Roo.form.Field.superclass.onResize.apply(this, arguments);
30214     },
30215
30216     initEvents : function(){
30217         // Roo.form.Checkbox.superclass.initEvents.call(this);
30218         // has no events...
30219        
30220     },
30221
30222
30223     getResizeEl : function(){
30224         return this.wrap;
30225     },
30226
30227     getPositionEl : function(){
30228         return this.wrap;
30229     },
30230
30231     // private
30232     onRender : function(ct, position){
30233         
30234         this.style = this.style || 'overflow: hidden; border:1px solid #c3daf9;';
30235         var style = this.style;
30236         delete this.style;
30237         
30238         Roo.form.GridField.superclass.onRender.call(this, ct, position);
30239         this.wrap = this.el.wrap({cls: ''}); // not sure why ive done thsi...
30240         this.viewEl = this.wrap.createChild({ tag: 'div' });
30241         if (style) {
30242             this.viewEl.applyStyles(style);
30243         }
30244         if (this.width) {
30245             this.viewEl.setWidth(this.width);
30246         }
30247         if (this.height) {
30248             this.viewEl.setHeight(this.height);
30249         }
30250         //if(this.inputValue !== undefined){
30251         //this.setValue(this.value);
30252         
30253         
30254         this.grid = new Roo.grid[this.xgrid.xtype](this.viewEl, this.xgrid);
30255         
30256         
30257         this.grid.render();
30258         this.grid.getDataSource().on('remove', this.refreshValue, this);
30259         this.grid.getDataSource().on('update', this.refreshValue, this);
30260         this.grid.on('afteredit', this.refreshValue, this);
30261  
30262     },
30263      
30264     
30265     /**
30266      * Sets the value of the item. 
30267      * @param {String} either an object  or a string..
30268      */
30269     setValue : function(v){
30270         //this.value = v;
30271         v = v || []; // empty set..
30272         // this does not seem smart - it really only affects memoryproxy grids..
30273         if (this.grid && this.grid.getDataSource() && typeof(v) != 'undefined') {
30274             var ds = this.grid.getDataSource();
30275             // assumes a json reader..
30276             var data = {}
30277             data[ds.reader.meta.root ] =  typeof(v) == 'string' ? Roo.decode(v) : v;
30278             ds.loadData( data);
30279         }
30280         // clear selection so it does not get stale.
30281         if (this.grid.sm) { 
30282             this.grid.sm.clearSelections();
30283         }
30284         
30285         Roo.form.GridField.superclass.setValue.call(this, v);
30286         this.refreshValue();
30287         // should load data in the grid really....
30288     },
30289     
30290     // private
30291     refreshValue: function() {
30292          var val = [];
30293         this.grid.getDataSource().each(function(r) {
30294             val.push(r.data);
30295         });
30296         this.el.dom.value = Roo.encode(val);
30297     }
30298     
30299      
30300     
30301     
30302 });/*
30303  * Based on:
30304  * Ext JS Library 1.1.1
30305  * Copyright(c) 2006-2007, Ext JS, LLC.
30306  *
30307  * Originally Released Under LGPL - original licence link has changed is not relivant.
30308  *
30309  * Fork - LGPL
30310  * <script type="text/javascript">
30311  */
30312 /**
30313  * @class Roo.form.DisplayField
30314  * @extends Roo.form.Field
30315  * A generic Field to display non-editable data.
30316  * @constructor
30317  * Creates a new Display Field item.
30318  * @param {Object} config Configuration options
30319  */
30320 Roo.form.DisplayField = function(config){
30321     Roo.form.DisplayField.superclass.constructor.call(this, config);
30322     
30323 };
30324
30325 Roo.extend(Roo.form.DisplayField, Roo.form.TextField,  {
30326     inputType:      'hidden',
30327     allowBlank:     true,
30328     readOnly:         true,
30329     
30330  
30331     /**
30332      * @cfg {String} focusClass The CSS class to use when the checkbox receives focus (defaults to undefined)
30333      */
30334     focusClass : undefined,
30335     /**
30336      * @cfg {String} fieldClass The default CSS class for the checkbox (defaults to "x-form-field")
30337      */
30338     fieldClass: 'x-form-field',
30339     
30340      /**
30341      * @cfg {Function} valueRenderer The renderer for the field (so you can reformat output). should return raw HTML
30342      */
30343     valueRenderer: undefined,
30344     
30345     width: 100,
30346     /**
30347      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
30348      * {tag: "input", type: "checkbox", autocomplete: "off"})
30349      */
30350      
30351  //   defaultAutoCreate : { tag: 'input', type: 'hidden', autocomplete: 'off'},
30352
30353     onResize : function(){
30354         Roo.form.DisplayField.superclass.onResize.apply(this, arguments);
30355         
30356     },
30357
30358     initEvents : function(){
30359         // Roo.form.Checkbox.superclass.initEvents.call(this);
30360         // has no events...
30361        
30362     },
30363
30364
30365     getResizeEl : function(){
30366         return this.wrap;
30367     },
30368
30369     getPositionEl : function(){
30370         return this.wrap;
30371     },
30372
30373     // private
30374     onRender : function(ct, position){
30375         
30376         Roo.form.DisplayField.superclass.onRender.call(this, ct, position);
30377         //if(this.inputValue !== undefined){
30378         this.wrap = this.el.wrap();
30379         
30380         this.viewEl = this.wrap.createChild({ tag: 'div', cls: 'x-form-displayfield'});
30381         
30382         if (this.bodyStyle) {
30383             this.viewEl.applyStyles(this.bodyStyle);
30384         }
30385         //this.viewEl.setStyle('padding', '2px');
30386         
30387         this.setValue(this.value);
30388         
30389     },
30390 /*
30391     // private
30392     initValue : Roo.emptyFn,
30393
30394   */
30395
30396         // private
30397     onClick : function(){
30398         
30399     },
30400
30401     /**
30402      * Sets the checked state of the checkbox.
30403      * @param {Boolean/String} checked True, 'true', '1', or 'on' to check the checkbox, any other value will uncheck it.
30404      */
30405     setValue : function(v){
30406         this.value = v;
30407         var html = this.valueRenderer ?  this.valueRenderer(v) : String.format('{0}', v);
30408         // this might be called before we have a dom element..
30409         if (!this.viewEl) {
30410             return;
30411         }
30412         this.viewEl.dom.innerHTML = html;
30413         Roo.form.DisplayField.superclass.setValue.call(this, v);
30414
30415     }
30416 });/*
30417  * 
30418  * Licence- LGPL
30419  * 
30420  */
30421
30422 /**
30423  * @class Roo.form.DayPicker
30424  * @extends Roo.form.Field
30425  * A Day picker show [M] [T] [W] ....
30426  * @constructor
30427  * Creates a new Day Picker
30428  * @param {Object} config Configuration options
30429  */
30430 Roo.form.DayPicker= function(config){
30431     Roo.form.DayPicker.superclass.constructor.call(this, config);
30432      
30433 };
30434
30435 Roo.extend(Roo.form.DayPicker, Roo.form.Field,  {
30436     /**
30437      * @cfg {String} focusClass The CSS class to use when the checkbox receives focus (defaults to undefined)
30438      */
30439     focusClass : undefined,
30440     /**
30441      * @cfg {String} fieldClass The default CSS class for the checkbox (defaults to "x-form-field")
30442      */
30443     fieldClass: "x-form-field",
30444    
30445     /**
30446      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
30447      * {tag: "input", type: "checkbox", autocomplete: "off"})
30448      */
30449     defaultAutoCreate : { tag: "input", type: 'hidden', autocomplete: "off"},
30450     
30451    
30452     actionMode : 'viewEl', 
30453     //
30454     // private
30455  
30456     inputType : 'hidden',
30457     
30458      
30459     inputElement: false, // real input element?
30460     basedOn: false, // ????
30461     
30462     isFormField: true, // not sure where this is needed!!!!
30463
30464     onResize : function(){
30465         Roo.form.Checkbox.superclass.onResize.apply(this, arguments);
30466         if(!this.boxLabel){
30467             this.el.alignTo(this.wrap, 'c-c');
30468         }
30469     },
30470
30471     initEvents : function(){
30472         Roo.form.Checkbox.superclass.initEvents.call(this);
30473         this.el.on("click", this.onClick,  this);
30474         this.el.on("change", this.onClick,  this);
30475     },
30476
30477
30478     getResizeEl : function(){
30479         return this.wrap;
30480     },
30481
30482     getPositionEl : function(){
30483         return this.wrap;
30484     },
30485
30486     
30487     // private
30488     onRender : function(ct, position){
30489         Roo.form.Checkbox.superclass.onRender.call(this, ct, position);
30490        
30491         this.wrap = this.el.wrap({cls: 'x-form-daypick-item '});
30492         
30493         var r1 = '<table><tr>';
30494         var r2 = '<tr class="x-form-daypick-icons">';
30495         for (var i=0; i < 7; i++) {
30496             r1+= '<td><div>' + Date.dayNames[i].substring(0,3) + '</div></td>';
30497             r2+= '<td><img class="x-menu-item-icon" src="' + Roo.BLANK_IMAGE_URL  +'"></td>';
30498         }
30499         
30500         var viewEl = this.wrap.createChild( r1 + '</tr>' + r2 + '</tr></table>');
30501         viewEl.select('img').on('click', this.onClick, this);
30502         this.viewEl = viewEl;   
30503         
30504         
30505         // this will not work on Chrome!!!
30506         this.el.on('DOMAttrModified', this.setFromHidden,  this); //ff
30507         this.el.on('propertychange', this.setFromHidden,  this);  //ie
30508         
30509         
30510           
30511
30512     },
30513
30514     // private
30515     initValue : Roo.emptyFn,
30516
30517     /**
30518      * Returns the checked state of the checkbox.
30519      * @return {Boolean} True if checked, else false
30520      */
30521     getValue : function(){
30522         return this.el.dom.value;
30523         
30524     },
30525
30526         // private
30527     onClick : function(e){ 
30528         //this.setChecked(!this.checked);
30529         Roo.get(e.target).toggleClass('x-menu-item-checked');
30530         this.refreshValue();
30531         //if(this.el.dom.checked != this.checked){
30532         //    this.setValue(this.el.dom.checked);
30533        // }
30534     },
30535     
30536     // private
30537     refreshValue : function()
30538     {
30539         var val = '';
30540         this.viewEl.select('img',true).each(function(e,i,n)  {
30541             val += e.is(".x-menu-item-checked") ? String(n) : '';
30542         });
30543         this.setValue(val, true);
30544     },
30545
30546     /**
30547      * Sets the checked state of the checkbox.
30548      * On is always based on a string comparison between inputValue and the param.
30549      * @param {Boolean/String} value - the value to set 
30550      * @param {Boolean/String} suppressEvent - whether to suppress the checkchange event.
30551      */
30552     setValue : function(v,suppressEvent){
30553         if (!this.el.dom) {
30554             return;
30555         }
30556         var old = this.el.dom.value ;
30557         this.el.dom.value = v;
30558         if (suppressEvent) {
30559             return ;
30560         }
30561          
30562         // update display..
30563         this.viewEl.select('img',true).each(function(e,i,n)  {
30564             
30565             var on = e.is(".x-menu-item-checked");
30566             var newv = v.indexOf(String(n)) > -1;
30567             if (on != newv) {
30568                 e.toggleClass('x-menu-item-checked');
30569             }
30570             
30571         });
30572         
30573         
30574         this.fireEvent('change', this, v, old);
30575         
30576         
30577     },
30578    
30579     // handle setting of hidden value by some other method!!?!?
30580     setFromHidden: function()
30581     {
30582         if(!this.el){
30583             return;
30584         }
30585         //console.log("SET FROM HIDDEN");
30586         //alert('setFrom hidden');
30587         this.setValue(this.el.dom.value);
30588     },
30589     
30590     onDestroy : function()
30591     {
30592         if(this.viewEl){
30593             Roo.get(this.viewEl).remove();
30594         }
30595          
30596         Roo.form.DayPicker.superclass.onDestroy.call(this);
30597     }
30598
30599 });/*
30600  * RooJS Library 1.1.1
30601  * Copyright(c) 2008-2011  Alan Knowles
30602  *
30603  * License - LGPL
30604  */
30605  
30606
30607 /**
30608  * @class Roo.form.ComboCheck
30609  * @extends Roo.form.ComboBox
30610  * A combobox for multiple select items.
30611  *
30612  * FIXME - could do with a reset button..
30613  * 
30614  * @constructor
30615  * Create a new ComboCheck
30616  * @param {Object} config Configuration options
30617  */
30618 Roo.form.ComboCheck = function(config){
30619     Roo.form.ComboCheck.superclass.constructor.call(this, config);
30620     // should verify some data...
30621     // like
30622     // hiddenName = required..
30623     // displayField = required
30624     // valudField == required
30625     var req= [ 'hiddenName', 'displayField', 'valueField' ];
30626     var _t = this;
30627     Roo.each(req, function(e) {
30628         if ((typeof(_t[e]) == 'undefined' ) || !_t[e].length) {
30629             throw "Roo.form.ComboCheck : missing value for: " + e;
30630         }
30631     });
30632     
30633     
30634 };
30635
30636 Roo.extend(Roo.form.ComboCheck, Roo.form.ComboBox, {
30637      
30638      
30639     editable : false,
30640      
30641     selectedClass: 'x-menu-item-checked', 
30642     
30643     // private
30644     onRender : function(ct, position){
30645         var _t = this;
30646         
30647         
30648         
30649         if(!this.tpl){
30650             var cls = 'x-combo-list';
30651
30652             
30653             this.tpl =  new Roo.Template({
30654                 html :  '<div class="'+cls+'-item x-menu-check-item">' +
30655                    '<img class="x-menu-item-icon" style="margin: 0px;" src="' + Roo.BLANK_IMAGE_URL + '">' + 
30656                    '<span>{' + this.displayField + '}</span>' +
30657                     '</div>' 
30658                 
30659             });
30660         }
30661  
30662         
30663         Roo.form.ComboCheck.superclass.onRender.call(this, ct, position);
30664         this.view.singleSelect = false;
30665         this.view.multiSelect = true;
30666         this.view.toggleSelect = true;
30667         this.pageTb.add(new Roo.Toolbar.Fill(), {
30668             
30669             text: 'Done',
30670             handler: function()
30671             {
30672                 _t.collapse();
30673             }
30674         });
30675     },
30676     
30677     onViewOver : function(e, t){
30678         // do nothing...
30679         return;
30680         
30681     },
30682     
30683     onViewClick : function(doFocus,index){
30684         return;
30685         
30686     },
30687     select: function () {
30688         //Roo.log("SELECT CALLED");
30689     },
30690      
30691     selectByValue : function(xv, scrollIntoView){
30692         var ar = this.getValueArray();
30693         var sels = [];
30694         
30695         Roo.each(ar, function(v) {
30696             if(v === undefined || v === null){
30697                 return;
30698             }
30699             var r = this.findRecord(this.valueField, v);
30700             if(r){
30701                 sels.push(this.store.indexOf(r))
30702                 
30703             }
30704         },this);
30705         this.view.select(sels);
30706         return false;
30707     },
30708     
30709     
30710     
30711     onSelect : function(record, index){
30712        // Roo.log("onselect Called");
30713        // this is only called by the clear button now..
30714         this.view.clearSelections();
30715         this.setValue('[]');
30716         if (this.value != this.valueBefore) {
30717             this.fireEvent('change', this, this.value, this.valueBefore);
30718             this.valueBefore = this.value;
30719         }
30720     },
30721     getValueArray : function()
30722     {
30723         var ar = [] ;
30724         
30725         try {
30726             //Roo.log(this.value);
30727             if (typeof(this.value) == 'undefined') {
30728                 return [];
30729             }
30730             var ar = Roo.decode(this.value);
30731             return  ar instanceof Array ? ar : []; //?? valid?
30732             
30733         } catch(e) {
30734             Roo.log(e + "\nRoo.form.ComboCheck:getValueArray  invalid data:" + this.getValue());
30735             return [];
30736         }
30737          
30738     },
30739     expand : function ()
30740     {
30741         
30742         Roo.form.ComboCheck.superclass.expand.call(this);
30743         this.valueBefore = typeof(this.value) == 'undefined' ? '' : this.value;
30744         //this.valueBefore = typeof(this.valueBefore) == 'undefined' ? '' : this.valueBefore;
30745         
30746
30747     },
30748     
30749     collapse : function(){
30750         Roo.form.ComboCheck.superclass.collapse.call(this);
30751         var sl = this.view.getSelectedIndexes();
30752         var st = this.store;
30753         var nv = [];
30754         var tv = [];
30755         var r;
30756         Roo.each(sl, function(i) {
30757             r = st.getAt(i);
30758             nv.push(r.get(this.valueField));
30759         },this);
30760         this.setValue(Roo.encode(nv));
30761         if (this.value != this.valueBefore) {
30762
30763             this.fireEvent('change', this, this.value, this.valueBefore);
30764             this.valueBefore = this.value;
30765         }
30766         
30767     },
30768     
30769     setValue : function(v){
30770         // Roo.log(v);
30771         this.value = v;
30772         
30773         var vals = this.getValueArray();
30774         var tv = [];
30775         Roo.each(vals, function(k) {
30776             var r = this.findRecord(this.valueField, k);
30777             if(r){
30778                 tv.push(r.data[this.displayField]);
30779             }else if(this.valueNotFoundText !== undefined){
30780                 tv.push( this.valueNotFoundText );
30781             }
30782         },this);
30783        // Roo.log(tv);
30784         
30785         Roo.form.ComboBox.superclass.setValue.call(this, tv.join(', '));
30786         this.hiddenField.value = v;
30787         this.value = v;
30788     }
30789     
30790 });/*
30791  * Based on:
30792  * Ext JS Library 1.1.1
30793  * Copyright(c) 2006-2007, Ext JS, LLC.
30794  *
30795  * Originally Released Under LGPL - original licence link has changed is not relivant.
30796  *
30797  * Fork - LGPL
30798  * <script type="text/javascript">
30799  */
30800  
30801 /**
30802  * @class Roo.form.Signature
30803  * @extends Roo.form.Field
30804  * Signature field.  
30805  * @constructor
30806  * 
30807  * @param {Object} config Configuration options
30808  */
30809
30810 Roo.form.Signature = function(config){
30811     Roo.form.Signature.superclass.constructor.call(this, config);
30812     
30813     this.addEvents({// not in used??
30814          /**
30815          * @event confirm
30816          * Fires when the 'confirm' icon is pressed (add a listener to enable add button)
30817              * @param {Roo.form.Signature} combo This combo box
30818              */
30819         'confirm' : true,
30820         /**
30821          * @event reset
30822          * Fires when the 'edit' icon is pressed (add a listener to enable add button)
30823              * @param {Roo.form.ComboBox} combo This combo box
30824              * @param {Roo.data.Record|false} record The data record returned from the underlying store (or false on nothing selected)
30825              */
30826         'reset' : true
30827     });
30828 };
30829
30830 Roo.extend(Roo.form.Signature, Roo.form.Field,  {
30831     /**
30832      * @cfg {Object} labels Label to use when rendering a form.
30833      * defaults to 
30834      * labels : { 
30835      *      clear : "Clear",
30836      *      confirm : "Confirm"
30837      *  }
30838      */
30839     labels : { 
30840         clear : "Clear",
30841         confirm : "Confirm"
30842     },
30843     /**
30844      * @cfg {Number} width The signature panel width (defaults to 300)
30845      */
30846     width: 300,
30847     /**
30848      * @cfg {Number} height The signature panel height (defaults to 100)
30849      */
30850     height : 100,
30851     /**
30852      * @cfg {Boolean} allowBlank False to validate that the value length > 0 (defaults to false)
30853      */
30854     allowBlank : false,
30855     
30856     //private
30857     // {Object} signPanel The signature SVG panel element (defaults to {})
30858     signPanel : {},
30859     // {Boolean} isMouseDown False to validate that the mouse down event (defaults to false)
30860     isMouseDown : false,
30861     // {Boolean} isConfirmed validate the signature is confirmed or not for submitting form (defaults to false)
30862     isConfirmed : false,
30863     // {String} signatureTmp SVG mapping string (defaults to empty string)
30864     signatureTmp : '',
30865     
30866     
30867     defaultAutoCreate : { // modified by initCompnoent..
30868         tag: "input",
30869         type:"hidden"
30870     },
30871
30872     // private
30873     onRender : function(ct, position){
30874         
30875         Roo.form.Signature.superclass.onRender.call(this, ct, position);
30876         
30877         this.wrap = this.el.wrap({
30878             cls:'x-form-signature-wrap', style : 'width: ' + this.width + 'px', cn:{cls:'x-form-signature'}
30879         });
30880         
30881         this.createToolbar(this);
30882         this.signPanel = this.wrap.createChild({
30883                 tag: 'div',
30884                 style: 'width: ' + this.width + 'px; height: ' + this.height + 'px; border: 0;'
30885             }, this.el
30886         );
30887             
30888         this.svgID = Roo.id();
30889         this.svgEl = this.signPanel.createChild({
30890               xmlns : 'http://www.w3.org/2000/svg',
30891               tag : 'svg',
30892               id : this.svgID + "-svg",
30893               width: this.width,
30894               height: this.height,
30895               viewBox: '0 0 '+this.width+' '+this.height,
30896               cn : [
30897                 {
30898                     tag: "rect",
30899                     id: this.svgID + "-svg-r",
30900                     width: this.width,
30901                     height: this.height,
30902                     fill: "#ffa"
30903                 },
30904                 {
30905                     tag: "line",
30906                     id: this.svgID + "-svg-l",
30907                     x1: "0", // start
30908                     y1: (this.height*0.8), // start set the line in 80% of height
30909                     x2: this.width, // end
30910                     y2: (this.height*0.8), // end set the line in 80% of height
30911                     'stroke': "#666",
30912                     'stroke-width': "1",
30913                     'stroke-dasharray': "3",
30914                     'shape-rendering': "crispEdges",
30915                     'pointer-events': "none"
30916                 },
30917                 {
30918                     tag: "path",
30919                     id: this.svgID + "-svg-p",
30920                     'stroke': "navy",
30921                     'stroke-width': "3",
30922                     'fill': "none",
30923                     'pointer-events': 'none'
30924                 }
30925               ]
30926         });
30927         this.createSVG();
30928         this.svgBox = this.svgEl.dom.getScreenCTM();
30929     },
30930     createSVG : function(){ 
30931         var svg = this.signPanel;
30932         var r = svg.select('#'+ this.svgID + '-svg-r', true).first().dom;
30933         var t = this;
30934
30935         r.addEventListener('mousedown', function(e) { return t.down(e); }, false);
30936         r.addEventListener('mousemove', function(e) { return t.move(e); }, false);
30937         r.addEventListener('mouseup', function(e) { return t.up(e); }, false);
30938         r.addEventListener('mouseout', function(e) { return t.up(e); }, false);
30939         r.addEventListener('touchstart', function(e) { return t.down(e); }, false);
30940         r.addEventListener('touchmove', function(e) { return t.move(e); }, false);
30941         r.addEventListener('touchend', function(e) { return t.up(e); }, false);
30942         
30943     },
30944     isTouchEvent : function(e){
30945         return e.type.match(/^touch/);
30946     },
30947     getCoords : function (e) {
30948         var pt    = this.svgEl.dom.createSVGPoint();
30949         pt.x = e.clientX; 
30950         pt.y = e.clientY;
30951         if (this.isTouchEvent(e)) {
30952             pt.x =  e.targetTouches[0].clientX 
30953             pt.y = e.targetTouches[0].clientY;
30954         }
30955         var a = this.svgEl.dom.getScreenCTM();
30956         var b = a.inverse();
30957         var mx = pt.matrixTransform(b);
30958         return mx.x + ',' + mx.y;
30959     },
30960     //mouse event headler 
30961     down : function (e) {
30962         this.signatureTmp += 'M' + this.getCoords(e) + ' ';
30963         this.signPanel.select('#'+ this.svgID + '-svg-p', true).first().attr('d', this.signatureTmp);
30964         
30965         this.isMouseDown = true;
30966         
30967         e.preventDefault();
30968     },
30969     move : function (e) {
30970         if (this.isMouseDown) {
30971             this.signatureTmp += 'L' + this.getCoords(e) + ' ';
30972             this.signPanel.select('#'+ this.svgID + '-svg-p', true).first().attr( 'd', this.signatureTmp);
30973         }
30974         
30975         e.preventDefault();
30976     },
30977     up : function (e) {
30978         this.isMouseDown = false;
30979         var sp = this.signatureTmp.split(' ');
30980         
30981         if(sp.length > 1){
30982             if(!sp[sp.length-2].match(/^L/)){
30983                 sp.pop();
30984                 sp.pop();
30985                 sp.push("");
30986                 this.signatureTmp = sp.join(" ");
30987             }
30988         }
30989         if(this.getValue() != this.signatureTmp){
30990             this.signPanel.select('#'+ this.svgID + '-svg-r', true).first().attr('fill', '#ffa');
30991             this.isConfirmed = false;
30992         }
30993         e.preventDefault();
30994     },
30995     
30996     /**
30997      * Protected method that will not generally be called directly. It
30998      * is called when the editor creates its toolbar. Override this method if you need to
30999      * add custom toolbar buttons.
31000      * @param {HtmlEditor} editor
31001      */
31002     createToolbar : function(editor){
31003          function btn(id, toggle, handler){
31004             var xid = fid + '-'+ id ;
31005             return {
31006                 id : xid,
31007                 cmd : id,
31008                 cls : 'x-btn-icon x-edit-'+id,
31009                 enableToggle:toggle !== false,
31010                 scope: editor, // was editor...
31011                 handler:handler||editor.relayBtnCmd,
31012                 clickEvent:'mousedown',
31013                 tooltip: etb.buttonTips[id] || undefined, ///tips ???
31014                 tabIndex:-1
31015             };
31016         }
31017         
31018         
31019         var tb = new Roo.Toolbar(editor.wrap.dom.firstChild);
31020         this.tb = tb;
31021         this.tb.add(
31022            {
31023                 cls : ' x-signature-btn x-signature-'+id,
31024                 scope: editor, // was editor...
31025                 handler: this.reset,
31026                 clickEvent:'mousedown',
31027                 text: this.labels.clear
31028             },
31029             {
31030                  xtype : 'Fill',
31031                  xns: Roo.Toolbar
31032             }, 
31033             {
31034                 cls : '  x-signature-btn x-signature-'+id,
31035                 scope: editor, // was editor...
31036                 handler: this.confirmHandler,
31037                 clickEvent:'mousedown',
31038                 text: this.labels.confirm
31039             }
31040         );
31041     
31042     },
31043     //public
31044     /**
31045      * when user is clicked confirm then show this image.....
31046      * 
31047      * @return {String} Image Data URI
31048      */
31049     getImageDataURI : function(){
31050         var svg = this.svgEl.dom.parentNode.innerHTML;
31051         var src = 'data:image/svg+xml;base64,'+window.btoa(svg);
31052         return src; 
31053     },
31054     /**
31055      * 
31056      * @return {Boolean} this.isConfirmed
31057      */
31058     getConfirmed : function(){
31059         return this.isConfirmed;
31060     },
31061     /**
31062      * 
31063      * @return {Number} this.width
31064      */
31065     getWidth : function(){
31066         return this.width;
31067     },
31068     /**
31069      * 
31070      * @return {Number} this.height
31071      */
31072     getHeight : function(){
31073         return this.height;
31074     },
31075     // private
31076     getSignature : function(){
31077         return this.signatureTmp;
31078     },
31079     // private
31080     reset : function(){
31081         this.signatureTmp = '';
31082         this.signPanel.select('#'+ this.svgID + '-svg-r', true).first().attr('fill', '#ffa');
31083         this.signPanel.select('#'+ this.svgID + '-svg-p', true).first().attr( 'd', '');
31084         this.isConfirmed = false;
31085         Roo.form.Signature.superclass.reset.call(this);
31086     },
31087     setSignature : function(s){
31088         this.signatureTmp = s;
31089         this.signPanel.select('#'+ this.svgID + '-svg-r', true).first().attr('fill', '#ffa');
31090         this.signPanel.select('#'+ this.svgID + '-svg-p', true).first().attr( 'd', s);
31091         this.setValue(s);
31092         this.isConfirmed = false;
31093         Roo.form.Signature.superclass.reset.call(this);
31094     }, 
31095     test : function(){
31096 //        Roo.log(this.signPanel.dom.contentWindow.up())
31097     },
31098     //private
31099     setConfirmed : function(){
31100         
31101         
31102         
31103 //        Roo.log(Roo.get(this.signPanel.dom.contentWindow.r).attr('fill', '#cfc'));
31104     },
31105     // private
31106     confirmHandler : function(){
31107         if(!this.getSignature()){
31108             return;
31109         }
31110         
31111         this.signPanel.select('#'+ this.svgID + '-svg-r', true).first().attr('fill', '#cfc');
31112         this.setValue(this.getSignature());
31113         this.isConfirmed = true;
31114         
31115         this.fireEvent('confirm', this);
31116     },
31117     // private
31118     // Subclasses should provide the validation implementation by overriding this
31119     validateValue : function(value){
31120         if(this.allowBlank){
31121             return true;
31122         }
31123         
31124         if(this.isConfirmed){
31125             return true;
31126         }
31127         return false;
31128     }
31129 });/*
31130  * Based on:
31131  * Ext JS Library 1.1.1
31132  * Copyright(c) 2006-2007, Ext JS, LLC.
31133  *
31134  * Originally Released Under LGPL - original licence link has changed is not relivant.
31135  *
31136  * Fork - LGPL
31137  * <script type="text/javascript">
31138  */
31139  
31140
31141 /**
31142  * @class Roo.form.ComboBox
31143  * @extends Roo.form.TriggerField
31144  * A combobox control with support for autocomplete, remote-loading, paging and many other features.
31145  * @constructor
31146  * Create a new ComboBox.
31147  * @param {Object} config Configuration options
31148  */
31149 Roo.form.Select = function(config){
31150     Roo.form.Select.superclass.constructor.call(this, config);
31151      
31152 };
31153
31154 Roo.extend(Roo.form.Select , Roo.form.ComboBox, {
31155     /**
31156      * @cfg {String/HTMLElement/Element} transform The id, DOM node or element of an existing select to convert to a ComboBox
31157      */
31158     /**
31159      * @cfg {Boolean} lazyRender True to prevent the ComboBox from rendering until requested (should always be used when
31160      * rendering into an Roo.Editor, defaults to false)
31161      */
31162     /**
31163      * @cfg {Boolean/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to:
31164      * {tag: "input", type: "text", size: "24", autocomplete: "off"})
31165      */
31166     /**
31167      * @cfg {Roo.data.Store} store The data store to which this combo is bound (defaults to undefined)
31168      */
31169     /**
31170      * @cfg {String} title If supplied, a header element is created containing this text and added into the top of
31171      * the dropdown list (defaults to undefined, with no header element)
31172      */
31173
31174      /**
31175      * @cfg {String/Roo.Template} tpl The template to use to render the output
31176      */
31177      
31178     // private
31179     defaultAutoCreate : {tag: "select"  },
31180     /**
31181      * @cfg {Number} listWidth The width in pixels of the dropdown list (defaults to the width of the ComboBox field)
31182      */
31183     listWidth: undefined,
31184     /**
31185      * @cfg {String} displayField The underlying data field name to bind to this CombBox (defaults to undefined if
31186      * mode = 'remote' or 'text' if mode = 'local')
31187      */
31188     displayField: undefined,
31189     /**
31190      * @cfg {String} valueField The underlying data value name to bind to this CombBox (defaults to undefined if
31191      * mode = 'remote' or 'value' if mode = 'local'). 
31192      * Note: use of a valueField requires the user make a selection
31193      * in order for a value to be mapped.
31194      */
31195     valueField: undefined,
31196     
31197     
31198     /**
31199      * @cfg {String} hiddenName If specified, a hidden form field with this name is dynamically generated to store the
31200      * field's data value (defaults to the underlying DOM element's name)
31201      */
31202     hiddenName: undefined,
31203     /**
31204      * @cfg {String} listClass CSS class to apply to the dropdown list element (defaults to '')
31205      */
31206     listClass: '',
31207     /**
31208      * @cfg {String} selectedClass CSS class to apply to the selected item in the dropdown list (defaults to 'x-combo-selected')
31209      */
31210     selectedClass: 'x-combo-selected',
31211     /**
31212      * @cfg {String} triggerClass An additional CSS class used to style the trigger button.  The trigger will always get the
31213      * class 'x-form-trigger' and triggerClass will be <b>appended</b> if specified (defaults to 'x-form-arrow-trigger'
31214      * which displays a downward arrow icon).
31215      */
31216     triggerClass : 'x-form-arrow-trigger',
31217     /**
31218      * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
31219      */
31220     shadow:'sides',
31221     /**
31222      * @cfg {String} listAlign A valid anchor position value. See {@link Roo.Element#alignTo} for details on supported
31223      * anchor positions (defaults to 'tl-bl')
31224      */
31225     listAlign: 'tl-bl?',
31226     /**
31227      * @cfg {Number} maxHeight The maximum height in pixels of the dropdown list before scrollbars are shown (defaults to 300)
31228      */
31229     maxHeight: 300,
31230     /**
31231      * @cfg {String} triggerAction The action to execute when the trigger field is activated.  Use 'all' to run the
31232      * query specified by the allQuery config option (defaults to 'query')
31233      */
31234     triggerAction: 'query',
31235     /**
31236      * @cfg {Number} minChars The minimum number of characters the user must type before autocomplete and typeahead activate
31237      * (defaults to 4, does not apply if editable = false)
31238      */
31239     minChars : 4,
31240     /**
31241      * @cfg {Boolean} typeAhead True to populate and autoselect the remainder of the text being typed after a configurable
31242      * delay (typeAheadDelay) if it matches a known value (defaults to false)
31243      */
31244     typeAhead: false,
31245     /**
31246      * @cfg {Number} queryDelay The length of time in milliseconds to delay between the start of typing and sending the
31247      * query to filter the dropdown list (defaults to 500 if mode = 'remote' or 10 if mode = 'local')
31248      */
31249     queryDelay: 500,
31250     /**
31251      * @cfg {Number} pageSize If greater than 0, a paging toolbar is displayed in the footer of the dropdown list and the
31252      * filter queries will execute with page start and limit parameters.  Only applies when mode = 'remote' (defaults to 0)
31253      */
31254     pageSize: 0,
31255     /**
31256      * @cfg {Boolean} selectOnFocus True to select any existing text in the field immediately on focus.  Only applies
31257      * when editable = true (defaults to false)
31258      */
31259     selectOnFocus:false,
31260     /**
31261      * @cfg {String} queryParam Name of the query as it will be passed on the querystring (defaults to 'query')
31262      */
31263     queryParam: 'query',
31264     /**
31265      * @cfg {String} loadingText The text to display in the dropdown list while data is loading.  Only applies
31266      * when mode = 'remote' (defaults to 'Loading...')
31267      */
31268     loadingText: 'Loading...',
31269     /**
31270      * @cfg {Boolean} resizable True to add a resize handle to the bottom of the dropdown list (defaults to false)
31271      */
31272     resizable: false,
31273     /**
31274      * @cfg {Number} handleHeight The height in pixels of the dropdown list resize handle if resizable = true (defaults to 8)
31275      */
31276     handleHeight : 8,
31277     /**
31278      * @cfg {Boolean} editable False to prevent the user from typing text directly into the field, just like a
31279      * traditional select (defaults to true)
31280      */
31281     editable: true,
31282     /**
31283      * @cfg {String} allQuery The text query to send to the server to return all records for the list with no filtering (defaults to '')
31284      */
31285     allQuery: '',
31286     /**
31287      * @cfg {String} mode Set to 'local' if the ComboBox loads local data (defaults to 'remote' which loads from the server)
31288      */
31289     mode: 'remote',
31290     /**
31291      * @cfg {Number} minListWidth The minimum width of the dropdown list in pixels (defaults to 70, will be ignored if
31292      * listWidth has a higher value)
31293      */
31294     minListWidth : 70,
31295     /**
31296      * @cfg {Boolean} forceSelection True to restrict the selected value to one of the values in the list, false to
31297      * allow the user to set arbitrary text into the field (defaults to false)
31298      */
31299     forceSelection:false,
31300     /**
31301      * @cfg {Number} typeAheadDelay The length of time in milliseconds to wait until the typeahead text is displayed
31302      * if typeAhead = true (defaults to 250)
31303      */
31304     typeAheadDelay : 250,
31305     /**
31306      * @cfg {String} valueNotFoundText When using a name/value combo, if the value passed to setValue is not found in
31307      * the store, valueNotFoundText will be displayed as the field text if defined (defaults to undefined)
31308      */
31309     valueNotFoundText : undefined,
31310     
31311     /**
31312      * @cfg {String} defaultValue The value displayed after loading the store.
31313      */
31314     defaultValue: '',
31315     
31316     /**
31317      * @cfg {Boolean} blockFocus Prevents all focus calls, so it can work with things like HTML edtor bar
31318      */
31319     blockFocus : false,
31320     
31321     /**
31322      * @cfg {Boolean} disableClear Disable showing of clear button.
31323      */
31324     disableClear : false,
31325     /**
31326      * @cfg {Boolean} alwaysQuery  Disable caching of results, and always send query
31327      */
31328     alwaysQuery : false,
31329     
31330     //private
31331     addicon : false,
31332     editicon: false,
31333     
31334     // element that contains real text value.. (when hidden is used..)
31335      
31336     // private
31337     onRender : function(ct, position){
31338         Roo.form.Field.prototype.onRender.call(this, ct, position);
31339         
31340         if(this.store){
31341             this.store.on('beforeload', this.onBeforeLoad, this);
31342             this.store.on('load', this.onLoad, this);
31343             this.store.on('loadexception', this.onLoadException, this);
31344             this.store.load({});
31345         }
31346         
31347         
31348         
31349     },
31350
31351     // private
31352     initEvents : function(){
31353         //Roo.form.ComboBox.superclass.initEvents.call(this);
31354  
31355     },
31356
31357     onDestroy : function(){
31358        
31359         if(this.store){
31360             this.store.un('beforeload', this.onBeforeLoad, this);
31361             this.store.un('load', this.onLoad, this);
31362             this.store.un('loadexception', this.onLoadException, this);
31363         }
31364         //Roo.form.ComboBox.superclass.onDestroy.call(this);
31365     },
31366
31367     // private
31368     fireKey : function(e){
31369         if(e.isNavKeyPress() && !this.list.isVisible()){
31370             this.fireEvent("specialkey", this, e);
31371         }
31372     },
31373
31374     // private
31375     onResize: function(w, h){
31376         
31377         return; 
31378     
31379         
31380     },
31381
31382     /**
31383      * Allow or prevent the user from directly editing the field text.  If false is passed,
31384      * the user will only be able to select from the items defined in the dropdown list.  This method
31385      * is the runtime equivalent of setting the 'editable' config option at config time.
31386      * @param {Boolean} value True to allow the user to directly edit the field text
31387      */
31388     setEditable : function(value){
31389          
31390     },
31391
31392     // private
31393     onBeforeLoad : function(){
31394         
31395         Roo.log("Select before load");
31396         return;
31397     
31398         this.innerList.update(this.loadingText ?
31399                '<div class="loading-indicator">'+this.loadingText+'</div>' : '');
31400         //this.restrictHeight();
31401         this.selectedIndex = -1;
31402     },
31403
31404     // private
31405     onLoad : function(){
31406
31407     
31408         var dom = this.el.dom;
31409         dom.innerHTML = '';
31410          var od = dom.ownerDocument;
31411          
31412         if (this.emptyText) {
31413             var op = od.createElement('option');
31414             op.setAttribute('value', '');
31415             op.innerHTML = String.format('{0}', this.emptyText);
31416             dom.appendChild(op);
31417         }
31418         if(this.store.getCount() > 0){
31419            
31420             var vf = this.valueField;
31421             var df = this.displayField;
31422             this.store.data.each(function(r) {
31423                 // which colmsn to use... testing - cdoe / title..
31424                 var op = od.createElement('option');
31425                 op.setAttribute('value', r.data[vf]);
31426                 op.innerHTML = String.format('{0}', r.data[df]);
31427                 dom.appendChild(op);
31428             });
31429             if (typeof(this.defaultValue != 'undefined')) {
31430                 this.setValue(this.defaultValue);
31431             }
31432             
31433              
31434         }else{
31435             //this.onEmptyResults();
31436         }
31437         //this.el.focus();
31438     },
31439     // private
31440     onLoadException : function()
31441     {
31442         dom.innerHTML = '';
31443             
31444         Roo.log("Select on load exception");
31445         return;
31446     
31447         this.collapse();
31448         Roo.log(this.store.reader.jsonData);
31449         if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
31450             Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
31451         }
31452         
31453         
31454     },
31455     // private
31456     onTypeAhead : function(){
31457          
31458     },
31459
31460     // private
31461     onSelect : function(record, index){
31462         Roo.log('on select?');
31463         return;
31464         if(this.fireEvent('beforeselect', this, record, index) !== false){
31465             this.setFromData(index > -1 ? record.data : false);
31466             this.collapse();
31467             this.fireEvent('select', this, record, index);
31468         }
31469     },
31470
31471     /**
31472      * Returns the currently selected field value or empty string if no value is set.
31473      * @return {String} value The selected value
31474      */
31475     getValue : function(){
31476         var dom = this.el.dom;
31477         this.value = dom.options[dom.selectedIndex].value;
31478         return this.value;
31479         
31480     },
31481
31482     /**
31483      * Clears any text/value currently set in the field
31484      */
31485     clearValue : function(){
31486         this.value = '';
31487         this.el.dom.selectedIndex = this.emptyText ? 0 : -1;
31488         
31489     },
31490
31491     /**
31492      * Sets the specified value into the field.  If the value finds a match, the corresponding record text
31493      * will be displayed in the field.  If the value does not match the data value of an existing item,
31494      * and the valueNotFoundText config option is defined, it will be displayed as the default field text.
31495      * Otherwise the field will be blank (although the value will still be set).
31496      * @param {String} value The value to match
31497      */
31498     setValue : function(v){
31499         var d = this.el.dom;
31500         for (var i =0; i < d.options.length;i++) {
31501             if (v == d.options[i].value) {
31502                 d.selectedIndex = i;
31503                 this.value = v;
31504                 return;
31505             }
31506         }
31507         this.clearValue();
31508     },
31509     /**
31510      * @property {Object} the last set data for the element
31511      */
31512     
31513     lastData : false,
31514     /**
31515      * Sets the value of the field based on a object which is related to the record format for the store.
31516      * @param {Object} value the value to set as. or false on reset?
31517      */
31518     setFromData : function(o){
31519         Roo.log('setfrom data?');
31520          
31521         
31522         
31523     },
31524     // private
31525     reset : function(){
31526         this.clearValue();
31527     },
31528     // private
31529     findRecord : function(prop, value){
31530         
31531         return false;
31532     
31533         var record;
31534         if(this.store.getCount() > 0){
31535             this.store.each(function(r){
31536                 if(r.data[prop] == value){
31537                     record = r;
31538                     return false;
31539                 }
31540                 return true;
31541             });
31542         }
31543         return record;
31544     },
31545     
31546     getName: function()
31547     {
31548         // returns hidden if it's set..
31549         if (!this.rendered) {return ''};
31550         return !this.hiddenName && this.el.dom.name  ? this.el.dom.name : (this.hiddenName || '');
31551         
31552     },
31553      
31554
31555     
31556
31557     // private
31558     onEmptyResults : function(){
31559         Roo.log('empty results');
31560         //this.collapse();
31561     },
31562
31563     /**
31564      * Returns true if the dropdown list is expanded, else false.
31565      */
31566     isExpanded : function(){
31567         return false;
31568     },
31569
31570     /**
31571      * Select an item in the dropdown list by its data value. This function does NOT cause the select event to fire.
31572      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
31573      * @param {String} value The data value of the item to select
31574      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
31575      * selected item if it is not currently in view (defaults to true)
31576      * @return {Boolean} True if the value matched an item in the list, else false
31577      */
31578     selectByValue : function(v, scrollIntoView){
31579         Roo.log('select By Value');
31580         return false;
31581     
31582         if(v !== undefined && v !== null){
31583             var r = this.findRecord(this.valueField || this.displayField, v);
31584             if(r){
31585                 this.select(this.store.indexOf(r), scrollIntoView);
31586                 return true;
31587             }
31588         }
31589         return false;
31590     },
31591
31592     /**
31593      * Select an item in the dropdown list by its numeric index in the list. This function does NOT cause the select event to fire.
31594      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
31595      * @param {Number} index The zero-based index of the list item to select
31596      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
31597      * selected item if it is not currently in view (defaults to true)
31598      */
31599     select : function(index, scrollIntoView){
31600         Roo.log('select ');
31601         return  ;
31602         
31603         this.selectedIndex = index;
31604         this.view.select(index);
31605         if(scrollIntoView !== false){
31606             var el = this.view.getNode(index);
31607             if(el){
31608                 this.innerList.scrollChildIntoView(el, false);
31609             }
31610         }
31611     },
31612
31613       
31614
31615     // private
31616     validateBlur : function(){
31617         
31618         return;
31619         
31620     },
31621
31622     // private
31623     initQuery : function(){
31624         this.doQuery(this.getRawValue());
31625     },
31626
31627     // private
31628     doForce : function(){
31629         if(this.el.dom.value.length > 0){
31630             this.el.dom.value =
31631                 this.lastSelectionText === undefined ? '' : this.lastSelectionText;
31632              
31633         }
31634     },
31635
31636     /**
31637      * Execute a query to filter the dropdown list.  Fires the beforequery event prior to performing the
31638      * query allowing the query action to be canceled if needed.
31639      * @param {String} query The SQL query to execute
31640      * @param {Boolean} forceAll True to force the query to execute even if there are currently fewer characters
31641      * in the field than the minimum specified by the minChars config option.  It also clears any filter previously
31642      * saved in the current store (defaults to false)
31643      */
31644     doQuery : function(q, forceAll){
31645         
31646         Roo.log('doQuery?');
31647         if(q === undefined || q === null){
31648             q = '';
31649         }
31650         var qe = {
31651             query: q,
31652             forceAll: forceAll,
31653             combo: this,
31654             cancel:false
31655         };
31656         if(this.fireEvent('beforequery', qe)===false || qe.cancel){
31657             return false;
31658         }
31659         q = qe.query;
31660         forceAll = qe.forceAll;
31661         if(forceAll === true || (q.length >= this.minChars)){
31662             if(this.lastQuery != q || this.alwaysQuery){
31663                 this.lastQuery = q;
31664                 if(this.mode == 'local'){
31665                     this.selectedIndex = -1;
31666                     if(forceAll){
31667                         this.store.clearFilter();
31668                     }else{
31669                         this.store.filter(this.displayField, q);
31670                     }
31671                     this.onLoad();
31672                 }else{
31673                     this.store.baseParams[this.queryParam] = q;
31674                     this.store.load({
31675                         params: this.getParams(q)
31676                     });
31677                     this.expand();
31678                 }
31679             }else{
31680                 this.selectedIndex = -1;
31681                 this.onLoad();   
31682             }
31683         }
31684     },
31685
31686     // private
31687     getParams : function(q){
31688         var p = {};
31689         //p[this.queryParam] = q;
31690         if(this.pageSize){
31691             p.start = 0;
31692             p.limit = this.pageSize;
31693         }
31694         return p;
31695     },
31696
31697     /**
31698      * Hides the dropdown list if it is currently expanded. Fires the 'collapse' event on completion.
31699      */
31700     collapse : function(){
31701         
31702     },
31703
31704     // private
31705     collapseIf : function(e){
31706         
31707     },
31708
31709     /**
31710      * Expands the dropdown list if it is currently hidden. Fires the 'expand' event on completion.
31711      */
31712     expand : function(){
31713         
31714     } ,
31715
31716     // private
31717      
31718
31719     /** 
31720     * @cfg {Boolean} grow 
31721     * @hide 
31722     */
31723     /** 
31724     * @cfg {Number} growMin 
31725     * @hide 
31726     */
31727     /** 
31728     * @cfg {Number} growMax 
31729     * @hide 
31730     */
31731     /**
31732      * @hide
31733      * @method autoSize
31734      */
31735     
31736     setWidth : function()
31737     {
31738         
31739     },
31740     getResizeEl : function(){
31741         return this.el;
31742     }
31743 });//<script type="text/javasscript">
31744  
31745
31746 /**
31747  * @class Roo.DDView
31748  * A DnD enabled version of Roo.View.
31749  * @param {Element/String} container The Element in which to create the View.
31750  * @param {String} tpl The template string used to create the markup for each element of the View
31751  * @param {Object} config The configuration properties. These include all the config options of
31752  * {@link Roo.View} plus some specific to this class.<br>
31753  * <p>
31754  * Drag/drop is implemented by adding {@link Roo.data.Record}s to the target DDView. If copying is
31755  * not being performed, the original {@link Roo.data.Record} is removed from the source DDView.<br>
31756  * <p>
31757  * The following extra CSS rules are needed to provide insertion point highlighting:<pre><code>
31758 .x-view-drag-insert-above {
31759         border-top:1px dotted #3366cc;
31760 }
31761 .x-view-drag-insert-below {
31762         border-bottom:1px dotted #3366cc;
31763 }
31764 </code></pre>
31765  * 
31766  */
31767  
31768 Roo.DDView = function(container, tpl, config) {
31769     Roo.DDView.superclass.constructor.apply(this, arguments);
31770     this.getEl().setStyle("outline", "0px none");
31771     this.getEl().unselectable();
31772     if (this.dragGroup) {
31773                 this.setDraggable(this.dragGroup.split(","));
31774     }
31775     if (this.dropGroup) {
31776                 this.setDroppable(this.dropGroup.split(","));
31777     }
31778     if (this.deletable) {
31779         this.setDeletable();
31780     }
31781     this.isDirtyFlag = false;
31782         this.addEvents({
31783                 "drop" : true
31784         });
31785 };
31786
31787 Roo.extend(Roo.DDView, Roo.View, {
31788 /**     @cfg {String/Array} dragGroup The ddgroup name(s) for the View's DragZone. */
31789 /**     @cfg {String/Array} dropGroup The ddgroup name(s) for the View's DropZone. */
31790 /**     @cfg {Boolean} copy Causes drag operations to copy nodes rather than move. */
31791 /**     @cfg {Boolean} allowCopy Causes ctrl/drag operations to copy nodes rather than move. */
31792
31793         isFormField: true,
31794
31795         reset: Roo.emptyFn,
31796         
31797         clearInvalid: Roo.form.Field.prototype.clearInvalid,
31798
31799         validate: function() {
31800                 return true;
31801         },
31802         
31803         destroy: function() {
31804                 this.purgeListeners();
31805                 this.getEl.removeAllListeners();
31806                 this.getEl().remove();
31807                 if (this.dragZone) {
31808                         if (this.dragZone.destroy) {
31809                                 this.dragZone.destroy();
31810                         }
31811                 }
31812                 if (this.dropZone) {
31813                         if (this.dropZone.destroy) {
31814                                 this.dropZone.destroy();
31815                         }
31816                 }
31817         },
31818
31819 /**     Allows this class to be an Roo.form.Field so it can be found using {@link Roo.form.BasicForm#findField}. */
31820         getName: function() {
31821                 return this.name;
31822         },
31823
31824 /**     Loads the View from a JSON string representing the Records to put into the Store. */
31825         setValue: function(v) {
31826                 if (!this.store) {
31827                         throw "DDView.setValue(). DDView must be constructed with a valid Store";
31828                 }
31829                 var data = {};
31830                 data[this.store.reader.meta.root] = v ? [].concat(v) : [];
31831                 this.store.proxy = new Roo.data.MemoryProxy(data);
31832                 this.store.load();
31833         },
31834
31835 /**     @return {String} a parenthesised list of the ids of the Records in the View. */
31836         getValue: function() {
31837                 var result = '(';
31838                 this.store.each(function(rec) {
31839                         result += rec.id + ',';
31840                 });
31841                 return result.substr(0, result.length - 1) + ')';
31842         },
31843         
31844         getIds: function() {
31845                 var i = 0, result = new Array(this.store.getCount());
31846                 this.store.each(function(rec) {
31847                         result[i++] = rec.id;
31848                 });
31849                 return result;
31850         },
31851         
31852         isDirty: function() {
31853                 return this.isDirtyFlag;
31854         },
31855
31856 /**
31857  *      Part of the Roo.dd.DropZone interface. If no target node is found, the
31858  *      whole Element becomes the target, and this causes the drop gesture to append.
31859  */
31860     getTargetFromEvent : function(e) {
31861                 var target = e.getTarget();
31862                 while ((target !== null) && (target.parentNode != this.el.dom)) {
31863                 target = target.parentNode;
31864                 }
31865                 if (!target) {
31866                         target = this.el.dom.lastChild || this.el.dom;
31867                 }
31868                 return target;
31869     },
31870
31871 /**
31872  *      Create the drag data which consists of an object which has the property "ddel" as
31873  *      the drag proxy element. 
31874  */
31875     getDragData : function(e) {
31876         var target = this.findItemFromChild(e.getTarget());
31877                 if(target) {
31878                         this.handleSelection(e);
31879                         var selNodes = this.getSelectedNodes();
31880             var dragData = {
31881                 source: this,
31882                 copy: this.copy || (this.allowCopy && e.ctrlKey),
31883                 nodes: selNodes,
31884                 records: []
31885                         };
31886                         var selectedIndices = this.getSelectedIndexes();
31887                         for (var i = 0; i < selectedIndices.length; i++) {
31888                                 dragData.records.push(this.store.getAt(selectedIndices[i]));
31889                         }
31890                         if (selNodes.length == 1) {
31891                                 dragData.ddel = target.cloneNode(true); // the div element
31892                         } else {
31893                                 var div = document.createElement('div'); // create the multi element drag "ghost"
31894                                 div.className = 'multi-proxy';
31895                                 for (var i = 0, len = selNodes.length; i < len; i++) {
31896                                         div.appendChild(selNodes[i].cloneNode(true));
31897                                 }
31898                                 dragData.ddel = div;
31899                         }
31900             //console.log(dragData)
31901             //console.log(dragData.ddel.innerHTML)
31902                         return dragData;
31903                 }
31904         //console.log('nodragData')
31905                 return false;
31906     },
31907     
31908 /**     Specify to which ddGroup items in this DDView may be dragged. */
31909     setDraggable: function(ddGroup) {
31910         if (ddGroup instanceof Array) {
31911                 Roo.each(ddGroup, this.setDraggable, this);
31912                 return;
31913         }
31914         if (this.dragZone) {
31915                 this.dragZone.addToGroup(ddGroup);
31916         } else {
31917                         this.dragZone = new Roo.dd.DragZone(this.getEl(), {
31918                                 containerScroll: true,
31919                                 ddGroup: ddGroup 
31920
31921                         });
31922 //                      Draggability implies selection. DragZone's mousedown selects the element.
31923                         if (!this.multiSelect) { this.singleSelect = true; }
31924
31925 //                      Wire the DragZone's handlers up to methods in *this*
31926                         this.dragZone.getDragData = this.getDragData.createDelegate(this);
31927                 }
31928     },
31929
31930 /**     Specify from which ddGroup this DDView accepts drops. */
31931     setDroppable: function(ddGroup) {
31932         if (ddGroup instanceof Array) {
31933                 Roo.each(ddGroup, this.setDroppable, this);
31934                 return;
31935         }
31936         if (this.dropZone) {
31937                 this.dropZone.addToGroup(ddGroup);
31938         } else {
31939                         this.dropZone = new Roo.dd.DropZone(this.getEl(), {
31940                                 containerScroll: true,
31941                                 ddGroup: ddGroup
31942                         });
31943
31944 //                      Wire the DropZone's handlers up to methods in *this*
31945                         this.dropZone.getTargetFromEvent = this.getTargetFromEvent.createDelegate(this);
31946                         this.dropZone.onNodeEnter = this.onNodeEnter.createDelegate(this);
31947                         this.dropZone.onNodeOver = this.onNodeOver.createDelegate(this);
31948                         this.dropZone.onNodeOut = this.onNodeOut.createDelegate(this);
31949                         this.dropZone.onNodeDrop = this.onNodeDrop.createDelegate(this);
31950                 }
31951     },
31952
31953 /**     Decide whether to drop above or below a View node. */
31954     getDropPoint : function(e, n, dd){
31955         if (n == this.el.dom) { return "above"; }
31956                 var t = Roo.lib.Dom.getY(n), b = t + n.offsetHeight;
31957                 var c = t + (b - t) / 2;
31958                 var y = Roo.lib.Event.getPageY(e);
31959                 if(y <= c) {
31960                         return "above";
31961                 }else{
31962                         return "below";
31963                 }
31964     },
31965
31966     onNodeEnter : function(n, dd, e, data){
31967                 return false;
31968     },
31969     
31970     onNodeOver : function(n, dd, e, data){
31971                 var pt = this.getDropPoint(e, n, dd);
31972                 // set the insert point style on the target node
31973                 var dragElClass = this.dropNotAllowed;
31974                 if (pt) {
31975                         var targetElClass;
31976                         if (pt == "above"){
31977                                 dragElClass = n.previousSibling ? "x-tree-drop-ok-between" : "x-tree-drop-ok-above";
31978                                 targetElClass = "x-view-drag-insert-above";
31979                         } else {
31980                                 dragElClass = n.nextSibling ? "x-tree-drop-ok-between" : "x-tree-drop-ok-below";
31981                                 targetElClass = "x-view-drag-insert-below";
31982                         }
31983                         if (this.lastInsertClass != targetElClass){
31984                                 Roo.fly(n).replaceClass(this.lastInsertClass, targetElClass);
31985                                 this.lastInsertClass = targetElClass;
31986                         }
31987                 }
31988                 return dragElClass;
31989         },
31990
31991     onNodeOut : function(n, dd, e, data){
31992                 this.removeDropIndicators(n);
31993     },
31994
31995     onNodeDrop : function(n, dd, e, data){
31996         if (this.fireEvent("drop", this, n, dd, e, data) === false) {
31997                 return false;
31998         }
31999         var pt = this.getDropPoint(e, n, dd);
32000                 var insertAt = (n == this.el.dom) ? this.nodes.length : n.nodeIndex;
32001                 if (pt == "below") { insertAt++; }
32002                 for (var i = 0; i < data.records.length; i++) {
32003                         var r = data.records[i];
32004                         var dup = this.store.getById(r.id);
32005                         if (dup && (dd != this.dragZone)) {
32006                                 Roo.fly(this.getNode(this.store.indexOf(dup))).frame("red", 1);
32007                         } else {
32008                                 if (data.copy) {
32009                                         this.store.insert(insertAt++, r.copy());
32010                                 } else {
32011                                         data.source.isDirtyFlag = true;
32012                                         r.store.remove(r);
32013                                         this.store.insert(insertAt++, r);
32014                                 }
32015                                 this.isDirtyFlag = true;
32016                         }
32017                 }
32018                 this.dragZone.cachedTarget = null;
32019                 return true;
32020     },
32021
32022     removeDropIndicators : function(n){
32023                 if(n){
32024                         Roo.fly(n).removeClass([
32025                                 "x-view-drag-insert-above",
32026                                 "x-view-drag-insert-below"]);
32027                         this.lastInsertClass = "_noclass";
32028                 }
32029     },
32030
32031 /**
32032  *      Utility method. Add a delete option to the DDView's context menu.
32033  *      @param {String} imageUrl The URL of the "delete" icon image.
32034  */
32035         setDeletable: function(imageUrl) {
32036                 if (!this.singleSelect && !this.multiSelect) {
32037                         this.singleSelect = true;
32038                 }
32039                 var c = this.getContextMenu();
32040                 this.contextMenu.on("itemclick", function(item) {
32041                         switch (item.id) {
32042                                 case "delete":
32043                                         this.remove(this.getSelectedIndexes());
32044                                         break;
32045                         }
32046                 }, this);
32047                 this.contextMenu.add({
32048                         icon: imageUrl,
32049                         id: "delete",
32050                         text: 'Delete'
32051                 });
32052         },
32053         
32054 /**     Return the context menu for this DDView. */
32055         getContextMenu: function() {
32056                 if (!this.contextMenu) {
32057 //                      Create the View's context menu
32058                         this.contextMenu = new Roo.menu.Menu({
32059                                 id: this.id + "-contextmenu"
32060                         });
32061                         this.el.on("contextmenu", this.showContextMenu, this);
32062                 }
32063                 return this.contextMenu;
32064         },
32065         
32066         disableContextMenu: function() {
32067                 if (this.contextMenu) {
32068                         this.el.un("contextmenu", this.showContextMenu, this);
32069                 }
32070         },
32071
32072         showContextMenu: function(e, item) {
32073         item = this.findItemFromChild(e.getTarget());
32074                 if (item) {
32075                         e.stopEvent();
32076                         this.select(this.getNode(item), this.multiSelect && e.ctrlKey, true);
32077                         this.contextMenu.showAt(e.getXY());
32078             }
32079     },
32080
32081 /**
32082  *      Remove {@link Roo.data.Record}s at the specified indices.
32083  *      @param {Array/Number} selectedIndices The index (or Array of indices) of Records to remove.
32084  */
32085     remove: function(selectedIndices) {
32086                 selectedIndices = [].concat(selectedIndices);
32087                 for (var i = 0; i < selectedIndices.length; i++) {
32088                         var rec = this.store.getAt(selectedIndices[i]);
32089                         this.store.remove(rec);
32090                 }
32091     },
32092
32093 /**
32094  *      Double click fires the event, but also, if this is draggable, and there is only one other
32095  *      related DropZone, it transfers the selected node.
32096  */
32097     onDblClick : function(e){
32098         var item = this.findItemFromChild(e.getTarget());
32099         if(item){
32100             if (this.fireEvent("dblclick", this, this.indexOf(item), item, e) === false) {
32101                 return false;
32102             }
32103             if (this.dragGroup) {
32104                     var targets = Roo.dd.DragDropMgr.getRelated(this.dragZone, true);
32105                     while (targets.indexOf(this.dropZone) > -1) {
32106                             targets.remove(this.dropZone);
32107                                 }
32108                     if (targets.length == 1) {
32109                                         this.dragZone.cachedTarget = null;
32110                         var el = Roo.get(targets[0].getEl());
32111                         var box = el.getBox(true);
32112                         targets[0].onNodeDrop(el.dom, {
32113                                 target: el.dom,
32114                                 xy: [box.x, box.y + box.height - 1]
32115                         }, null, this.getDragData(e));
32116                     }
32117                 }
32118         }
32119     },
32120     
32121     handleSelection: function(e) {
32122                 this.dragZone.cachedTarget = null;
32123         var item = this.findItemFromChild(e.getTarget());
32124         if (!item) {
32125                 this.clearSelections(true);
32126                 return;
32127         }
32128                 if (item && (this.multiSelect || this.singleSelect)){
32129                         if(this.multiSelect && e.shiftKey && (!e.ctrlKey) && this.lastSelection){
32130                                 this.select(this.getNodes(this.indexOf(this.lastSelection), item.nodeIndex), false);
32131                         }else if (this.isSelected(this.getNode(item)) && e.ctrlKey){
32132                                 this.unselect(item);
32133                         } else {
32134                                 this.select(item, this.multiSelect && e.ctrlKey);
32135                                 this.lastSelection = item;
32136                         }
32137                 }
32138     },
32139
32140     onItemClick : function(item, index, e){
32141                 if(this.fireEvent("beforeclick", this, index, item, e) === false){
32142                         return false;
32143                 }
32144                 return true;
32145     },
32146
32147     unselect : function(nodeInfo, suppressEvent){
32148                 var node = this.getNode(nodeInfo);
32149                 if(node && this.isSelected(node)){
32150                         if(this.fireEvent("beforeselect", this, node, this.selections) !== false){
32151                                 Roo.fly(node).removeClass(this.selectedClass);
32152                                 this.selections.remove(node);
32153                                 if(!suppressEvent){
32154                                         this.fireEvent("selectionchange", this, this.selections);
32155                                 }
32156                         }
32157                 }
32158     }
32159 });
32160 /*
32161  * Based on:
32162  * Ext JS Library 1.1.1
32163  * Copyright(c) 2006-2007, Ext JS, LLC.
32164  *
32165  * Originally Released Under LGPL - original licence link has changed is not relivant.
32166  *
32167  * Fork - LGPL
32168  * <script type="text/javascript">
32169  */
32170  
32171 /**
32172  * @class Roo.LayoutManager
32173  * @extends Roo.util.Observable
32174  * Base class for layout managers.
32175  */
32176 Roo.LayoutManager = function(container, config){
32177     Roo.LayoutManager.superclass.constructor.call(this);
32178     this.el = Roo.get(container);
32179     // ie scrollbar fix
32180     if(this.el.dom == document.body && Roo.isIE && !config.allowScroll){
32181         document.body.scroll = "no";
32182     }else if(this.el.dom != document.body && this.el.getStyle('position') == 'static'){
32183         this.el.position('relative');
32184     }
32185     this.id = this.el.id;
32186     this.el.addClass("x-layout-container");
32187     /** false to disable window resize monitoring @type Boolean */
32188     this.monitorWindowResize = true;
32189     this.regions = {};
32190     this.addEvents({
32191         /**
32192          * @event layout
32193          * Fires when a layout is performed. 
32194          * @param {Roo.LayoutManager} this
32195          */
32196         "layout" : true,
32197         /**
32198          * @event regionresized
32199          * Fires when the user resizes a region. 
32200          * @param {Roo.LayoutRegion} region The resized region
32201          * @param {Number} newSize The new size (width for east/west, height for north/south)
32202          */
32203         "regionresized" : true,
32204         /**
32205          * @event regioncollapsed
32206          * Fires when a region is collapsed. 
32207          * @param {Roo.LayoutRegion} region The collapsed region
32208          */
32209         "regioncollapsed" : true,
32210         /**
32211          * @event regionexpanded
32212          * Fires when a region is expanded.  
32213          * @param {Roo.LayoutRegion} region The expanded region
32214          */
32215         "regionexpanded" : true
32216     });
32217     this.updating = false;
32218     Roo.EventManager.onWindowResize(this.onWindowResize, this, true);
32219 };
32220
32221 Roo.extend(Roo.LayoutManager, Roo.util.Observable, {
32222     /**
32223      * Returns true if this layout is currently being updated
32224      * @return {Boolean}
32225      */
32226     isUpdating : function(){
32227         return this.updating; 
32228     },
32229     
32230     /**
32231      * Suspend the LayoutManager from doing auto-layouts while
32232      * making multiple add or remove calls
32233      */
32234     beginUpdate : function(){
32235         this.updating = true;    
32236     },
32237     
32238     /**
32239      * Restore auto-layouts and optionally disable the manager from performing a layout
32240      * @param {Boolean} noLayout true to disable a layout update 
32241      */
32242     endUpdate : function(noLayout){
32243         this.updating = false;
32244         if(!noLayout){
32245             this.layout();
32246         }    
32247     },
32248     
32249     layout: function(){
32250         
32251     },
32252     
32253     onRegionResized : function(region, newSize){
32254         this.fireEvent("regionresized", region, newSize);
32255         this.layout();
32256     },
32257     
32258     onRegionCollapsed : function(region){
32259         this.fireEvent("regioncollapsed", region);
32260     },
32261     
32262     onRegionExpanded : function(region){
32263         this.fireEvent("regionexpanded", region);
32264     },
32265         
32266     /**
32267      * Returns the size of the current view. This method normalizes document.body and element embedded layouts and
32268      * performs box-model adjustments.
32269      * @return {Object} The size as an object {width: (the width), height: (the height)}
32270      */
32271     getViewSize : function(){
32272         var size;
32273         if(this.el.dom != document.body){
32274             size = this.el.getSize();
32275         }else{
32276             size = {width: Roo.lib.Dom.getViewWidth(), height: Roo.lib.Dom.getViewHeight()};
32277         }
32278         size.width -= this.el.getBorderWidth("lr")-this.el.getPadding("lr");
32279         size.height -= this.el.getBorderWidth("tb")-this.el.getPadding("tb");
32280         return size;
32281     },
32282     
32283     /**
32284      * Returns the Element this layout is bound to.
32285      * @return {Roo.Element}
32286      */
32287     getEl : function(){
32288         return this.el;
32289     },
32290     
32291     /**
32292      * Returns the specified region.
32293      * @param {String} target The region key ('center', 'north', 'south', 'east' or 'west')
32294      * @return {Roo.LayoutRegion}
32295      */
32296     getRegion : function(target){
32297         return this.regions[target.toLowerCase()];
32298     },
32299     
32300     onWindowResize : function(){
32301         if(this.monitorWindowResize){
32302             this.layout();
32303         }
32304     }
32305 });/*
32306  * Based on:
32307  * Ext JS Library 1.1.1
32308  * Copyright(c) 2006-2007, Ext JS, LLC.
32309  *
32310  * Originally Released Under LGPL - original licence link has changed is not relivant.
32311  *
32312  * Fork - LGPL
32313  * <script type="text/javascript">
32314  */
32315 /**
32316  * @class Roo.BorderLayout
32317  * @extends Roo.LayoutManager
32318  * This class represents a common layout manager used in desktop applications. For screenshots and more details,
32319  * please see: <br><br>
32320  * <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>
32321  * <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>
32322  * Example:
32323  <pre><code>
32324  var layout = new Roo.BorderLayout(document.body, {
32325     north: {
32326         initialSize: 25,
32327         titlebar: false
32328     },
32329     west: {
32330         split:true,
32331         initialSize: 200,
32332         minSize: 175,
32333         maxSize: 400,
32334         titlebar: true,
32335         collapsible: true
32336     },
32337     east: {
32338         split:true,
32339         initialSize: 202,
32340         minSize: 175,
32341         maxSize: 400,
32342         titlebar: true,
32343         collapsible: true
32344     },
32345     south: {
32346         split:true,
32347         initialSize: 100,
32348         minSize: 100,
32349         maxSize: 200,
32350         titlebar: true,
32351         collapsible: true
32352     },
32353     center: {
32354         titlebar: true,
32355         autoScroll:true,
32356         resizeTabs: true,
32357         minTabWidth: 50,
32358         preferredTabWidth: 150
32359     }
32360 });
32361
32362 // shorthand
32363 var CP = Roo.ContentPanel;
32364
32365 layout.beginUpdate();
32366 layout.add("north", new CP("north", "North"));
32367 layout.add("south", new CP("south", {title: "South", closable: true}));
32368 layout.add("west", new CP("west", {title: "West"}));
32369 layout.add("east", new CP("autoTabs", {title: "Auto Tabs", closable: true}));
32370 layout.add("center", new CP("center1", {title: "Close Me", closable: true}));
32371 layout.add("center", new CP("center2", {title: "Center Panel", closable: false}));
32372 layout.getRegion("center").showPanel("center1");
32373 layout.endUpdate();
32374 </code></pre>
32375
32376 <b>The container the layout is rendered into can be either the body element or any other element.
32377 If it is not the body element, the container needs to either be an absolute positioned element,
32378 or you will need to add "position:relative" to the css of the container.  You will also need to specify
32379 the container size if it is not the body element.</b>
32380
32381 * @constructor
32382 * Create a new BorderLayout
32383 * @param {String/HTMLElement/Element} container The container this layout is bound to
32384 * @param {Object} config Configuration options
32385  */
32386 Roo.BorderLayout = function(container, config){
32387     config = config || {};
32388     Roo.BorderLayout.superclass.constructor.call(this, container, config);
32389     this.factory = config.factory || Roo.BorderLayout.RegionFactory;
32390     for(var i = 0, len = this.factory.validRegions.length; i < len; i++) {
32391         var target = this.factory.validRegions[i];
32392         if(config[target]){
32393             this.addRegion(target, config[target]);
32394         }
32395     }
32396 };
32397
32398 Roo.extend(Roo.BorderLayout, Roo.LayoutManager, {
32399     /**
32400      * Creates and adds a new region if it doesn't already exist.
32401      * @param {String} target The target region key (north, south, east, west or center).
32402      * @param {Object} config The regions config object
32403      * @return {BorderLayoutRegion} The new region
32404      */
32405     addRegion : function(target, config){
32406         if(!this.regions[target]){
32407             var r = this.factory.create(target, this, config);
32408             this.bindRegion(target, r);
32409         }
32410         return this.regions[target];
32411     },
32412
32413     // private (kinda)
32414     bindRegion : function(name, r){
32415         this.regions[name] = r;
32416         r.on("visibilitychange", this.layout, this);
32417         r.on("paneladded", this.layout, this);
32418         r.on("panelremoved", this.layout, this);
32419         r.on("invalidated", this.layout, this);
32420         r.on("resized", this.onRegionResized, this);
32421         r.on("collapsed", this.onRegionCollapsed, this);
32422         r.on("expanded", this.onRegionExpanded, this);
32423     },
32424
32425     /**
32426      * Performs a layout update.
32427      */
32428     layout : function(){
32429         if(this.updating) return;
32430         var size = this.getViewSize();
32431         var w = size.width;
32432         var h = size.height;
32433         var centerW = w;
32434         var centerH = h;
32435         var centerY = 0;
32436         var centerX = 0;
32437         //var x = 0, y = 0;
32438
32439         var rs = this.regions;
32440         var north = rs["north"];
32441         var south = rs["south"]; 
32442         var west = rs["west"];
32443         var east = rs["east"];
32444         var center = rs["center"];
32445         //if(this.hideOnLayout){ // not supported anymore
32446             //c.el.setStyle("display", "none");
32447         //}
32448         if(north && north.isVisible()){
32449             var b = north.getBox();
32450             var m = north.getMargins();
32451             b.width = w - (m.left+m.right);
32452             b.x = m.left;
32453             b.y = m.top;
32454             centerY = b.height + b.y + m.bottom;
32455             centerH -= centerY;
32456             north.updateBox(this.safeBox(b));
32457         }
32458         if(south && south.isVisible()){
32459             var b = south.getBox();
32460             var m = south.getMargins();
32461             b.width = w - (m.left+m.right);
32462             b.x = m.left;
32463             var totalHeight = (b.height + m.top + m.bottom);
32464             b.y = h - totalHeight + m.top;
32465             centerH -= totalHeight;
32466             south.updateBox(this.safeBox(b));
32467         }
32468         if(west && west.isVisible()){
32469             var b = west.getBox();
32470             var m = west.getMargins();
32471             b.height = centerH - (m.top+m.bottom);
32472             b.x = m.left;
32473             b.y = centerY + m.top;
32474             var totalWidth = (b.width + m.left + m.right);
32475             centerX += totalWidth;
32476             centerW -= totalWidth;
32477             west.updateBox(this.safeBox(b));
32478         }
32479         if(east && east.isVisible()){
32480             var b = east.getBox();
32481             var m = east.getMargins();
32482             b.height = centerH - (m.top+m.bottom);
32483             var totalWidth = (b.width + m.left + m.right);
32484             b.x = w - totalWidth + m.left;
32485             b.y = centerY + m.top;
32486             centerW -= totalWidth;
32487             east.updateBox(this.safeBox(b));
32488         }
32489         if(center){
32490             var m = center.getMargins();
32491             var centerBox = {
32492                 x: centerX + m.left,
32493                 y: centerY + m.top,
32494                 width: centerW - (m.left+m.right),
32495                 height: centerH - (m.top+m.bottom)
32496             };
32497             //if(this.hideOnLayout){
32498                 //center.el.setStyle("display", "block");
32499             //}
32500             center.updateBox(this.safeBox(centerBox));
32501         }
32502         this.el.repaint();
32503         this.fireEvent("layout", this);
32504     },
32505
32506     // private
32507     safeBox : function(box){
32508         box.width = Math.max(0, box.width);
32509         box.height = Math.max(0, box.height);
32510         return box;
32511     },
32512
32513     /**
32514      * Adds a ContentPanel (or subclass) to this layout.
32515      * @param {String} target The target region key (north, south, east, west or center).
32516      * @param {Roo.ContentPanel} panel The panel to add
32517      * @return {Roo.ContentPanel} The added panel
32518      */
32519     add : function(target, panel){
32520          
32521         target = target.toLowerCase();
32522         return this.regions[target].add(panel);
32523     },
32524
32525     /**
32526      * Remove a ContentPanel (or subclass) to this layout.
32527      * @param {String} target The target region key (north, south, east, west or center).
32528      * @param {Number/String/Roo.ContentPanel} panel The index, id or panel to remove
32529      * @return {Roo.ContentPanel} The removed panel
32530      */
32531     remove : function(target, panel){
32532         target = target.toLowerCase();
32533         return this.regions[target].remove(panel);
32534     },
32535
32536     /**
32537      * Searches all regions for a panel with the specified id
32538      * @param {String} panelId
32539      * @return {Roo.ContentPanel} The panel or null if it wasn't found
32540      */
32541     findPanel : function(panelId){
32542         var rs = this.regions;
32543         for(var target in rs){
32544             if(typeof rs[target] != "function"){
32545                 var p = rs[target].getPanel(panelId);
32546                 if(p){
32547                     return p;
32548                 }
32549             }
32550         }
32551         return null;
32552     },
32553
32554     /**
32555      * Searches all regions for a panel with the specified id and activates (shows) it.
32556      * @param {String/ContentPanel} panelId The panels id or the panel itself
32557      * @return {Roo.ContentPanel} The shown panel or null
32558      */
32559     showPanel : function(panelId) {
32560       var rs = this.regions;
32561       for(var target in rs){
32562          var r = rs[target];
32563          if(typeof r != "function"){
32564             if(r.hasPanel(panelId)){
32565                return r.showPanel(panelId);
32566             }
32567          }
32568       }
32569       return null;
32570    },
32571
32572    /**
32573      * Restores this layout's state using Roo.state.Manager or the state provided by the passed provider.
32574      * @param {Roo.state.Provider} provider (optional) An alternate state provider
32575      */
32576     restoreState : function(provider){
32577         if(!provider){
32578             provider = Roo.state.Manager;
32579         }
32580         var sm = new Roo.LayoutStateManager();
32581         sm.init(this, provider);
32582     },
32583
32584     /**
32585      * Adds a batch of multiple ContentPanels dynamically by passing a special regions config object.  This config
32586      * object should contain properties for each region to add ContentPanels to, and each property's value should be
32587      * a valid ContentPanel config object.  Example:
32588      * <pre><code>
32589 // Create the main layout
32590 var layout = new Roo.BorderLayout('main-ct', {
32591     west: {
32592         split:true,
32593         minSize: 175,
32594         titlebar: true
32595     },
32596     center: {
32597         title:'Components'
32598     }
32599 }, 'main-ct');
32600
32601 // Create and add multiple ContentPanels at once via configs
32602 layout.batchAdd({
32603    west: {
32604        id: 'source-files',
32605        autoCreate:true,
32606        title:'Ext Source Files',
32607        autoScroll:true,
32608        fitToFrame:true
32609    },
32610    center : {
32611        el: cview,
32612        autoScroll:true,
32613        fitToFrame:true,
32614        toolbar: tb,
32615        resizeEl:'cbody'
32616    }
32617 });
32618 </code></pre>
32619      * @param {Object} regions An object containing ContentPanel configs by region name
32620      */
32621     batchAdd : function(regions){
32622         this.beginUpdate();
32623         for(var rname in regions){
32624             var lr = this.regions[rname];
32625             if(lr){
32626                 this.addTypedPanels(lr, regions[rname]);
32627             }
32628         }
32629         this.endUpdate();
32630     },
32631
32632     // private
32633     addTypedPanels : function(lr, ps){
32634         if(typeof ps == 'string'){
32635             lr.add(new Roo.ContentPanel(ps));
32636         }
32637         else if(ps instanceof Array){
32638             for(var i =0, len = ps.length; i < len; i++){
32639                 this.addTypedPanels(lr, ps[i]);
32640             }
32641         }
32642         else if(!ps.events){ // raw config?
32643             var el = ps.el;
32644             delete ps.el; // prevent conflict
32645             lr.add(new Roo.ContentPanel(el || Roo.id(), ps));
32646         }
32647         else {  // panel object assumed!
32648             lr.add(ps);
32649         }
32650     },
32651     /**
32652      * Adds a xtype elements to the layout.
32653      * <pre><code>
32654
32655 layout.addxtype({
32656        xtype : 'ContentPanel',
32657        region: 'west',
32658        items: [ .... ]
32659    }
32660 );
32661
32662 layout.addxtype({
32663         xtype : 'NestedLayoutPanel',
32664         region: 'west',
32665         layout: {
32666            center: { },
32667            west: { }   
32668         },
32669         items : [ ... list of content panels or nested layout panels.. ]
32670    }
32671 );
32672 </code></pre>
32673      * @param {Object} cfg Xtype definition of item to add.
32674      */
32675     addxtype : function(cfg)
32676     {
32677         // basically accepts a pannel...
32678         // can accept a layout region..!?!?
32679         //Roo.log('Roo.BorderLayout add ' + cfg.xtype)
32680         
32681         if (!cfg.xtype.match(/Panel$/)) {
32682             return false;
32683         }
32684         var ret = false;
32685         
32686         if (typeof(cfg.region) == 'undefined') {
32687             Roo.log("Failed to add Panel, region was not set");
32688             Roo.log(cfg);
32689             return false;
32690         }
32691         var region = cfg.region;
32692         delete cfg.region;
32693         
32694           
32695         var xitems = [];
32696         if (cfg.items) {
32697             xitems = cfg.items;
32698             delete cfg.items;
32699         }
32700         var nb = false;
32701         
32702         switch(cfg.xtype) 
32703         {
32704             case 'ContentPanel':  // ContentPanel (el, cfg)
32705             case 'ScrollPanel':  // ContentPanel (el, cfg)
32706             case 'ViewPanel': 
32707                 if(cfg.autoCreate) {
32708                     ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
32709                 } else {
32710                     var el = this.el.createChild();
32711                     ret = new Roo[cfg.xtype](el, cfg); // new panel!!!!!
32712                 }
32713                 
32714                 this.add(region, ret);
32715                 break;
32716             
32717             
32718             case 'TreePanel': // our new panel!
32719                 cfg.el = this.el.createChild();
32720                 ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
32721                 this.add(region, ret);
32722                 break;
32723             
32724             case 'NestedLayoutPanel': 
32725                 // create a new Layout (which is  a Border Layout...
32726                 var el = this.el.createChild();
32727                 var clayout = cfg.layout;
32728                 delete cfg.layout;
32729                 clayout.items   = clayout.items  || [];
32730                 // replace this exitems with the clayout ones..
32731                 xitems = clayout.items;
32732                  
32733                 
32734                 if (region == 'center' && this.active && this.getRegion('center').panels.length < 1) {
32735                     cfg.background = false;
32736                 }
32737                 var layout = new Roo.BorderLayout(el, clayout);
32738                 
32739                 ret = new Roo[cfg.xtype](layout, cfg); // new panel!!!!!
32740                 //console.log('adding nested layout panel '  + cfg.toSource());
32741                 this.add(region, ret);
32742                 nb = {}; /// find first...
32743                 break;
32744                 
32745             case 'GridPanel': 
32746             
32747                 // needs grid and region
32748                 
32749                 //var el = this.getRegion(region).el.createChild();
32750                 var el = this.el.createChild();
32751                 // create the grid first...
32752                 
32753                 var grid = new Roo.grid[cfg.grid.xtype](el, cfg.grid);
32754                 delete cfg.grid;
32755                 if (region == 'center' && this.active ) {
32756                     cfg.background = false;
32757                 }
32758                 ret = new Roo[cfg.xtype](grid, cfg); // new panel!!!!!
32759                 
32760                 this.add(region, ret);
32761                 if (cfg.background) {
32762                     ret.on('activate', function(gp) {
32763                         if (!gp.grid.rendered) {
32764                             gp.grid.render();
32765                         }
32766                     });
32767                 } else {
32768                     grid.render();
32769                 }
32770                 break;
32771            
32772            
32773            
32774                 
32775                 
32776                 
32777             default: 
32778                 alert("Can not add '" + cfg.xtype + "' to BorderLayout");
32779                 return null;
32780              // GridPanel (grid, cfg)
32781             
32782         }
32783         this.beginUpdate();
32784         // add children..
32785         var region = '';
32786         var abn = {};
32787         Roo.each(xitems, function(i)  {
32788             region = nb && i.region ? i.region : false;
32789             
32790             var add = ret.addxtype(i);
32791            
32792             if (region) {
32793                 nb[region] = nb[region] == undefined ? 0 : nb[region]+1;
32794                 if (!i.background) {
32795                     abn[region] = nb[region] ;
32796                 }
32797             }
32798             
32799         });
32800         this.endUpdate();
32801
32802         // make the last non-background panel active..
32803         //if (nb) { Roo.log(abn); }
32804         if (nb) {
32805             
32806             for(var r in abn) {
32807                 region = this.getRegion(r);
32808                 if (region) {
32809                     // tried using nb[r], but it does not work..
32810                      
32811                     region.showPanel(abn[r]);
32812                    
32813                 }
32814             }
32815         }
32816         return ret;
32817         
32818     }
32819 });
32820
32821 /**
32822  * Shortcut for creating a new BorderLayout object and adding one or more ContentPanels to it in a single step, handling
32823  * the beginUpdate and endUpdate calls internally.  The key to this method is the <b>panels</b> property that can be
32824  * provided with each region config, which allows you to add ContentPanel configs in addition to the region configs
32825  * during creation.  The following code is equivalent to the constructor-based example at the beginning of this class:
32826  * <pre><code>
32827 // shorthand
32828 var CP = Roo.ContentPanel;
32829
32830 var layout = Roo.BorderLayout.create({
32831     north: {
32832         initialSize: 25,
32833         titlebar: false,
32834         panels: [new CP("north", "North")]
32835     },
32836     west: {
32837         split:true,
32838         initialSize: 200,
32839         minSize: 175,
32840         maxSize: 400,
32841         titlebar: true,
32842         collapsible: true,
32843         panels: [new CP("west", {title: "West"})]
32844     },
32845     east: {
32846         split:true,
32847         initialSize: 202,
32848         minSize: 175,
32849         maxSize: 400,
32850         titlebar: true,
32851         collapsible: true,
32852         panels: [new CP("autoTabs", {title: "Auto Tabs", closable: true})]
32853     },
32854     south: {
32855         split:true,
32856         initialSize: 100,
32857         minSize: 100,
32858         maxSize: 200,
32859         titlebar: true,
32860         collapsible: true,
32861         panels: [new CP("south", {title: "South", closable: true})]
32862     },
32863     center: {
32864         titlebar: true,
32865         autoScroll:true,
32866         resizeTabs: true,
32867         minTabWidth: 50,
32868         preferredTabWidth: 150,
32869         panels: [
32870             new CP("center1", {title: "Close Me", closable: true}),
32871             new CP("center2", {title: "Center Panel", closable: false})
32872         ]
32873     }
32874 }, document.body);
32875
32876 layout.getRegion("center").showPanel("center1");
32877 </code></pre>
32878  * @param config
32879  * @param targetEl
32880  */
32881 Roo.BorderLayout.create = function(config, targetEl){
32882     var layout = new Roo.BorderLayout(targetEl || document.body, config);
32883     layout.beginUpdate();
32884     var regions = Roo.BorderLayout.RegionFactory.validRegions;
32885     for(var j = 0, jlen = regions.length; j < jlen; j++){
32886         var lr = regions[j];
32887         if(layout.regions[lr] && config[lr].panels){
32888             var r = layout.regions[lr];
32889             var ps = config[lr].panels;
32890             layout.addTypedPanels(r, ps);
32891         }
32892     }
32893     layout.endUpdate();
32894     return layout;
32895 };
32896
32897 // private
32898 Roo.BorderLayout.RegionFactory = {
32899     // private
32900     validRegions : ["north","south","east","west","center"],
32901
32902     // private
32903     create : function(target, mgr, config){
32904         target = target.toLowerCase();
32905         if(config.lightweight || config.basic){
32906             return new Roo.BasicLayoutRegion(mgr, config, target);
32907         }
32908         switch(target){
32909             case "north":
32910                 return new Roo.NorthLayoutRegion(mgr, config);
32911             case "south":
32912                 return new Roo.SouthLayoutRegion(mgr, config);
32913             case "east":
32914                 return new Roo.EastLayoutRegion(mgr, config);
32915             case "west":
32916                 return new Roo.WestLayoutRegion(mgr, config);
32917             case "center":
32918                 return new Roo.CenterLayoutRegion(mgr, config);
32919         }
32920         throw 'Layout region "'+target+'" not supported.';
32921     }
32922 };/*
32923  * Based on:
32924  * Ext JS Library 1.1.1
32925  * Copyright(c) 2006-2007, Ext JS, LLC.
32926  *
32927  * Originally Released Under LGPL - original licence link has changed is not relivant.
32928  *
32929  * Fork - LGPL
32930  * <script type="text/javascript">
32931  */
32932  
32933 /**
32934  * @class Roo.BasicLayoutRegion
32935  * @extends Roo.util.Observable
32936  * This class represents a lightweight region in a layout manager. This region does not move dom nodes
32937  * and does not have a titlebar, tabs or any other features. All it does is size and position 
32938  * panels. To create a BasicLayoutRegion, add lightweight:true or basic:true to your regions config.
32939  */
32940 Roo.BasicLayoutRegion = function(mgr, config, pos, skipConfig){
32941     this.mgr = mgr;
32942     this.position  = pos;
32943     this.events = {
32944         /**
32945          * @scope Roo.BasicLayoutRegion
32946          */
32947         
32948         /**
32949          * @event beforeremove
32950          * Fires before a panel is removed (or closed). To cancel the removal set "e.cancel = true" on the event argument.
32951          * @param {Roo.LayoutRegion} this
32952          * @param {Roo.ContentPanel} panel The panel
32953          * @param {Object} e The cancel event object
32954          */
32955         "beforeremove" : true,
32956         /**
32957          * @event invalidated
32958          * Fires when the layout for this region is changed.
32959          * @param {Roo.LayoutRegion} this
32960          */
32961         "invalidated" : true,
32962         /**
32963          * @event visibilitychange
32964          * Fires when this region is shown or hidden 
32965          * @param {Roo.LayoutRegion} this
32966          * @param {Boolean} visibility true or false
32967          */
32968         "visibilitychange" : true,
32969         /**
32970          * @event paneladded
32971          * Fires when a panel is added. 
32972          * @param {Roo.LayoutRegion} this
32973          * @param {Roo.ContentPanel} panel The panel
32974          */
32975         "paneladded" : true,
32976         /**
32977          * @event panelremoved
32978          * Fires when a panel is removed. 
32979          * @param {Roo.LayoutRegion} this
32980          * @param {Roo.ContentPanel} panel The panel
32981          */
32982         "panelremoved" : true,
32983         /**
32984          * @event collapsed
32985          * Fires when this region is collapsed.
32986          * @param {Roo.LayoutRegion} this
32987          */
32988         "collapsed" : true,
32989         /**
32990          * @event expanded
32991          * Fires when this region is expanded.
32992          * @param {Roo.LayoutRegion} this
32993          */
32994         "expanded" : true,
32995         /**
32996          * @event slideshow
32997          * Fires when this region is slid into view.
32998          * @param {Roo.LayoutRegion} this
32999          */
33000         "slideshow" : true,
33001         /**
33002          * @event slidehide
33003          * Fires when this region slides out of view. 
33004          * @param {Roo.LayoutRegion} this
33005          */
33006         "slidehide" : true,
33007         /**
33008          * @event panelactivated
33009          * Fires when a panel is activated. 
33010          * @param {Roo.LayoutRegion} this
33011          * @param {Roo.ContentPanel} panel The activated panel
33012          */
33013         "panelactivated" : true,
33014         /**
33015          * @event resized
33016          * Fires when the user resizes this region. 
33017          * @param {Roo.LayoutRegion} this
33018          * @param {Number} newSize The new size (width for east/west, height for north/south)
33019          */
33020         "resized" : true
33021     };
33022     /** A collection of panels in this region. @type Roo.util.MixedCollection */
33023     this.panels = new Roo.util.MixedCollection();
33024     this.panels.getKey = this.getPanelId.createDelegate(this);
33025     this.box = null;
33026     this.activePanel = null;
33027     // ensure listeners are added...
33028     
33029     if (config.listeners || config.events) {
33030         Roo.BasicLayoutRegion.superclass.constructor.call(this, {
33031             listeners : config.listeners || {},
33032             events : config.events || {}
33033         });
33034     }
33035     
33036     if(skipConfig !== true){
33037         this.applyConfig(config);
33038     }
33039 };
33040
33041 Roo.extend(Roo.BasicLayoutRegion, Roo.util.Observable, {
33042     getPanelId : function(p){
33043         return p.getId();
33044     },
33045     
33046     applyConfig : function(config){
33047         this.margins = config.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
33048         this.config = config;
33049         
33050     },
33051     
33052     /**
33053      * Resizes the region to the specified size. For vertical regions (west, east) this adjusts 
33054      * the width, for horizontal (north, south) the height.
33055      * @param {Number} newSize The new width or height
33056      */
33057     resizeTo : function(newSize){
33058         var el = this.el ? this.el :
33059                  (this.activePanel ? this.activePanel.getEl() : null);
33060         if(el){
33061             switch(this.position){
33062                 case "east":
33063                 case "west":
33064                     el.setWidth(newSize);
33065                     this.fireEvent("resized", this, newSize);
33066                 break;
33067                 case "north":
33068                 case "south":
33069                     el.setHeight(newSize);
33070                     this.fireEvent("resized", this, newSize);
33071                 break;                
33072             }
33073         }
33074     },
33075     
33076     getBox : function(){
33077         return this.activePanel ? this.activePanel.getEl().getBox(false, true) : null;
33078     },
33079     
33080     getMargins : function(){
33081         return this.margins;
33082     },
33083     
33084     updateBox : function(box){
33085         this.box = box;
33086         var el = this.activePanel.getEl();
33087         el.dom.style.left = box.x + "px";
33088         el.dom.style.top = box.y + "px";
33089         this.activePanel.setSize(box.width, box.height);
33090     },
33091     
33092     /**
33093      * Returns the container element for this region.
33094      * @return {Roo.Element}
33095      */
33096     getEl : function(){
33097         return this.activePanel;
33098     },
33099     
33100     /**
33101      * Returns true if this region is currently visible.
33102      * @return {Boolean}
33103      */
33104     isVisible : function(){
33105         return this.activePanel ? true : false;
33106     },
33107     
33108     setActivePanel : function(panel){
33109         panel = this.getPanel(panel);
33110         if(this.activePanel && this.activePanel != panel){
33111             this.activePanel.setActiveState(false);
33112             this.activePanel.getEl().setLeftTop(-10000,-10000);
33113         }
33114         this.activePanel = panel;
33115         panel.setActiveState(true);
33116         if(this.box){
33117             panel.setSize(this.box.width, this.box.height);
33118         }
33119         this.fireEvent("panelactivated", this, panel);
33120         this.fireEvent("invalidated");
33121     },
33122     
33123     /**
33124      * Show the specified panel.
33125      * @param {Number/String/ContentPanel} panelId The panels index, id or the panel itself
33126      * @return {Roo.ContentPanel} The shown panel or null
33127      */
33128     showPanel : function(panel){
33129         if(panel = this.getPanel(panel)){
33130             this.setActivePanel(panel);
33131         }
33132         return panel;
33133     },
33134     
33135     /**
33136      * Get the active panel for this region.
33137      * @return {Roo.ContentPanel} The active panel or null
33138      */
33139     getActivePanel : function(){
33140         return this.activePanel;
33141     },
33142     
33143     /**
33144      * Add the passed ContentPanel(s)
33145      * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
33146      * @return {Roo.ContentPanel} The panel added (if only one was added)
33147      */
33148     add : function(panel){
33149         if(arguments.length > 1){
33150             for(var i = 0, len = arguments.length; i < len; i++) {
33151                 this.add(arguments[i]);
33152             }
33153             return null;
33154         }
33155         if(this.hasPanel(panel)){
33156             this.showPanel(panel);
33157             return panel;
33158         }
33159         var el = panel.getEl();
33160         if(el.dom.parentNode != this.mgr.el.dom){
33161             this.mgr.el.dom.appendChild(el.dom);
33162         }
33163         if(panel.setRegion){
33164             panel.setRegion(this);
33165         }
33166         this.panels.add(panel);
33167         el.setStyle("position", "absolute");
33168         if(!panel.background){
33169             this.setActivePanel(panel);
33170             if(this.config.initialSize && this.panels.getCount()==1){
33171                 this.resizeTo(this.config.initialSize);
33172             }
33173         }
33174         this.fireEvent("paneladded", this, panel);
33175         return panel;
33176     },
33177     
33178     /**
33179      * Returns true if the panel is in this region.
33180      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
33181      * @return {Boolean}
33182      */
33183     hasPanel : function(panel){
33184         if(typeof panel == "object"){ // must be panel obj
33185             panel = panel.getId();
33186         }
33187         return this.getPanel(panel) ? true : false;
33188     },
33189     
33190     /**
33191      * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
33192      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
33193      * @param {Boolean} preservePanel Overrides the config preservePanel option
33194      * @return {Roo.ContentPanel} The panel that was removed
33195      */
33196     remove : function(panel, preservePanel){
33197         panel = this.getPanel(panel);
33198         if(!panel){
33199             return null;
33200         }
33201         var e = {};
33202         this.fireEvent("beforeremove", this, panel, e);
33203         if(e.cancel === true){
33204             return null;
33205         }
33206         var panelId = panel.getId();
33207         this.panels.removeKey(panelId);
33208         return panel;
33209     },
33210     
33211     /**
33212      * Returns the panel specified or null if it's not in this region.
33213      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
33214      * @return {Roo.ContentPanel}
33215      */
33216     getPanel : function(id){
33217         if(typeof id == "object"){ // must be panel obj
33218             return id;
33219         }
33220         return this.panels.get(id);
33221     },
33222     
33223     /**
33224      * Returns this regions position (north/south/east/west/center).
33225      * @return {String} 
33226      */
33227     getPosition: function(){
33228         return this.position;    
33229     }
33230 });/*
33231  * Based on:
33232  * Ext JS Library 1.1.1
33233  * Copyright(c) 2006-2007, Ext JS, LLC.
33234  *
33235  * Originally Released Under LGPL - original licence link has changed is not relivant.
33236  *
33237  * Fork - LGPL
33238  * <script type="text/javascript">
33239  */
33240  
33241 /**
33242  * @class Roo.LayoutRegion
33243  * @extends Roo.BasicLayoutRegion
33244  * This class represents a region in a layout manager.
33245  * @cfg {Boolean}   collapsible     False to disable collapsing (defaults to true)
33246  * @cfg {Boolean}   collapsed       True to set the initial display to collapsed (defaults to false)
33247  * @cfg {Boolean}   floatable       False to disable floating (defaults to true)
33248  * @cfg {Object}    margins         Margins for the element (defaults to {top: 0, left: 0, right:0, bottom: 0})
33249  * @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})
33250  * @cfg {String}    tabPosition     "top" or "bottom" (defaults to "bottom")
33251  * @cfg {String}    collapsedTitle  Optional string message to display in the collapsed block of a north or south region
33252  * @cfg {Boolean}   alwaysShowTabs  True to always display tabs even when there is only 1 panel (defaults to false)
33253  * @cfg {Boolean}   autoScroll      True to enable overflow scrolling (defaults to false)
33254  * @cfg {Boolean}   titlebar        True to display a title bar (defaults to true)
33255  * @cfg {String}    title           The title for the region (overrides panel titles)
33256  * @cfg {Boolean}   animate         True to animate expand/collapse (defaults to false)
33257  * @cfg {Boolean}   autoHide        False to disable auto hiding when the mouse leaves the "floated" region (defaults to true)
33258  * @cfg {Boolean}   preservePanels  True to preserve removed panels so they can be readded later (defaults to false)
33259  * @cfg {Boolean}   closeOnTab      True to place the close icon on the tabs instead of the region titlebar (defaults to false)
33260  * @cfg {Boolean}   hideTabs        True to hide the tab strip (defaults to false)
33261  * @cfg {Boolean}   resizeTabs      True to enable automatic tab resizing. This will resize the tabs so they are all the same size and fit within
33262  *                      the space available, similar to FireFox 1.5 tabs (defaults to false)
33263  * @cfg {Number}    minTabWidth     The minimum tab width (defaults to 40)
33264  * @cfg {Number}    preferredTabWidth The preferred tab width (defaults to 150)
33265  * @cfg {Boolean}   showPin         True to show a pin button
33266  * @cfg {Boolean}   hidden          True to start the region hidden (defaults to false)
33267  * @cfg {Boolean}   hideWhenEmpty   True to hide the region when it has no panels
33268  * @cfg {Boolean}   disableTabTips  True to disable tab tooltips
33269  * @cfg {Number}    width           For East/West panels
33270  * @cfg {Number}    height          For North/South panels
33271  * @cfg {Boolean}   split           To show the splitter
33272  * @cfg {Boolean}   toolbar         xtype configuration for a toolbar - shows on right of tabbar
33273  */
33274 Roo.LayoutRegion = function(mgr, config, pos){
33275     Roo.LayoutRegion.superclass.constructor.call(this, mgr, config, pos, true);
33276     var dh = Roo.DomHelper;
33277     /** This region's container element 
33278     * @type Roo.Element */
33279     this.el = dh.append(mgr.el.dom, {tag: "div", cls: "x-layout-panel x-layout-panel-" + this.position}, true);
33280     /** This region's title element 
33281     * @type Roo.Element */
33282
33283     this.titleEl = dh.append(this.el.dom, {tag: "div", unselectable: "on", cls: "x-unselectable x-layout-panel-hd x-layout-title-"+this.position, children:[
33284         {tag: "span", cls: "x-unselectable x-layout-panel-hd-text", unselectable: "on", html: "&#160;"},
33285         {tag: "div", cls: "x-unselectable x-layout-panel-hd-tools", unselectable: "on"}
33286     ]}, true);
33287     this.titleEl.enableDisplayMode();
33288     /** This region's title text element 
33289     * @type HTMLElement */
33290     this.titleTextEl = this.titleEl.dom.firstChild;
33291     this.tools = Roo.get(this.titleEl.dom.childNodes[1], true);
33292     this.closeBtn = this.createTool(this.tools.dom, "x-layout-close");
33293     this.closeBtn.enableDisplayMode();
33294     this.closeBtn.on("click", this.closeClicked, this);
33295     this.closeBtn.hide();
33296
33297     this.createBody(config);
33298     this.visible = true;
33299     this.collapsed = false;
33300
33301     if(config.hideWhenEmpty){
33302         this.hide();
33303         this.on("paneladded", this.validateVisibility, this);
33304         this.on("panelremoved", this.validateVisibility, this);
33305     }
33306     this.applyConfig(config);
33307 };
33308
33309 Roo.extend(Roo.LayoutRegion, Roo.BasicLayoutRegion, {
33310
33311     createBody : function(){
33312         /** This region's body element 
33313         * @type Roo.Element */
33314         this.bodyEl = this.el.createChild({tag: "div", cls: "x-layout-panel-body"});
33315     },
33316
33317     applyConfig : function(c){
33318         if(c.collapsible && this.position != "center" && !this.collapsedEl){
33319             var dh = Roo.DomHelper;
33320             if(c.titlebar !== false){
33321                 this.collapseBtn = this.createTool(this.tools.dom, "x-layout-collapse-"+this.position);
33322                 this.collapseBtn.on("click", this.collapse, this);
33323                 this.collapseBtn.enableDisplayMode();
33324
33325                 if(c.showPin === true || this.showPin){
33326                     this.stickBtn = this.createTool(this.tools.dom, "x-layout-stick");
33327                     this.stickBtn.enableDisplayMode();
33328                     this.stickBtn.on("click", this.expand, this);
33329                     this.stickBtn.hide();
33330                 }
33331             }
33332             /** This region's collapsed element
33333             * @type Roo.Element */
33334             this.collapsedEl = dh.append(this.mgr.el.dom, {cls: "x-layout-collapsed x-layout-collapsed-"+this.position, children:[
33335                 {cls: "x-layout-collapsed-tools", children:[{cls: "x-layout-ctools-inner"}]}
33336             ]}, true);
33337             if(c.floatable !== false){
33338                this.collapsedEl.addClassOnOver("x-layout-collapsed-over");
33339                this.collapsedEl.on("click", this.collapseClick, this);
33340             }
33341
33342             if(c.collapsedTitle && (this.position == "north" || this.position== "south")) {
33343                 this.collapsedTitleTextEl = dh.append(this.collapsedEl.dom, {tag: "div", cls: "x-unselectable x-layout-panel-hd-text",
33344                    id: "message", unselectable: "on", style:{"float":"left"}});
33345                this.collapsedTitleTextEl.innerHTML = c.collapsedTitle;
33346              }
33347             this.expandBtn = this.createTool(this.collapsedEl.dom.firstChild.firstChild, "x-layout-expand-"+this.position);
33348             this.expandBtn.on("click", this.expand, this);
33349         }
33350         if(this.collapseBtn){
33351             this.collapseBtn.setVisible(c.collapsible == true);
33352         }
33353         this.cmargins = c.cmargins || this.cmargins ||
33354                          (this.position == "west" || this.position == "east" ?
33355                              {top: 0, left: 2, right:2, bottom: 0} :
33356                              {top: 2, left: 0, right:0, bottom: 2});
33357         this.margins = c.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
33358         this.bottomTabs = c.tabPosition != "top";
33359         this.autoScroll = c.autoScroll || false;
33360         if(this.autoScroll){
33361             this.bodyEl.setStyle("overflow", "auto");
33362         }else{
33363             this.bodyEl.setStyle("overflow", "hidden");
33364         }
33365         //if(c.titlebar !== false){
33366             if((!c.titlebar && !c.title) || c.titlebar === false){
33367                 this.titleEl.hide();
33368             }else{
33369                 this.titleEl.show();
33370                 if(c.title){
33371                     this.titleTextEl.innerHTML = c.title;
33372                 }
33373             }
33374         //}
33375         this.duration = c.duration || .30;
33376         this.slideDuration = c.slideDuration || .45;
33377         this.config = c;
33378         if(c.collapsed){
33379             this.collapse(true);
33380         }
33381         if(c.hidden){
33382             this.hide();
33383         }
33384     },
33385     /**
33386      * Returns true if this region is currently visible.
33387      * @return {Boolean}
33388      */
33389     isVisible : function(){
33390         return this.visible;
33391     },
33392
33393     /**
33394      * Updates the title for collapsed north/south regions (used with {@link #collapsedTitle} config option)
33395      * @param {String} title (optional) The title text (accepts HTML markup, defaults to the numeric character reference for a non-breaking space, "&amp;#160;")
33396      */
33397     setCollapsedTitle : function(title){
33398         title = title || "&#160;";
33399         if(this.collapsedTitleTextEl){
33400             this.collapsedTitleTextEl.innerHTML = title;
33401         }
33402     },
33403
33404     getBox : function(){
33405         var b;
33406         if(!this.collapsed){
33407             b = this.el.getBox(false, true);
33408         }else{
33409             b = this.collapsedEl.getBox(false, true);
33410         }
33411         return b;
33412     },
33413
33414     getMargins : function(){
33415         return this.collapsed ? this.cmargins : this.margins;
33416     },
33417
33418     highlight : function(){
33419         this.el.addClass("x-layout-panel-dragover");
33420     },
33421
33422     unhighlight : function(){
33423         this.el.removeClass("x-layout-panel-dragover");
33424     },
33425
33426     updateBox : function(box){
33427         this.box = box;
33428         if(!this.collapsed){
33429             this.el.dom.style.left = box.x + "px";
33430             this.el.dom.style.top = box.y + "px";
33431             this.updateBody(box.width, box.height);
33432         }else{
33433             this.collapsedEl.dom.style.left = box.x + "px";
33434             this.collapsedEl.dom.style.top = box.y + "px";
33435             this.collapsedEl.setSize(box.width, box.height);
33436         }
33437         if(this.tabs){
33438             this.tabs.autoSizeTabs();
33439         }
33440     },
33441
33442     updateBody : function(w, h){
33443         if(w !== null){
33444             this.el.setWidth(w);
33445             w -= this.el.getBorderWidth("rl");
33446             if(this.config.adjustments){
33447                 w += this.config.adjustments[0];
33448             }
33449         }
33450         if(h !== null){
33451             this.el.setHeight(h);
33452             h = this.titleEl && this.titleEl.isDisplayed() ? h - (this.titleEl.getHeight()||0) : h;
33453             h -= this.el.getBorderWidth("tb");
33454             if(this.config.adjustments){
33455                 h += this.config.adjustments[1];
33456             }
33457             this.bodyEl.setHeight(h);
33458             if(this.tabs){
33459                 h = this.tabs.syncHeight(h);
33460             }
33461         }
33462         if(this.panelSize){
33463             w = w !== null ? w : this.panelSize.width;
33464             h = h !== null ? h : this.panelSize.height;
33465         }
33466         if(this.activePanel){
33467             var el = this.activePanel.getEl();
33468             w = w !== null ? w : el.getWidth();
33469             h = h !== null ? h : el.getHeight();
33470             this.panelSize = {width: w, height: h};
33471             this.activePanel.setSize(w, h);
33472         }
33473         if(Roo.isIE && this.tabs){
33474             this.tabs.el.repaint();
33475         }
33476     },
33477
33478     /**
33479      * Returns the container element for this region.
33480      * @return {Roo.Element}
33481      */
33482     getEl : function(){
33483         return this.el;
33484     },
33485
33486     /**
33487      * Hides this region.
33488      */
33489     hide : function(){
33490         if(!this.collapsed){
33491             this.el.dom.style.left = "-2000px";
33492             this.el.hide();
33493         }else{
33494             this.collapsedEl.dom.style.left = "-2000px";
33495             this.collapsedEl.hide();
33496         }
33497         this.visible = false;
33498         this.fireEvent("visibilitychange", this, false);
33499     },
33500
33501     /**
33502      * Shows this region if it was previously hidden.
33503      */
33504     show : function(){
33505         if(!this.collapsed){
33506             this.el.show();
33507         }else{
33508             this.collapsedEl.show();
33509         }
33510         this.visible = true;
33511         this.fireEvent("visibilitychange", this, true);
33512     },
33513
33514     closeClicked : function(){
33515         if(this.activePanel){
33516             this.remove(this.activePanel);
33517         }
33518     },
33519
33520     collapseClick : function(e){
33521         if(this.isSlid){
33522            e.stopPropagation();
33523            this.slideIn();
33524         }else{
33525            e.stopPropagation();
33526            this.slideOut();
33527         }
33528     },
33529
33530     /**
33531      * Collapses this region.
33532      * @param {Boolean} skipAnim (optional) true to collapse the element without animation (if animate is true)
33533      */
33534     collapse : function(skipAnim){
33535         if(this.collapsed) return;
33536         this.collapsed = true;
33537         if(this.split){
33538             this.split.el.hide();
33539         }
33540         if(this.config.animate && skipAnim !== true){
33541             this.fireEvent("invalidated", this);
33542             this.animateCollapse();
33543         }else{
33544             this.el.setLocation(-20000,-20000);
33545             this.el.hide();
33546             this.collapsedEl.show();
33547             this.fireEvent("collapsed", this);
33548             this.fireEvent("invalidated", this);
33549         }
33550     },
33551
33552     animateCollapse : function(){
33553         // overridden
33554     },
33555
33556     /**
33557      * Expands this region if it was previously collapsed.
33558      * @param {Roo.EventObject} e The event that triggered the expand (or null if calling manually)
33559      * @param {Boolean} skipAnim (optional) true to expand the element without animation (if animate is true)
33560      */
33561     expand : function(e, skipAnim){
33562         if(e) e.stopPropagation();
33563         if(!this.collapsed || this.el.hasActiveFx()) return;
33564         if(this.isSlid){
33565             this.afterSlideIn();
33566             skipAnim = true;
33567         }
33568         this.collapsed = false;
33569         if(this.config.animate && skipAnim !== true){
33570             this.animateExpand();
33571         }else{
33572             this.el.show();
33573             if(this.split){
33574                 this.split.el.show();
33575             }
33576             this.collapsedEl.setLocation(-2000,-2000);
33577             this.collapsedEl.hide();
33578             this.fireEvent("invalidated", this);
33579             this.fireEvent("expanded", this);
33580         }
33581     },
33582
33583     animateExpand : function(){
33584         // overridden
33585     },
33586
33587     initTabs : function()
33588     {
33589         this.bodyEl.setStyle("overflow", "hidden");
33590         var ts = new Roo.TabPanel(
33591                 this.bodyEl.dom,
33592                 {
33593                     tabPosition: this.bottomTabs ? 'bottom' : 'top',
33594                     disableTooltips: this.config.disableTabTips,
33595                     toolbar : this.config.toolbar
33596                 }
33597         );
33598         if(this.config.hideTabs){
33599             ts.stripWrap.setDisplayed(false);
33600         }
33601         this.tabs = ts;
33602         ts.resizeTabs = this.config.resizeTabs === true;
33603         ts.minTabWidth = this.config.minTabWidth || 40;
33604         ts.maxTabWidth = this.config.maxTabWidth || 250;
33605         ts.preferredTabWidth = this.config.preferredTabWidth || 150;
33606         ts.monitorResize = false;
33607         ts.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden");
33608         ts.bodyEl.addClass('x-layout-tabs-body');
33609         this.panels.each(this.initPanelAsTab, this);
33610     },
33611
33612     initPanelAsTab : function(panel){
33613         var ti = this.tabs.addTab(panel.getEl().id, panel.getTitle(), null,
33614                     this.config.closeOnTab && panel.isClosable());
33615         if(panel.tabTip !== undefined){
33616             ti.setTooltip(panel.tabTip);
33617         }
33618         ti.on("activate", function(){
33619               this.setActivePanel(panel);
33620         }, this);
33621         if(this.config.closeOnTab){
33622             ti.on("beforeclose", function(t, e){
33623                 e.cancel = true;
33624                 this.remove(panel);
33625             }, this);
33626         }
33627         return ti;
33628     },
33629
33630     updatePanelTitle : function(panel, title){
33631         if(this.activePanel == panel){
33632             this.updateTitle(title);
33633         }
33634         if(this.tabs){
33635             var ti = this.tabs.getTab(panel.getEl().id);
33636             ti.setText(title);
33637             if(panel.tabTip !== undefined){
33638                 ti.setTooltip(panel.tabTip);
33639             }
33640         }
33641     },
33642
33643     updateTitle : function(title){
33644         if(this.titleTextEl && !this.config.title){
33645             this.titleTextEl.innerHTML = (typeof title != "undefined" && title.length > 0 ? title : "&#160;");
33646         }
33647     },
33648
33649     setActivePanel : function(panel){
33650         panel = this.getPanel(panel);
33651         if(this.activePanel && this.activePanel != panel){
33652             this.activePanel.setActiveState(false);
33653         }
33654         this.activePanel = panel;
33655         panel.setActiveState(true);
33656         if(this.panelSize){
33657             panel.setSize(this.panelSize.width, this.panelSize.height);
33658         }
33659         if(this.closeBtn){
33660             this.closeBtn.setVisible(!this.config.closeOnTab && !this.isSlid && panel.isClosable());
33661         }
33662         this.updateTitle(panel.getTitle());
33663         if(this.tabs){
33664             this.fireEvent("invalidated", this);
33665         }
33666         this.fireEvent("panelactivated", this, panel);
33667     },
33668
33669     /**
33670      * Shows the specified panel.
33671      * @param {Number/String/ContentPanel} panelId The panel's index, id or the panel itself
33672      * @return {Roo.ContentPanel} The shown panel, or null if a panel could not be found from panelId
33673      */
33674     showPanel : function(panel){
33675         if(panel = this.getPanel(panel)){
33676             if(this.tabs){
33677                 var tab = this.tabs.getTab(panel.getEl().id);
33678                 if(tab.isHidden()){
33679                     this.tabs.unhideTab(tab.id);
33680                 }
33681                 tab.activate();
33682             }else{
33683                 this.setActivePanel(panel);
33684             }
33685         }
33686         return panel;
33687     },
33688
33689     /**
33690      * Get the active panel for this region.
33691      * @return {Roo.ContentPanel} The active panel or null
33692      */
33693     getActivePanel : function(){
33694         return this.activePanel;
33695     },
33696
33697     validateVisibility : function(){
33698         if(this.panels.getCount() < 1){
33699             this.updateTitle("&#160;");
33700             this.closeBtn.hide();
33701             this.hide();
33702         }else{
33703             if(!this.isVisible()){
33704                 this.show();
33705             }
33706         }
33707     },
33708
33709     /**
33710      * Adds the passed ContentPanel(s) to this region.
33711      * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
33712      * @return {Roo.ContentPanel} The panel added (if only one was added; null otherwise)
33713      */
33714     add : function(panel){
33715         if(arguments.length > 1){
33716             for(var i = 0, len = arguments.length; i < len; i++) {
33717                 this.add(arguments[i]);
33718             }
33719             return null;
33720         }
33721         if(this.hasPanel(panel)){
33722             this.showPanel(panel);
33723             return panel;
33724         }
33725         panel.setRegion(this);
33726         this.panels.add(panel);
33727         if(this.panels.getCount() == 1 && !this.config.alwaysShowTabs){
33728             this.bodyEl.dom.appendChild(panel.getEl().dom);
33729             if(panel.background !== true){
33730                 this.setActivePanel(panel);
33731             }
33732             this.fireEvent("paneladded", this, panel);
33733             return panel;
33734         }
33735         if(!this.tabs){
33736             this.initTabs();
33737         }else{
33738             this.initPanelAsTab(panel);
33739         }
33740         if(panel.background !== true){
33741             this.tabs.activate(panel.getEl().id);
33742         }
33743         this.fireEvent("paneladded", this, panel);
33744         return panel;
33745     },
33746
33747     /**
33748      * Hides the tab for the specified panel.
33749      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
33750      */
33751     hidePanel : function(panel){
33752         if(this.tabs && (panel = this.getPanel(panel))){
33753             this.tabs.hideTab(panel.getEl().id);
33754         }
33755     },
33756
33757     /**
33758      * Unhides the tab for a previously hidden panel.
33759      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
33760      */
33761     unhidePanel : function(panel){
33762         if(this.tabs && (panel = this.getPanel(panel))){
33763             this.tabs.unhideTab(panel.getEl().id);
33764         }
33765     },
33766
33767     clearPanels : function(){
33768         while(this.panels.getCount() > 0){
33769              this.remove(this.panels.first());
33770         }
33771     },
33772
33773     /**
33774      * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
33775      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
33776      * @param {Boolean} preservePanel Overrides the config preservePanel option
33777      * @return {Roo.ContentPanel} The panel that was removed
33778      */
33779     remove : function(panel, preservePanel){
33780         panel = this.getPanel(panel);
33781         if(!panel){
33782             return null;
33783         }
33784         var e = {};
33785         this.fireEvent("beforeremove", this, panel, e);
33786         if(e.cancel === true){
33787             return null;
33788         }
33789         preservePanel = (typeof preservePanel != "undefined" ? preservePanel : (this.config.preservePanels === true || panel.preserve === true));
33790         var panelId = panel.getId();
33791         this.panels.removeKey(panelId);
33792         if(preservePanel){
33793             document.body.appendChild(panel.getEl().dom);
33794         }
33795         if(this.tabs){
33796             this.tabs.removeTab(panel.getEl().id);
33797         }else if (!preservePanel){
33798             this.bodyEl.dom.removeChild(panel.getEl().dom);
33799         }
33800         if(this.panels.getCount() == 1 && this.tabs && !this.config.alwaysShowTabs){
33801             var p = this.panels.first();
33802             var tempEl = document.createElement("div"); // temp holder to keep IE from deleting the node
33803             tempEl.appendChild(p.getEl().dom);
33804             this.bodyEl.update("");
33805             this.bodyEl.dom.appendChild(p.getEl().dom);
33806             tempEl = null;
33807             this.updateTitle(p.getTitle());
33808             this.tabs = null;
33809             this.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden");
33810             this.setActivePanel(p);
33811         }
33812         panel.setRegion(null);
33813         if(this.activePanel == panel){
33814             this.activePanel = null;
33815         }
33816         if(this.config.autoDestroy !== false && preservePanel !== true){
33817             try{panel.destroy();}catch(e){}
33818         }
33819         this.fireEvent("panelremoved", this, panel);
33820         return panel;
33821     },
33822
33823     /**
33824      * Returns the TabPanel component used by this region
33825      * @return {Roo.TabPanel}
33826      */
33827     getTabs : function(){
33828         return this.tabs;
33829     },
33830
33831     createTool : function(parentEl, className){
33832         var btn = Roo.DomHelper.append(parentEl, {tag: "div", cls: "x-layout-tools-button",
33833             children: [{tag: "div", cls: "x-layout-tools-button-inner " + className, html: "&#160;"}]}, true);
33834         btn.addClassOnOver("x-layout-tools-button-over");
33835         return btn;
33836     }
33837 });/*
33838  * Based on:
33839  * Ext JS Library 1.1.1
33840  * Copyright(c) 2006-2007, Ext JS, LLC.
33841  *
33842  * Originally Released Under LGPL - original licence link has changed is not relivant.
33843  *
33844  * Fork - LGPL
33845  * <script type="text/javascript">
33846  */
33847  
33848
33849
33850 /**
33851  * @class Roo.SplitLayoutRegion
33852  * @extends Roo.LayoutRegion
33853  * Adds a splitbar and other (private) useful functionality to a {@link Roo.LayoutRegion}.
33854  */
33855 Roo.SplitLayoutRegion = function(mgr, config, pos, cursor){
33856     this.cursor = cursor;
33857     Roo.SplitLayoutRegion.superclass.constructor.call(this, mgr, config, pos);
33858 };
33859
33860 Roo.extend(Roo.SplitLayoutRegion, Roo.LayoutRegion, {
33861     splitTip : "Drag to resize.",
33862     collapsibleSplitTip : "Drag to resize. Double click to hide.",
33863     useSplitTips : false,
33864
33865     applyConfig : function(config){
33866         Roo.SplitLayoutRegion.superclass.applyConfig.call(this, config);
33867         if(config.split){
33868             if(!this.split){
33869                 var splitEl = Roo.DomHelper.append(this.mgr.el.dom, 
33870                         {tag: "div", id: this.el.id + "-split", cls: "x-layout-split x-layout-split-"+this.position, html: "&#160;"});
33871                 /** The SplitBar for this region 
33872                 * @type Roo.SplitBar */
33873                 this.split = new Roo.SplitBar(splitEl, this.el, this.orientation);
33874                 this.split.on("moved", this.onSplitMove, this);
33875                 this.split.useShim = config.useShim === true;
33876                 this.split.getMaximumSize = this[this.position == 'north' || this.position == 'south' ? 'getVMaxSize' : 'getHMaxSize'].createDelegate(this);
33877                 if(this.useSplitTips){
33878                     this.split.el.dom.title = config.collapsible ? this.collapsibleSplitTip : this.splitTip;
33879                 }
33880                 if(config.collapsible){
33881                     this.split.el.on("dblclick", this.collapse,  this);
33882                 }
33883             }
33884             if(typeof config.minSize != "undefined"){
33885                 this.split.minSize = config.minSize;
33886             }
33887             if(typeof config.maxSize != "undefined"){
33888                 this.split.maxSize = config.maxSize;
33889             }
33890             if(config.hideWhenEmpty || config.hidden || config.collapsed){
33891                 this.hideSplitter();
33892             }
33893         }
33894     },
33895
33896     getHMaxSize : function(){
33897          var cmax = this.config.maxSize || 10000;
33898          var center = this.mgr.getRegion("center");
33899          return Math.min(cmax, (this.el.getWidth()+center.getEl().getWidth())-center.getMinWidth());
33900     },
33901
33902     getVMaxSize : function(){
33903          var cmax = this.config.maxSize || 10000;
33904          var center = this.mgr.getRegion("center");
33905          return Math.min(cmax, (this.el.getHeight()+center.getEl().getHeight())-center.getMinHeight());
33906     },
33907
33908     onSplitMove : function(split, newSize){
33909         this.fireEvent("resized", this, newSize);
33910     },
33911     
33912     /** 
33913      * Returns the {@link Roo.SplitBar} for this region.
33914      * @return {Roo.SplitBar}
33915      */
33916     getSplitBar : function(){
33917         return this.split;
33918     },
33919     
33920     hide : function(){
33921         this.hideSplitter();
33922         Roo.SplitLayoutRegion.superclass.hide.call(this);
33923     },
33924
33925     hideSplitter : function(){
33926         if(this.split){
33927             this.split.el.setLocation(-2000,-2000);
33928             this.split.el.hide();
33929         }
33930     },
33931
33932     show : function(){
33933         if(this.split){
33934             this.split.el.show();
33935         }
33936         Roo.SplitLayoutRegion.superclass.show.call(this);
33937     },
33938     
33939     beforeSlide: function(){
33940         if(Roo.isGecko){// firefox overflow auto bug workaround
33941             this.bodyEl.clip();
33942             if(this.tabs) this.tabs.bodyEl.clip();
33943             if(this.activePanel){
33944                 this.activePanel.getEl().clip();
33945                 
33946                 if(this.activePanel.beforeSlide){
33947                     this.activePanel.beforeSlide();
33948                 }
33949             }
33950         }
33951     },
33952     
33953     afterSlide : function(){
33954         if(Roo.isGecko){// firefox overflow auto bug workaround
33955             this.bodyEl.unclip();
33956             if(this.tabs) this.tabs.bodyEl.unclip();
33957             if(this.activePanel){
33958                 this.activePanel.getEl().unclip();
33959                 if(this.activePanel.afterSlide){
33960                     this.activePanel.afterSlide();
33961                 }
33962             }
33963         }
33964     },
33965
33966     initAutoHide : function(){
33967         if(this.autoHide !== false){
33968             if(!this.autoHideHd){
33969                 var st = new Roo.util.DelayedTask(this.slideIn, this);
33970                 this.autoHideHd = {
33971                     "mouseout": function(e){
33972                         if(!e.within(this.el, true)){
33973                             st.delay(500);
33974                         }
33975                     },
33976                     "mouseover" : function(e){
33977                         st.cancel();
33978                     },
33979                     scope : this
33980                 };
33981             }
33982             this.el.on(this.autoHideHd);
33983         }
33984     },
33985
33986     clearAutoHide : function(){
33987         if(this.autoHide !== false){
33988             this.el.un("mouseout", this.autoHideHd.mouseout);
33989             this.el.un("mouseover", this.autoHideHd.mouseover);
33990         }
33991     },
33992
33993     clearMonitor : function(){
33994         Roo.get(document).un("click", this.slideInIf, this);
33995     },
33996
33997     // these names are backwards but not changed for compat
33998     slideOut : function(){
33999         if(this.isSlid || this.el.hasActiveFx()){
34000             return;
34001         }
34002         this.isSlid = true;
34003         if(this.collapseBtn){
34004             this.collapseBtn.hide();
34005         }
34006         this.closeBtnState = this.closeBtn.getStyle('display');
34007         this.closeBtn.hide();
34008         if(this.stickBtn){
34009             this.stickBtn.show();
34010         }
34011         this.el.show();
34012         this.el.alignTo(this.collapsedEl, this.getCollapseAnchor());
34013         this.beforeSlide();
34014         this.el.setStyle("z-index", 10001);
34015         this.el.slideIn(this.getSlideAnchor(), {
34016             callback: function(){
34017                 this.afterSlide();
34018                 this.initAutoHide();
34019                 Roo.get(document).on("click", this.slideInIf, this);
34020                 this.fireEvent("slideshow", this);
34021             },
34022             scope: this,
34023             block: true
34024         });
34025     },
34026
34027     afterSlideIn : function(){
34028         this.clearAutoHide();
34029         this.isSlid = false;
34030         this.clearMonitor();
34031         this.el.setStyle("z-index", "");
34032         if(this.collapseBtn){
34033             this.collapseBtn.show();
34034         }
34035         this.closeBtn.setStyle('display', this.closeBtnState);
34036         if(this.stickBtn){
34037             this.stickBtn.hide();
34038         }
34039         this.fireEvent("slidehide", this);
34040     },
34041
34042     slideIn : function(cb){
34043         if(!this.isSlid || this.el.hasActiveFx()){
34044             Roo.callback(cb);
34045             return;
34046         }
34047         this.isSlid = false;
34048         this.beforeSlide();
34049         this.el.slideOut(this.getSlideAnchor(), {
34050             callback: function(){
34051                 this.el.setLeftTop(-10000, -10000);
34052                 this.afterSlide();
34053                 this.afterSlideIn();
34054                 Roo.callback(cb);
34055             },
34056             scope: this,
34057             block: true
34058         });
34059     },
34060     
34061     slideInIf : function(e){
34062         if(!e.within(this.el)){
34063             this.slideIn();
34064         }
34065     },
34066
34067     animateCollapse : function(){
34068         this.beforeSlide();
34069         this.el.setStyle("z-index", 20000);
34070         var anchor = this.getSlideAnchor();
34071         this.el.slideOut(anchor, {
34072             callback : function(){
34073                 this.el.setStyle("z-index", "");
34074                 this.collapsedEl.slideIn(anchor, {duration:.3});
34075                 this.afterSlide();
34076                 this.el.setLocation(-10000,-10000);
34077                 this.el.hide();
34078                 this.fireEvent("collapsed", this);
34079             },
34080             scope: this,
34081             block: true
34082         });
34083     },
34084
34085     animateExpand : function(){
34086         this.beforeSlide();
34087         this.el.alignTo(this.collapsedEl, this.getCollapseAnchor(), this.getExpandAdj());
34088         this.el.setStyle("z-index", 20000);
34089         this.collapsedEl.hide({
34090             duration:.1
34091         });
34092         this.el.slideIn(this.getSlideAnchor(), {
34093             callback : function(){
34094                 this.el.setStyle("z-index", "");
34095                 this.afterSlide();
34096                 if(this.split){
34097                     this.split.el.show();
34098                 }
34099                 this.fireEvent("invalidated", this);
34100                 this.fireEvent("expanded", this);
34101             },
34102             scope: this,
34103             block: true
34104         });
34105     },
34106
34107     anchors : {
34108         "west" : "left",
34109         "east" : "right",
34110         "north" : "top",
34111         "south" : "bottom"
34112     },
34113
34114     sanchors : {
34115         "west" : "l",
34116         "east" : "r",
34117         "north" : "t",
34118         "south" : "b"
34119     },
34120
34121     canchors : {
34122         "west" : "tl-tr",
34123         "east" : "tr-tl",
34124         "north" : "tl-bl",
34125         "south" : "bl-tl"
34126     },
34127
34128     getAnchor : function(){
34129         return this.anchors[this.position];
34130     },
34131
34132     getCollapseAnchor : function(){
34133         return this.canchors[this.position];
34134     },
34135
34136     getSlideAnchor : function(){
34137         return this.sanchors[this.position];
34138     },
34139
34140     getAlignAdj : function(){
34141         var cm = this.cmargins;
34142         switch(this.position){
34143             case "west":
34144                 return [0, 0];
34145             break;
34146             case "east":
34147                 return [0, 0];
34148             break;
34149             case "north":
34150                 return [0, 0];
34151             break;
34152             case "south":
34153                 return [0, 0];
34154             break;
34155         }
34156     },
34157
34158     getExpandAdj : function(){
34159         var c = this.collapsedEl, cm = this.cmargins;
34160         switch(this.position){
34161             case "west":
34162                 return [-(cm.right+c.getWidth()+cm.left), 0];
34163             break;
34164             case "east":
34165                 return [cm.right+c.getWidth()+cm.left, 0];
34166             break;
34167             case "north":
34168                 return [0, -(cm.top+cm.bottom+c.getHeight())];
34169             break;
34170             case "south":
34171                 return [0, cm.top+cm.bottom+c.getHeight()];
34172             break;
34173         }
34174     }
34175 });/*
34176  * Based on:
34177  * Ext JS Library 1.1.1
34178  * Copyright(c) 2006-2007, Ext JS, LLC.
34179  *
34180  * Originally Released Under LGPL - original licence link has changed is not relivant.
34181  *
34182  * Fork - LGPL
34183  * <script type="text/javascript">
34184  */
34185 /*
34186  * These classes are private internal classes
34187  */
34188 Roo.CenterLayoutRegion = function(mgr, config){
34189     Roo.LayoutRegion.call(this, mgr, config, "center");
34190     this.visible = true;
34191     this.minWidth = config.minWidth || 20;
34192     this.minHeight = config.minHeight || 20;
34193 };
34194
34195 Roo.extend(Roo.CenterLayoutRegion, Roo.LayoutRegion, {
34196     hide : function(){
34197         // center panel can't be hidden
34198     },
34199     
34200     show : function(){
34201         // center panel can't be hidden
34202     },
34203     
34204     getMinWidth: function(){
34205         return this.minWidth;
34206     },
34207     
34208     getMinHeight: function(){
34209         return this.minHeight;
34210     }
34211 });
34212
34213
34214 Roo.NorthLayoutRegion = function(mgr, config){
34215     Roo.LayoutRegion.call(this, mgr, config, "north", "n-resize");
34216     if(this.split){
34217         this.split.placement = Roo.SplitBar.TOP;
34218         this.split.orientation = Roo.SplitBar.VERTICAL;
34219         this.split.el.addClass("x-layout-split-v");
34220     }
34221     var size = config.initialSize || config.height;
34222     if(typeof size != "undefined"){
34223         this.el.setHeight(size);
34224     }
34225 };
34226 Roo.extend(Roo.NorthLayoutRegion, Roo.SplitLayoutRegion, {
34227     orientation: Roo.SplitBar.VERTICAL,
34228     getBox : function(){
34229         if(this.collapsed){
34230             return this.collapsedEl.getBox();
34231         }
34232         var box = this.el.getBox();
34233         if(this.split){
34234             box.height += this.split.el.getHeight();
34235         }
34236         return box;
34237     },
34238     
34239     updateBox : function(box){
34240         if(this.split && !this.collapsed){
34241             box.height -= this.split.el.getHeight();
34242             this.split.el.setLeft(box.x);
34243             this.split.el.setTop(box.y+box.height);
34244             this.split.el.setWidth(box.width);
34245         }
34246         if(this.collapsed){
34247             this.updateBody(box.width, null);
34248         }
34249         Roo.LayoutRegion.prototype.updateBox.call(this, box);
34250     }
34251 });
34252
34253 Roo.SouthLayoutRegion = function(mgr, config){
34254     Roo.SplitLayoutRegion.call(this, mgr, config, "south", "s-resize");
34255     if(this.split){
34256         this.split.placement = Roo.SplitBar.BOTTOM;
34257         this.split.orientation = Roo.SplitBar.VERTICAL;
34258         this.split.el.addClass("x-layout-split-v");
34259     }
34260     var size = config.initialSize || config.height;
34261     if(typeof size != "undefined"){
34262         this.el.setHeight(size);
34263     }
34264 };
34265 Roo.extend(Roo.SouthLayoutRegion, Roo.SplitLayoutRegion, {
34266     orientation: Roo.SplitBar.VERTICAL,
34267     getBox : function(){
34268         if(this.collapsed){
34269             return this.collapsedEl.getBox();
34270         }
34271         var box = this.el.getBox();
34272         if(this.split){
34273             var sh = this.split.el.getHeight();
34274             box.height += sh;
34275             box.y -= sh;
34276         }
34277         return box;
34278     },
34279     
34280     updateBox : function(box){
34281         if(this.split && !this.collapsed){
34282             var sh = this.split.el.getHeight();
34283             box.height -= sh;
34284             box.y += sh;
34285             this.split.el.setLeft(box.x);
34286             this.split.el.setTop(box.y-sh);
34287             this.split.el.setWidth(box.width);
34288         }
34289         if(this.collapsed){
34290             this.updateBody(box.width, null);
34291         }
34292         Roo.LayoutRegion.prototype.updateBox.call(this, box);
34293     }
34294 });
34295
34296 Roo.EastLayoutRegion = function(mgr, config){
34297     Roo.SplitLayoutRegion.call(this, mgr, config, "east", "e-resize");
34298     if(this.split){
34299         this.split.placement = Roo.SplitBar.RIGHT;
34300         this.split.orientation = Roo.SplitBar.HORIZONTAL;
34301         this.split.el.addClass("x-layout-split-h");
34302     }
34303     var size = config.initialSize || config.width;
34304     if(typeof size != "undefined"){
34305         this.el.setWidth(size);
34306     }
34307 };
34308 Roo.extend(Roo.EastLayoutRegion, Roo.SplitLayoutRegion, {
34309     orientation: Roo.SplitBar.HORIZONTAL,
34310     getBox : function(){
34311         if(this.collapsed){
34312             return this.collapsedEl.getBox();
34313         }
34314         var box = this.el.getBox();
34315         if(this.split){
34316             var sw = this.split.el.getWidth();
34317             box.width += sw;
34318             box.x -= sw;
34319         }
34320         return box;
34321     },
34322
34323     updateBox : function(box){
34324         if(this.split && !this.collapsed){
34325             var sw = this.split.el.getWidth();
34326             box.width -= sw;
34327             this.split.el.setLeft(box.x);
34328             this.split.el.setTop(box.y);
34329             this.split.el.setHeight(box.height);
34330             box.x += sw;
34331         }
34332         if(this.collapsed){
34333             this.updateBody(null, box.height);
34334         }
34335         Roo.LayoutRegion.prototype.updateBox.call(this, box);
34336     }
34337 });
34338
34339 Roo.WestLayoutRegion = function(mgr, config){
34340     Roo.SplitLayoutRegion.call(this, mgr, config, "west", "w-resize");
34341     if(this.split){
34342         this.split.placement = Roo.SplitBar.LEFT;
34343         this.split.orientation = Roo.SplitBar.HORIZONTAL;
34344         this.split.el.addClass("x-layout-split-h");
34345     }
34346     var size = config.initialSize || config.width;
34347     if(typeof size != "undefined"){
34348         this.el.setWidth(size);
34349     }
34350 };
34351 Roo.extend(Roo.WestLayoutRegion, Roo.SplitLayoutRegion, {
34352     orientation: Roo.SplitBar.HORIZONTAL,
34353     getBox : function(){
34354         if(this.collapsed){
34355             return this.collapsedEl.getBox();
34356         }
34357         var box = this.el.getBox();
34358         if(this.split){
34359             box.width += this.split.el.getWidth();
34360         }
34361         return box;
34362     },
34363     
34364     updateBox : function(box){
34365         if(this.split && !this.collapsed){
34366             var sw = this.split.el.getWidth();
34367             box.width -= sw;
34368             this.split.el.setLeft(box.x+box.width);
34369             this.split.el.setTop(box.y);
34370             this.split.el.setHeight(box.height);
34371         }
34372         if(this.collapsed){
34373             this.updateBody(null, box.height);
34374         }
34375         Roo.LayoutRegion.prototype.updateBox.call(this, box);
34376     }
34377 });
34378 /*
34379  * Based on:
34380  * Ext JS Library 1.1.1
34381  * Copyright(c) 2006-2007, Ext JS, LLC.
34382  *
34383  * Originally Released Under LGPL - original licence link has changed is not relivant.
34384  *
34385  * Fork - LGPL
34386  * <script type="text/javascript">
34387  */
34388  
34389  
34390 /*
34391  * Private internal class for reading and applying state
34392  */
34393 Roo.LayoutStateManager = function(layout){
34394      // default empty state
34395      this.state = {
34396         north: {},
34397         south: {},
34398         east: {},
34399         west: {}       
34400     };
34401 };
34402
34403 Roo.LayoutStateManager.prototype = {
34404     init : function(layout, provider){
34405         this.provider = provider;
34406         var state = provider.get(layout.id+"-layout-state");
34407         if(state){
34408             var wasUpdating = layout.isUpdating();
34409             if(!wasUpdating){
34410                 layout.beginUpdate();
34411             }
34412             for(var key in state){
34413                 if(typeof state[key] != "function"){
34414                     var rstate = state[key];
34415                     var r = layout.getRegion(key);
34416                     if(r && rstate){
34417                         if(rstate.size){
34418                             r.resizeTo(rstate.size);
34419                         }
34420                         if(rstate.collapsed == true){
34421                             r.collapse(true);
34422                         }else{
34423                             r.expand(null, true);
34424                         }
34425                     }
34426                 }
34427             }
34428             if(!wasUpdating){
34429                 layout.endUpdate();
34430             }
34431             this.state = state; 
34432         }
34433         this.layout = layout;
34434         layout.on("regionresized", this.onRegionResized, this);
34435         layout.on("regioncollapsed", this.onRegionCollapsed, this);
34436         layout.on("regionexpanded", this.onRegionExpanded, this);
34437     },
34438     
34439     storeState : function(){
34440         this.provider.set(this.layout.id+"-layout-state", this.state);
34441     },
34442     
34443     onRegionResized : function(region, newSize){
34444         this.state[region.getPosition()].size = newSize;
34445         this.storeState();
34446     },
34447     
34448     onRegionCollapsed : function(region){
34449         this.state[region.getPosition()].collapsed = true;
34450         this.storeState();
34451     },
34452     
34453     onRegionExpanded : function(region){
34454         this.state[region.getPosition()].collapsed = false;
34455         this.storeState();
34456     }
34457 };/*
34458  * Based on:
34459  * Ext JS Library 1.1.1
34460  * Copyright(c) 2006-2007, Ext JS, LLC.
34461  *
34462  * Originally Released Under LGPL - original licence link has changed is not relivant.
34463  *
34464  * Fork - LGPL
34465  * <script type="text/javascript">
34466  */
34467 /**
34468  * @class Roo.ContentPanel
34469  * @extends Roo.util.Observable
34470  * A basic ContentPanel element.
34471  * @cfg {Boolean}   fitToFrame    True for this panel to adjust its size to fit when the region resizes  (defaults to false)
34472  * @cfg {Boolean}   fitContainer   When using {@link #fitToFrame} and {@link #resizeEl}, you can also fit the parent container  (defaults to false)
34473  * @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
34474  * @cfg {Boolean}   closable      True if the panel can be closed/removed
34475  * @cfg {Boolean}   background    True if the panel should not be activated when it is added (defaults to false)
34476  * @cfg {String/HTMLElement/Element} resizeEl An element to resize if {@link #fitToFrame} is true (instead of this panel's element)
34477  * @cfg {Toolbar}   toolbar       A toolbar for this panel
34478  * @cfg {Boolean} autoScroll    True to scroll overflow in this panel (use with {@link #fitToFrame})
34479  * @cfg {String} title          The title for this panel
34480  * @cfg {Array} adjustments     Values to <b>add</b> to the width/height when doing a {@link #fitToFrame} (default is [0, 0])
34481  * @cfg {String} url            Calls {@link #setUrl} with this value
34482  * @cfg {String} region         (center|north|south|east|west) which region to put this panel on (when used with xtype constructors)
34483  * @cfg {String/Object} params  When used with {@link #url}, calls {@link #setUrl} with this value
34484  * @cfg {Boolean} loadOnce      When used with {@link #url}, calls {@link #setUrl} with this value
34485  * @cfg {String}    content        Raw content to fill content panel with (uses setContent on construction.)
34486
34487  * @constructor
34488  * Create a new ContentPanel.
34489  * @param {String/HTMLElement/Roo.Element} el The container element for this panel
34490  * @param {String/Object} config A string to set only the title or a config object
34491  * @param {String} content (optional) Set the HTML content for this panel
34492  * @param {String} region (optional) Used by xtype constructors to add to regions. (values center,east,west,south,north)
34493  */
34494 Roo.ContentPanel = function(el, config, content){
34495     
34496      
34497     /*
34498     if(el.autoCreate || el.xtype){ // xtype is available if this is called from factory
34499         config = el;
34500         el = Roo.id();
34501     }
34502     if (config && config.parentLayout) { 
34503         el = config.parentLayout.el.createChild(); 
34504     }
34505     */
34506     if(el.autoCreate){ // xtype is available if this is called from factory
34507         config = el;
34508         el = Roo.id();
34509     }
34510     this.el = Roo.get(el);
34511     if(!this.el && config && config.autoCreate){
34512         if(typeof config.autoCreate == "object"){
34513             if(!config.autoCreate.id){
34514                 config.autoCreate.id = config.id||el;
34515             }
34516             this.el = Roo.DomHelper.append(document.body,
34517                         config.autoCreate, true);
34518         }else{
34519             this.el = Roo.DomHelper.append(document.body,
34520                         {tag: "div", cls: "x-layout-inactive-content", id: config.id||el}, true);
34521         }
34522     }
34523     this.closable = false;
34524     this.loaded = false;
34525     this.active = false;
34526     if(typeof config == "string"){
34527         this.title = config;
34528     }else{
34529         Roo.apply(this, config);
34530     }
34531     
34532     if (this.toolbar && !this.toolbar.el && this.toolbar.xtype) {
34533         this.wrapEl = this.el.wrap();
34534         this.toolbar.container = this.el.insertSibling(false, 'before');
34535         this.toolbar = new Roo.Toolbar(this.toolbar);
34536     }
34537     
34538     // xtype created footer. - not sure if will work as we normally have to render first..
34539     if (this.footer && !this.footer.el && this.footer.xtype) {
34540         if (!this.wrapEl) {
34541             this.wrapEl = this.el.wrap();
34542         }
34543     
34544         this.footer.container = this.wrapEl.createChild();
34545          
34546         this.footer = Roo.factory(this.footer, Roo);
34547         
34548     }
34549     
34550     if(this.resizeEl){
34551         this.resizeEl = Roo.get(this.resizeEl, true);
34552     }else{
34553         this.resizeEl = this.el;
34554     }
34555     // handle view.xtype
34556     
34557  
34558     
34559     
34560     this.addEvents({
34561         /**
34562          * @event activate
34563          * Fires when this panel is activated. 
34564          * @param {Roo.ContentPanel} this
34565          */
34566         "activate" : true,
34567         /**
34568          * @event deactivate
34569          * Fires when this panel is activated. 
34570          * @param {Roo.ContentPanel} this
34571          */
34572         "deactivate" : true,
34573
34574         /**
34575          * @event resize
34576          * Fires when this panel is resized if fitToFrame is true.
34577          * @param {Roo.ContentPanel} this
34578          * @param {Number} width The width after any component adjustments
34579          * @param {Number} height The height after any component adjustments
34580          */
34581         "resize" : true,
34582         
34583          /**
34584          * @event render
34585          * Fires when this tab is created
34586          * @param {Roo.ContentPanel} this
34587          */
34588         "render" : true
34589         
34590         
34591         
34592     });
34593     
34594
34595     
34596     
34597     if(this.autoScroll){
34598         this.resizeEl.setStyle("overflow", "auto");
34599     } else {
34600         // fix randome scrolling
34601         this.el.on('scroll', function() {
34602             Roo.log('fix random scolling');
34603             this.scrollTo('top',0); 
34604         });
34605     }
34606     content = content || this.content;
34607     if(content){
34608         this.setContent(content);
34609     }
34610     if(config && config.url){
34611         this.setUrl(this.url, this.params, this.loadOnce);
34612     }
34613     
34614     
34615     
34616     Roo.ContentPanel.superclass.constructor.call(this);
34617     
34618     if (this.view && typeof(this.view.xtype) != 'undefined') {
34619         this.view.el = this.el.appendChild(document.createElement("div"));
34620         this.view = Roo.factory(this.view); 
34621         this.view.render  &&  this.view.render(false, '');  
34622     }
34623     
34624     
34625     this.fireEvent('render', this);
34626 };
34627
34628 Roo.extend(Roo.ContentPanel, Roo.util.Observable, {
34629     tabTip:'',
34630     setRegion : function(region){
34631         this.region = region;
34632         if(region){
34633            this.el.replaceClass("x-layout-inactive-content", "x-layout-active-content");
34634         }else{
34635            this.el.replaceClass("x-layout-active-content", "x-layout-inactive-content");
34636         } 
34637     },
34638     
34639     /**
34640      * Returns the toolbar for this Panel if one was configured. 
34641      * @return {Roo.Toolbar} 
34642      */
34643     getToolbar : function(){
34644         return this.toolbar;
34645     },
34646     
34647     setActiveState : function(active){
34648         this.active = active;
34649         if(!active){
34650             this.fireEvent("deactivate", this);
34651         }else{
34652             this.fireEvent("activate", this);
34653         }
34654     },
34655     /**
34656      * Updates this panel's element
34657      * @param {String} content The new content
34658      * @param {Boolean} loadScripts (optional) true to look for and process scripts
34659     */
34660     setContent : function(content, loadScripts){
34661         this.el.update(content, loadScripts);
34662     },
34663
34664     ignoreResize : function(w, h){
34665         if(this.lastSize && this.lastSize.width == w && this.lastSize.height == h){
34666             return true;
34667         }else{
34668             this.lastSize = {width: w, height: h};
34669             return false;
34670         }
34671     },
34672     /**
34673      * Get the {@link Roo.UpdateManager} for this panel. Enables you to perform Ajax updates.
34674      * @return {Roo.UpdateManager} The UpdateManager
34675      */
34676     getUpdateManager : function(){
34677         return this.el.getUpdateManager();
34678     },
34679      /**
34680      * Loads this content panel immediately with content from XHR. Note: to delay loading until the panel is activated, use {@link #setUrl}.
34681      * @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:
34682 <pre><code>
34683 panel.load({
34684     url: "your-url.php",
34685     params: {param1: "foo", param2: "bar"}, // or a URL encoded string
34686     callback: yourFunction,
34687     scope: yourObject, //(optional scope)
34688     discardUrl: false,
34689     nocache: false,
34690     text: "Loading...",
34691     timeout: 30,
34692     scripts: false
34693 });
34694 </code></pre>
34695      * The only required property is <i>url</i>. The optional properties <i>nocache</i>, <i>text</i> and <i>scripts</i>
34696      * 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.
34697      * @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}
34698      * @param {Function} callback (optional) Callback when transaction is complete -- called with signature (oElement, bSuccess, oResponse)
34699      * @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.
34700      * @return {Roo.ContentPanel} this
34701      */
34702     load : function(){
34703         var um = this.el.getUpdateManager();
34704         um.update.apply(um, arguments);
34705         return this;
34706     },
34707
34708
34709     /**
34710      * 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.
34711      * @param {String/Function} url The URL to load the content from or a function to call to get the URL
34712      * @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)
34713      * @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)
34714      * @return {Roo.UpdateManager} The UpdateManager
34715      */
34716     setUrl : function(url, params, loadOnce){
34717         if(this.refreshDelegate){
34718             this.removeListener("activate", this.refreshDelegate);
34719         }
34720         this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
34721         this.on("activate", this.refreshDelegate);
34722         return this.el.getUpdateManager();
34723     },
34724     
34725     _handleRefresh : function(url, params, loadOnce){
34726         if(!loadOnce || !this.loaded){
34727             var updater = this.el.getUpdateManager();
34728             updater.update(url, params, this._setLoaded.createDelegate(this));
34729         }
34730     },
34731     
34732     _setLoaded : function(){
34733         this.loaded = true;
34734     }, 
34735     
34736     /**
34737      * Returns this panel's id
34738      * @return {String} 
34739      */
34740     getId : function(){
34741         return this.el.id;
34742     },
34743     
34744     /** 
34745      * Returns this panel's element - used by regiosn to add.
34746      * @return {Roo.Element} 
34747      */
34748     getEl : function(){
34749         return this.wrapEl || this.el;
34750     },
34751     
34752     adjustForComponents : function(width, height)
34753     {
34754         //Roo.log('adjustForComponents ');
34755         if(this.resizeEl != this.el){
34756             width -= this.el.getFrameWidth('lr');
34757             height -= this.el.getFrameWidth('tb');
34758         }
34759         if(this.toolbar){
34760             var te = this.toolbar.getEl();
34761             height -= te.getHeight();
34762             te.setWidth(width);
34763         }
34764         if(this.footer){
34765             var te = this.footer.getEl();
34766             Roo.log("footer:" + te.getHeight());
34767             
34768             height -= te.getHeight();
34769             te.setWidth(width);
34770         }
34771         
34772         
34773         if(this.adjustments){
34774             width += this.adjustments[0];
34775             height += this.adjustments[1];
34776         }
34777         return {"width": width, "height": height};
34778     },
34779     
34780     setSize : function(width, height){
34781         if(this.fitToFrame && !this.ignoreResize(width, height)){
34782             if(this.fitContainer && this.resizeEl != this.el){
34783                 this.el.setSize(width, height);
34784             }
34785             var size = this.adjustForComponents(width, height);
34786             this.resizeEl.setSize(this.autoWidth ? "auto" : size.width, this.autoHeight ? "auto" : size.height);
34787             this.fireEvent('resize', this, size.width, size.height);
34788         }
34789     },
34790     
34791     /**
34792      * Returns this panel's title
34793      * @return {String} 
34794      */
34795     getTitle : function(){
34796         return this.title;
34797     },
34798     
34799     /**
34800      * Set this panel's title
34801      * @param {String} title
34802      */
34803     setTitle : function(title){
34804         this.title = title;
34805         if(this.region){
34806             this.region.updatePanelTitle(this, title);
34807         }
34808     },
34809     
34810     /**
34811      * Returns true is this panel was configured to be closable
34812      * @return {Boolean} 
34813      */
34814     isClosable : function(){
34815         return this.closable;
34816     },
34817     
34818     beforeSlide : function(){
34819         this.el.clip();
34820         this.resizeEl.clip();
34821     },
34822     
34823     afterSlide : function(){
34824         this.el.unclip();
34825         this.resizeEl.unclip();
34826     },
34827     
34828     /**
34829      *   Force a content refresh from the URL specified in the {@link #setUrl} method.
34830      *   Will fail silently if the {@link #setUrl} method has not been called.
34831      *   This does not activate the panel, just updates its content.
34832      */
34833     refresh : function(){
34834         if(this.refreshDelegate){
34835            this.loaded = false;
34836            this.refreshDelegate();
34837         }
34838     },
34839     
34840     /**
34841      * Destroys this panel
34842      */
34843     destroy : function(){
34844         this.el.removeAllListeners();
34845         var tempEl = document.createElement("span");
34846         tempEl.appendChild(this.el.dom);
34847         tempEl.innerHTML = "";
34848         this.el.remove();
34849         this.el = null;
34850     },
34851     
34852     /**
34853      * form - if the content panel contains a form - this is a reference to it.
34854      * @type {Roo.form.Form}
34855      */
34856     form : false,
34857     /**
34858      * view - if the content panel contains a view (Roo.DatePicker / Roo.View / Roo.JsonView)
34859      *    This contains a reference to it.
34860      * @type {Roo.View}
34861      */
34862     view : false,
34863     
34864       /**
34865      * Adds a xtype elements to the panel - currently only supports Forms, View, JsonView.
34866      * <pre><code>
34867
34868 layout.addxtype({
34869        xtype : 'Form',
34870        items: [ .... ]
34871    }
34872 );
34873
34874 </code></pre>
34875      * @param {Object} cfg Xtype definition of item to add.
34876      */
34877     
34878     addxtype : function(cfg) {
34879         // add form..
34880         if (cfg.xtype.match(/^Form$/)) {
34881             
34882             var el;
34883             //if (this.footer) {
34884             //    el = this.footer.container.insertSibling(false, 'before');
34885             //} else {
34886                 el = this.el.createChild();
34887             //}
34888
34889             this.form = new  Roo.form.Form(cfg);
34890             
34891             
34892             if ( this.form.allItems.length) this.form.render(el.dom);
34893             return this.form;
34894         }
34895         // should only have one of theses..
34896         if ([ 'View', 'JsonView', 'DatePicker'].indexOf(cfg.xtype) > -1) {
34897             // views.. should not be just added - used named prop 'view''
34898             
34899             cfg.el = this.el.appendChild(document.createElement("div"));
34900             // factory?
34901             
34902             var ret = new Roo.factory(cfg);
34903              
34904              ret.render && ret.render(false, ''); // render blank..
34905             this.view = ret;
34906             return ret;
34907         }
34908         return false;
34909     }
34910 });
34911
34912 /**
34913  * @class Roo.GridPanel
34914  * @extends Roo.ContentPanel
34915  * @constructor
34916  * Create a new GridPanel.
34917  * @param {Roo.grid.Grid} grid The grid for this panel
34918  * @param {String/Object} config A string to set only the panel's title, or a config object
34919  */
34920 Roo.GridPanel = function(grid, config){
34921     
34922   
34923     this.wrapper = Roo.DomHelper.append(document.body, // wrapper for IE7 strict & safari scroll issue
34924         {tag: "div", cls: "x-layout-grid-wrapper x-layout-inactive-content"}, true);
34925         
34926     this.wrapper.dom.appendChild(grid.getGridEl().dom);
34927     
34928     Roo.GridPanel.superclass.constructor.call(this, this.wrapper, config);
34929     
34930     if(this.toolbar){
34931         this.toolbar.el.insertBefore(this.wrapper.dom.firstChild);
34932     }
34933     // xtype created footer. - not sure if will work as we normally have to render first..
34934     if (this.footer && !this.footer.el && this.footer.xtype) {
34935         
34936         this.footer.container = this.grid.getView().getFooterPanel(true);
34937         this.footer.dataSource = this.grid.dataSource;
34938         this.footer = Roo.factory(this.footer, Roo);
34939         
34940     }
34941     
34942     grid.monitorWindowResize = false; // turn off autosizing
34943     grid.autoHeight = false;
34944     grid.autoWidth = false;
34945     this.grid = grid;
34946     this.grid.getGridEl().replaceClass("x-layout-inactive-content", "x-layout-component-panel");
34947 };
34948
34949 Roo.extend(Roo.GridPanel, Roo.ContentPanel, {
34950     getId : function(){
34951         return this.grid.id;
34952     },
34953     
34954     /**
34955      * Returns the grid for this panel
34956      * @return {Roo.grid.Grid} 
34957      */
34958     getGrid : function(){
34959         return this.grid;    
34960     },
34961     
34962     setSize : function(width, height){
34963         if(!this.ignoreResize(width, height)){
34964             var grid = this.grid;
34965             var size = this.adjustForComponents(width, height);
34966             grid.getGridEl().setSize(size.width, size.height);
34967             grid.autoSize();
34968         }
34969     },
34970     
34971     beforeSlide : function(){
34972         this.grid.getView().scroller.clip();
34973     },
34974     
34975     afterSlide : function(){
34976         this.grid.getView().scroller.unclip();
34977     },
34978     
34979     destroy : function(){
34980         this.grid.destroy();
34981         delete this.grid;
34982         Roo.GridPanel.superclass.destroy.call(this); 
34983     }
34984 });
34985
34986
34987 /**
34988  * @class Roo.NestedLayoutPanel
34989  * @extends Roo.ContentPanel
34990  * @constructor
34991  * Create a new NestedLayoutPanel.
34992  * 
34993  * 
34994  * @param {Roo.BorderLayout} layout The layout for this panel
34995  * @param {String/Object} config A string to set only the title or a config object
34996  */
34997 Roo.NestedLayoutPanel = function(layout, config)
34998 {
34999     // construct with only one argument..
35000     /* FIXME - implement nicer consturctors
35001     if (layout.layout) {
35002         config = layout;
35003         layout = config.layout;
35004         delete config.layout;
35005     }
35006     if (layout.xtype && !layout.getEl) {
35007         // then layout needs constructing..
35008         layout = Roo.factory(layout, Roo);
35009     }
35010     */
35011     
35012     
35013     Roo.NestedLayoutPanel.superclass.constructor.call(this, layout.getEl(), config);
35014     
35015     layout.monitorWindowResize = false; // turn off autosizing
35016     this.layout = layout;
35017     this.layout.getEl().addClass("x-layout-nested-layout");
35018     
35019     
35020     
35021     
35022 };
35023
35024 Roo.extend(Roo.NestedLayoutPanel, Roo.ContentPanel, {
35025
35026     setSize : function(width, height){
35027         if(!this.ignoreResize(width, height)){
35028             var size = this.adjustForComponents(width, height);
35029             var el = this.layout.getEl();
35030             el.setSize(size.width, size.height);
35031             var touch = el.dom.offsetWidth;
35032             this.layout.layout();
35033             // ie requires a double layout on the first pass
35034             if(Roo.isIE && !this.initialized){
35035                 this.initialized = true;
35036                 this.layout.layout();
35037             }
35038         }
35039     },
35040     
35041     // activate all subpanels if not currently active..
35042     
35043     setActiveState : function(active){
35044         this.active = active;
35045         if(!active){
35046             this.fireEvent("deactivate", this);
35047             return;
35048         }
35049         
35050         this.fireEvent("activate", this);
35051         // not sure if this should happen before or after..
35052         if (!this.layout) {
35053             return; // should not happen..
35054         }
35055         var reg = false;
35056         for (var r in this.layout.regions) {
35057             reg = this.layout.getRegion(r);
35058             if (reg.getActivePanel()) {
35059                 //reg.showPanel(reg.getActivePanel()); // force it to activate.. 
35060                 reg.setActivePanel(reg.getActivePanel());
35061                 continue;
35062             }
35063             if (!reg.panels.length) {
35064                 continue;
35065             }
35066             reg.showPanel(reg.getPanel(0));
35067         }
35068         
35069         
35070         
35071         
35072     },
35073     
35074     /**
35075      * Returns the nested BorderLayout for this panel
35076      * @return {Roo.BorderLayout} 
35077      */
35078     getLayout : function(){
35079         return this.layout;
35080     },
35081     
35082      /**
35083      * Adds a xtype elements to the layout of the nested panel
35084      * <pre><code>
35085
35086 panel.addxtype({
35087        xtype : 'ContentPanel',
35088        region: 'west',
35089        items: [ .... ]
35090    }
35091 );
35092
35093 panel.addxtype({
35094         xtype : 'NestedLayoutPanel',
35095         region: 'west',
35096         layout: {
35097            center: { },
35098            west: { }   
35099         },
35100         items : [ ... list of content panels or nested layout panels.. ]
35101    }
35102 );
35103 </code></pre>
35104      * @param {Object} cfg Xtype definition of item to add.
35105      */
35106     addxtype : function(cfg) {
35107         return this.layout.addxtype(cfg);
35108     
35109     }
35110 });
35111
35112 Roo.ScrollPanel = function(el, config, content){
35113     config = config || {};
35114     config.fitToFrame = true;
35115     Roo.ScrollPanel.superclass.constructor.call(this, el, config, content);
35116     
35117     this.el.dom.style.overflow = "hidden";
35118     var wrap = this.el.wrap({cls: "x-scroller x-layout-inactive-content"});
35119     this.el.removeClass("x-layout-inactive-content");
35120     this.el.on("mousewheel", this.onWheel, this);
35121
35122     var up = wrap.createChild({cls: "x-scroller-up", html: "&#160;"}, this.el.dom);
35123     var down = wrap.createChild({cls: "x-scroller-down", html: "&#160;"});
35124     up.unselectable(); down.unselectable();
35125     up.on("click", this.scrollUp, this);
35126     down.on("click", this.scrollDown, this);
35127     up.addClassOnOver("x-scroller-btn-over");
35128     down.addClassOnOver("x-scroller-btn-over");
35129     up.addClassOnClick("x-scroller-btn-click");
35130     down.addClassOnClick("x-scroller-btn-click");
35131     this.adjustments = [0, -(up.getHeight() + down.getHeight())];
35132
35133     this.resizeEl = this.el;
35134     this.el = wrap; this.up = up; this.down = down;
35135 };
35136
35137 Roo.extend(Roo.ScrollPanel, Roo.ContentPanel, {
35138     increment : 100,
35139     wheelIncrement : 5,
35140     scrollUp : function(){
35141         this.resizeEl.scroll("up", this.increment, {callback: this.afterScroll, scope: this});
35142     },
35143
35144     scrollDown : function(){
35145         this.resizeEl.scroll("down", this.increment, {callback: this.afterScroll, scope: this});
35146     },
35147
35148     afterScroll : function(){
35149         var el = this.resizeEl;
35150         var t = el.dom.scrollTop, h = el.dom.scrollHeight, ch = el.dom.clientHeight;
35151         this.up[t == 0 ? "addClass" : "removeClass"]("x-scroller-btn-disabled");
35152         this.down[h - t <= ch ? "addClass" : "removeClass"]("x-scroller-btn-disabled");
35153     },
35154
35155     setSize : function(){
35156         Roo.ScrollPanel.superclass.setSize.apply(this, arguments);
35157         this.afterScroll();
35158     },
35159
35160     onWheel : function(e){
35161         var d = e.getWheelDelta();
35162         this.resizeEl.dom.scrollTop -= (d*this.wheelIncrement);
35163         this.afterScroll();
35164         e.stopEvent();
35165     },
35166
35167     setContent : function(content, loadScripts){
35168         this.resizeEl.update(content, loadScripts);
35169     }
35170
35171 });
35172
35173
35174
35175
35176
35177
35178
35179
35180
35181 /**
35182  * @class Roo.TreePanel
35183  * @extends Roo.ContentPanel
35184  * @constructor
35185  * Create a new TreePanel. - defaults to fit/scoll contents.
35186  * @param {String/Object} config A string to set only the panel's title, or a config object
35187  * @cfg {Roo.tree.TreePanel} tree The tree TreePanel, with config etc.
35188  */
35189 Roo.TreePanel = function(config){
35190     var el = config.el;
35191     var tree = config.tree;
35192     delete config.tree; 
35193     delete config.el; // hopefull!
35194     
35195     // wrapper for IE7 strict & safari scroll issue
35196     
35197     var treeEl = el.createChild();
35198     config.resizeEl = treeEl;
35199     
35200     
35201     
35202     Roo.TreePanel.superclass.constructor.call(this, el, config);
35203  
35204  
35205     this.tree = new Roo.tree.TreePanel(treeEl , tree);
35206     //console.log(tree);
35207     this.on('activate', function()
35208     {
35209         if (this.tree.rendered) {
35210             return;
35211         }
35212         //console.log('render tree');
35213         this.tree.render();
35214     });
35215     // this should not be needed.. - it's actually the 'el' that resizes?
35216     // actuall it breaks the containerScroll - dragging nodes auto scroll at top
35217     
35218     //this.on('resize',  function (cp, w, h) {
35219     //        this.tree.innerCt.setWidth(w);
35220     //        this.tree.innerCt.setHeight(h);
35221     //        //this.tree.innerCt.setStyle('overflow-y', 'auto');
35222     //});
35223
35224         
35225     
35226 };
35227
35228 Roo.extend(Roo.TreePanel, Roo.ContentPanel, {   
35229     fitToFrame : true,
35230     autoScroll : true
35231 });
35232
35233
35234
35235
35236
35237
35238
35239
35240
35241
35242
35243 /*
35244  * Based on:
35245  * Ext JS Library 1.1.1
35246  * Copyright(c) 2006-2007, Ext JS, LLC.
35247  *
35248  * Originally Released Under LGPL - original licence link has changed is not relivant.
35249  *
35250  * Fork - LGPL
35251  * <script type="text/javascript">
35252  */
35253  
35254
35255 /**
35256  * @class Roo.ReaderLayout
35257  * @extends Roo.BorderLayout
35258  * This is a pre-built layout that represents a classic, 5-pane application.  It consists of a header, a primary
35259  * center region containing two nested regions (a top one for a list view and one for item preview below),
35260  * and regions on either side that can be used for navigation, application commands, informational displays, etc.
35261  * The setup and configuration work exactly the same as it does for a {@link Roo.BorderLayout} - this class simply
35262  * expedites the setup of the overall layout and regions for this common application style.
35263  * Example:
35264  <pre><code>
35265 var reader = new Roo.ReaderLayout();
35266 var CP = Roo.ContentPanel;  // shortcut for adding
35267
35268 reader.beginUpdate();
35269 reader.add("north", new CP("north", "North"));
35270 reader.add("west", new CP("west", {title: "West"}));
35271 reader.add("east", new CP("east", {title: "East"}));
35272
35273 reader.regions.listView.add(new CP("listView", "List"));
35274 reader.regions.preview.add(new CP("preview", "Preview"));
35275 reader.endUpdate();
35276 </code></pre>
35277 * @constructor
35278 * Create a new ReaderLayout
35279 * @param {Object} config Configuration options
35280 * @param {String/HTMLElement/Element} container (optional) The container this layout is bound to (defaults to
35281 * document.body if omitted)
35282 */
35283 Roo.ReaderLayout = function(config, renderTo){
35284     var c = config || {size:{}};
35285     Roo.ReaderLayout.superclass.constructor.call(this, renderTo || document.body, {
35286         north: c.north !== false ? Roo.apply({
35287             split:false,
35288             initialSize: 32,
35289             titlebar: false
35290         }, c.north) : false,
35291         west: c.west !== false ? Roo.apply({
35292             split:true,
35293             initialSize: 200,
35294             minSize: 175,
35295             maxSize: 400,
35296             titlebar: true,
35297             collapsible: true,
35298             animate: true,
35299             margins:{left:5,right:0,bottom:5,top:5},
35300             cmargins:{left:5,right:5,bottom:5,top:5}
35301         }, c.west) : false,
35302         east: c.east !== false ? Roo.apply({
35303             split:true,
35304             initialSize: 200,
35305             minSize: 175,
35306             maxSize: 400,
35307             titlebar: true,
35308             collapsible: true,
35309             animate: true,
35310             margins:{left:0,right:5,bottom:5,top:5},
35311             cmargins:{left:5,right:5,bottom:5,top:5}
35312         }, c.east) : false,
35313         center: Roo.apply({
35314             tabPosition: 'top',
35315             autoScroll:false,
35316             closeOnTab: true,
35317             titlebar:false,
35318             margins:{left:c.west!==false ? 0 : 5,right:c.east!==false ? 0 : 5,bottom:5,top:2}
35319         }, c.center)
35320     });
35321
35322     this.el.addClass('x-reader');
35323
35324     this.beginUpdate();
35325
35326     var inner = new Roo.BorderLayout(Roo.get(document.body).createChild(), {
35327         south: c.preview !== false ? Roo.apply({
35328             split:true,
35329             initialSize: 200,
35330             minSize: 100,
35331             autoScroll:true,
35332             collapsible:true,
35333             titlebar: true,
35334             cmargins:{top:5,left:0, right:0, bottom:0}
35335         }, c.preview) : false,
35336         center: Roo.apply({
35337             autoScroll:false,
35338             titlebar:false,
35339             minHeight:200
35340         }, c.listView)
35341     });
35342     this.add('center', new Roo.NestedLayoutPanel(inner,
35343             Roo.apply({title: c.mainTitle || '',tabTip:''},c.innerPanelCfg)));
35344
35345     this.endUpdate();
35346
35347     this.regions.preview = inner.getRegion('south');
35348     this.regions.listView = inner.getRegion('center');
35349 };
35350
35351 Roo.extend(Roo.ReaderLayout, Roo.BorderLayout);/*
35352  * Based on:
35353  * Ext JS Library 1.1.1
35354  * Copyright(c) 2006-2007, Ext JS, LLC.
35355  *
35356  * Originally Released Under LGPL - original licence link has changed is not relivant.
35357  *
35358  * Fork - LGPL
35359  * <script type="text/javascript">
35360  */
35361  
35362 /**
35363  * @class Roo.grid.Grid
35364  * @extends Roo.util.Observable
35365  * This class represents the primary interface of a component based grid control.
35366  * <br><br>Usage:<pre><code>
35367  var grid = new Roo.grid.Grid("my-container-id", {
35368      ds: myDataStore,
35369      cm: myColModel,
35370      selModel: mySelectionModel,
35371      autoSizeColumns: true,
35372      monitorWindowResize: false,
35373      trackMouseOver: true
35374  });
35375  // set any options
35376  grid.render();
35377  * </code></pre>
35378  * <b>Common Problems:</b><br/>
35379  * - Grid does not resize properly when going smaller: Setting overflow hidden on the container
35380  * element will correct this<br/>
35381  * - If you get el.style[camel]= NaNpx or -2px or something related, be certain you have given your container element
35382  * dimensions. The grid adapts to your container's size, if your container has no size defined then the results
35383  * are unpredictable.<br/>
35384  * - Do not render the grid into an element with display:none. Try using visibility:hidden. Otherwise there is no way for the
35385  * grid to calculate dimensions/offsets.<br/>
35386   * @constructor
35387  * @param {String/HTMLElement/Roo.Element} container The element into which this grid will be rendered -
35388  * The container MUST have some type of size defined for the grid to fill. The container will be
35389  * automatically set to position relative if it isn't already.
35390  * @param {Object} config A config object that sets properties on this grid.
35391  */
35392 Roo.grid.Grid = function(container, config){
35393         // initialize the container
35394         this.container = Roo.get(container);
35395         this.container.update("");
35396         this.container.setStyle("overflow", "hidden");
35397     this.container.addClass('x-grid-container');
35398
35399     this.id = this.container.id;
35400
35401     Roo.apply(this, config);
35402     // check and correct shorthanded configs
35403     if(this.ds){
35404         this.dataSource = this.ds;
35405         delete this.ds;
35406     }
35407     if(this.cm){
35408         this.colModel = this.cm;
35409         delete this.cm;
35410     }
35411     if(this.sm){
35412         this.selModel = this.sm;
35413         delete this.sm;
35414     }
35415
35416     if (this.selModel) {
35417         this.selModel = Roo.factory(this.selModel, Roo.grid);
35418         this.sm = this.selModel;
35419         this.sm.xmodule = this.xmodule || false;
35420     }
35421     if (typeof(this.colModel.config) == 'undefined') {
35422         this.colModel = new Roo.grid.ColumnModel(this.colModel);
35423         this.cm = this.colModel;
35424         this.cm.xmodule = this.xmodule || false;
35425     }
35426     if (this.dataSource) {
35427         this.dataSource= Roo.factory(this.dataSource, Roo.data);
35428         this.ds = this.dataSource;
35429         this.ds.xmodule = this.xmodule || false;
35430          
35431     }
35432     
35433     
35434     
35435     if(this.width){
35436         this.container.setWidth(this.width);
35437     }
35438
35439     if(this.height){
35440         this.container.setHeight(this.height);
35441     }
35442     /** @private */
35443         this.addEvents({
35444         // raw events
35445         /**
35446          * @event click
35447          * The raw click event for the entire grid.
35448          * @param {Roo.EventObject} e
35449          */
35450         "click" : true,
35451         /**
35452          * @event dblclick
35453          * The raw dblclick event for the entire grid.
35454          * @param {Roo.EventObject} e
35455          */
35456         "dblclick" : true,
35457         /**
35458          * @event contextmenu
35459          * The raw contextmenu event for the entire grid.
35460          * @param {Roo.EventObject} e
35461          */
35462         "contextmenu" : true,
35463         /**
35464          * @event mousedown
35465          * The raw mousedown event for the entire grid.
35466          * @param {Roo.EventObject} e
35467          */
35468         "mousedown" : true,
35469         /**
35470          * @event mouseup
35471          * The raw mouseup event for the entire grid.
35472          * @param {Roo.EventObject} e
35473          */
35474         "mouseup" : true,
35475         /**
35476          * @event mouseover
35477          * The raw mouseover event for the entire grid.
35478          * @param {Roo.EventObject} e
35479          */
35480         "mouseover" : true,
35481         /**
35482          * @event mouseout
35483          * The raw mouseout event for the entire grid.
35484          * @param {Roo.EventObject} e
35485          */
35486         "mouseout" : true,
35487         /**
35488          * @event keypress
35489          * The raw keypress event for the entire grid.
35490          * @param {Roo.EventObject} e
35491          */
35492         "keypress" : true,
35493         /**
35494          * @event keydown
35495          * The raw keydown event for the entire grid.
35496          * @param {Roo.EventObject} e
35497          */
35498         "keydown" : true,
35499
35500         // custom events
35501
35502         /**
35503          * @event cellclick
35504          * Fires when a cell is clicked
35505          * @param {Grid} this
35506          * @param {Number} rowIndex
35507          * @param {Number} columnIndex
35508          * @param {Roo.EventObject} e
35509          */
35510         "cellclick" : true,
35511         /**
35512          * @event celldblclick
35513          * Fires when a cell is double clicked
35514          * @param {Grid} this
35515          * @param {Number} rowIndex
35516          * @param {Number} columnIndex
35517          * @param {Roo.EventObject} e
35518          */
35519         "celldblclick" : true,
35520         /**
35521          * @event rowclick
35522          * Fires when a row is clicked
35523          * @param {Grid} this
35524          * @param {Number} rowIndex
35525          * @param {Roo.EventObject} e
35526          */
35527         "rowclick" : true,
35528         /**
35529          * @event rowdblclick
35530          * Fires when a row is double clicked
35531          * @param {Grid} this
35532          * @param {Number} rowIndex
35533          * @param {Roo.EventObject} e
35534          */
35535         "rowdblclick" : true,
35536         /**
35537          * @event headerclick
35538          * Fires when a header is clicked
35539          * @param {Grid} this
35540          * @param {Number} columnIndex
35541          * @param {Roo.EventObject} e
35542          */
35543         "headerclick" : true,
35544         /**
35545          * @event headerdblclick
35546          * Fires when a header cell is double clicked
35547          * @param {Grid} this
35548          * @param {Number} columnIndex
35549          * @param {Roo.EventObject} e
35550          */
35551         "headerdblclick" : true,
35552         /**
35553          * @event rowcontextmenu
35554          * Fires when a row is right clicked
35555          * @param {Grid} this
35556          * @param {Number} rowIndex
35557          * @param {Roo.EventObject} e
35558          */
35559         "rowcontextmenu" : true,
35560         /**
35561          * @event cellcontextmenu
35562          * Fires when a cell is right clicked
35563          * @param {Grid} this
35564          * @param {Number} rowIndex
35565          * @param {Number} cellIndex
35566          * @param {Roo.EventObject} e
35567          */
35568          "cellcontextmenu" : true,
35569         /**
35570          * @event headercontextmenu
35571          * Fires when a header is right clicked
35572          * @param {Grid} this
35573          * @param {Number} columnIndex
35574          * @param {Roo.EventObject} e
35575          */
35576         "headercontextmenu" : true,
35577         /**
35578          * @event bodyscroll
35579          * Fires when the body element is scrolled
35580          * @param {Number} scrollLeft
35581          * @param {Number} scrollTop
35582          */
35583         "bodyscroll" : true,
35584         /**
35585          * @event columnresize
35586          * Fires when the user resizes a column
35587          * @param {Number} columnIndex
35588          * @param {Number} newSize
35589          */
35590         "columnresize" : true,
35591         /**
35592          * @event columnmove
35593          * Fires when the user moves a column
35594          * @param {Number} oldIndex
35595          * @param {Number} newIndex
35596          */
35597         "columnmove" : true,
35598         /**
35599          * @event startdrag
35600          * Fires when row(s) start being dragged
35601          * @param {Grid} this
35602          * @param {Roo.GridDD} dd The drag drop object
35603          * @param {event} e The raw browser event
35604          */
35605         "startdrag" : true,
35606         /**
35607          * @event enddrag
35608          * Fires when a drag operation is complete
35609          * @param {Grid} this
35610          * @param {Roo.GridDD} dd The drag drop object
35611          * @param {event} e The raw browser event
35612          */
35613         "enddrag" : true,
35614         /**
35615          * @event dragdrop
35616          * Fires when dragged row(s) are dropped on a valid DD target
35617          * @param {Grid} this
35618          * @param {Roo.GridDD} dd The drag drop object
35619          * @param {String} targetId The target drag drop object
35620          * @param {event} e The raw browser event
35621          */
35622         "dragdrop" : true,
35623         /**
35624          * @event dragover
35625          * Fires while row(s) are being dragged. "targetId" is the id of the Yahoo.util.DD object the selected rows are being dragged over.
35626          * @param {Grid} this
35627          * @param {Roo.GridDD} dd The drag drop object
35628          * @param {String} targetId The target drag drop object
35629          * @param {event} e The raw browser event
35630          */
35631         "dragover" : true,
35632         /**
35633          * @event dragenter
35634          *  Fires when the dragged row(s) first cross another DD target while being dragged
35635          * @param {Grid} this
35636          * @param {Roo.GridDD} dd The drag drop object
35637          * @param {String} targetId The target drag drop object
35638          * @param {event} e The raw browser event
35639          */
35640         "dragenter" : true,
35641         /**
35642          * @event dragout
35643          * Fires when the dragged row(s) leave another DD target while being dragged
35644          * @param {Grid} this
35645          * @param {Roo.GridDD} dd The drag drop object
35646          * @param {String} targetId The target drag drop object
35647          * @param {event} e The raw browser event
35648          */
35649         "dragout" : true,
35650         /**
35651          * @event rowclass
35652          * Fires when a row is rendered, so you can change add a style to it.
35653          * @param {GridView} gridview   The grid view
35654          * @param {Object} rowcfg   contains record  rowIndex and rowClass - set rowClass to add a style.
35655          */
35656         'rowclass' : true,
35657
35658         /**
35659          * @event render
35660          * Fires when the grid is rendered
35661          * @param {Grid} grid
35662          */
35663         'render' : true
35664     });
35665
35666     Roo.grid.Grid.superclass.constructor.call(this);
35667 };
35668 Roo.extend(Roo.grid.Grid, Roo.util.Observable, {
35669     
35670     /**
35671      * @cfg {String} ddGroup - drag drop group.
35672      */
35673
35674     /**
35675      * @cfg {Number} minColumnWidth The minimum width a column can be resized to. Default is 25.
35676      */
35677     minColumnWidth : 25,
35678
35679     /**
35680      * @cfg {Boolean} autoSizeColumns True to automatically resize the columns to fit their content
35681      * <b>on initial render.</b> It is more efficient to explicitly size the columns
35682      * through the ColumnModel's {@link Roo.grid.ColumnModel#width} config option.  Default is false.
35683      */
35684     autoSizeColumns : false,
35685
35686     /**
35687      * @cfg {Boolean} autoSizeHeaders True to measure headers with column data when auto sizing columns. Default is true.
35688      */
35689     autoSizeHeaders : true,
35690
35691     /**
35692      * @cfg {Boolean} monitorWindowResize True to autoSize the grid when the window resizes. Default is true.
35693      */
35694     monitorWindowResize : true,
35695
35696     /**
35697      * @cfg {Boolean} maxRowsToMeasure If autoSizeColumns is on, maxRowsToMeasure can be used to limit the number of
35698      * rows measured to get a columns size. Default is 0 (all rows).
35699      */
35700     maxRowsToMeasure : 0,
35701
35702     /**
35703      * @cfg {Boolean} trackMouseOver True to highlight rows when the mouse is over. Default is true.
35704      */
35705     trackMouseOver : true,
35706
35707     /**
35708     * @cfg {Boolean} enableDrag  True to enable drag of rows. Default is false. (double check if this is needed?)
35709     */
35710     
35711     /**
35712     * @cfg {Boolean} enableDragDrop True to enable drag and drop of rows. Default is false.
35713     */
35714     enableDragDrop : false,
35715     
35716     /**
35717     * @cfg {Boolean} enableColumnMove True to enable drag and drop reorder of columns. Default is true.
35718     */
35719     enableColumnMove : true,
35720     
35721     /**
35722     * @cfg {Boolean} enableColumnHide True to enable hiding of columns with the header context menu. Default is true.
35723     */
35724     enableColumnHide : true,
35725     
35726     /**
35727     * @cfg {Boolean} enableRowHeightSync True to manually sync row heights across locked and not locked rows. Default is false.
35728     */
35729     enableRowHeightSync : false,
35730     
35731     /**
35732     * @cfg {Boolean} stripeRows True to stripe the rows.  Default is true.
35733     */
35734     stripeRows : true,
35735     
35736     /**
35737     * @cfg {Boolean} autoHeight True to fit the height of the grid container to the height of the data. Default is false.
35738     */
35739     autoHeight : false,
35740
35741     /**
35742      * @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.
35743      */
35744     autoExpandColumn : false,
35745
35746     /**
35747     * @cfg {Number} autoExpandMin The minimum width the autoExpandColumn can have (if enabled).
35748     * Default is 50.
35749     */
35750     autoExpandMin : 50,
35751
35752     /**
35753     * @cfg {Number} autoExpandMax The maximum width the autoExpandColumn can have (if enabled). Default is 1000.
35754     */
35755     autoExpandMax : 1000,
35756
35757     /**
35758     * @cfg {Object} view The {@link Roo.grid.GridView} used by the grid. This can be set before a call to render().
35759     */
35760     view : null,
35761
35762     /**
35763     * @cfg {Object} loadMask An {@link Roo.LoadMask} config or true to mask the grid while loading. Default is false.
35764     */
35765     loadMask : false,
35766     /**
35767     * @cfg {Roo.dd.DropTarget} dropTarget An {@link Roo.dd.DropTarget} config
35768     */
35769     dropTarget: false,
35770     
35771    
35772     
35773     // private
35774     rendered : false,
35775
35776     /**
35777     * @cfg {Boolean} autoWidth True to set the grid's width to the default total width of the grid's columns instead
35778     * of a fixed width. Default is false.
35779     */
35780     /**
35781     * @cfg {Number} maxHeight Sets the maximum height of the grid - ignored if autoHeight is not on.
35782     */
35783     /**
35784      * Called once after all setup has been completed and the grid is ready to be rendered.
35785      * @return {Roo.grid.Grid} this
35786      */
35787     render : function()
35788     {
35789         var c = this.container;
35790         // try to detect autoHeight/width mode
35791         if((!c.dom.offsetHeight || c.dom.offsetHeight < 20) || c.getStyle("height") == "auto"){
35792             this.autoHeight = true;
35793         }
35794         var view = this.getView();
35795         view.init(this);
35796
35797         c.on("click", this.onClick, this);
35798         c.on("dblclick", this.onDblClick, this);
35799         c.on("contextmenu", this.onContextMenu, this);
35800         c.on("keydown", this.onKeyDown, this);
35801         if (Roo.isTouch) {
35802             c.on("touchstart", this.onTouchStart, this);
35803         }
35804
35805         this.relayEvents(c, ["mousedown","mouseup","mouseover","mouseout","keypress"]);
35806
35807         this.getSelectionModel().init(this);
35808
35809         view.render();
35810
35811         if(this.loadMask){
35812             this.loadMask = new Roo.LoadMask(this.container,
35813                     Roo.apply({store:this.dataSource}, this.loadMask));
35814         }
35815         
35816         
35817         if (this.toolbar && this.toolbar.xtype) {
35818             this.toolbar.container = this.getView().getHeaderPanel(true);
35819             this.toolbar = new Roo.Toolbar(this.toolbar);
35820         }
35821         if (this.footer && this.footer.xtype) {
35822             this.footer.dataSource = this.getDataSource();
35823             this.footer.container = this.getView().getFooterPanel(true);
35824             this.footer = Roo.factory(this.footer, Roo);
35825         }
35826         if (this.dropTarget && this.dropTarget.xtype) {
35827             delete this.dropTarget.xtype;
35828             this.dropTarget =  new Roo.dd.DropTarget(this.getView().mainBody, this.dropTarget);
35829         }
35830         
35831         
35832         this.rendered = true;
35833         this.fireEvent('render', this);
35834         return this;
35835     },
35836
35837         /**
35838          * Reconfigures the grid to use a different Store and Column Model.
35839          * The View will be bound to the new objects and refreshed.
35840          * @param {Roo.data.Store} dataSource The new {@link Roo.data.Store} object
35841          * @param {Roo.grid.ColumnModel} The new {@link Roo.grid.ColumnModel} object
35842          */
35843     reconfigure : function(dataSource, colModel){
35844         if(this.loadMask){
35845             this.loadMask.destroy();
35846             this.loadMask = new Roo.LoadMask(this.container,
35847                     Roo.apply({store:dataSource}, this.loadMask));
35848         }
35849         this.view.bind(dataSource, colModel);
35850         this.dataSource = dataSource;
35851         this.colModel = colModel;
35852         this.view.refresh(true);
35853     },
35854
35855     // private
35856     onKeyDown : function(e){
35857         this.fireEvent("keydown", e);
35858     },
35859
35860     /**
35861      * Destroy this grid.
35862      * @param {Boolean} removeEl True to remove the element
35863      */
35864     destroy : function(removeEl, keepListeners){
35865         if(this.loadMask){
35866             this.loadMask.destroy();
35867         }
35868         var c = this.container;
35869         c.removeAllListeners();
35870         this.view.destroy();
35871         this.colModel.purgeListeners();
35872         if(!keepListeners){
35873             this.purgeListeners();
35874         }
35875         c.update("");
35876         if(removeEl === true){
35877             c.remove();
35878         }
35879     },
35880
35881     // private
35882     processEvent : function(name, e){
35883         // does this fire select???
35884         Roo.log('grid:processEvent '  + name);
35885         
35886         if (name != 'touchstart' ) {
35887             this.fireEvent(name, e);    
35888         }
35889         
35890         var t = e.getTarget();
35891         var v = this.view;
35892         var header = v.findHeaderIndex(t);
35893         if(header !== false){
35894             this.fireEvent("header" + (name == 'touchstart' ? 'click' : name), this, header, e);
35895         }else{
35896             var row = v.findRowIndex(t);
35897             var cell = v.findCellIndex(t);
35898             if (name == 'touchstart') {
35899                 // first touch is always a click.
35900                 // hopefull this happens after selection is updated.?
35901                 name = false;
35902                 
35903                 if (typeof(this.selModel.getSelectedCell) != 'undefined') {
35904                     var cs = this.selModel.getSelectedCell();
35905                     if (row == cs[0] && cell == cs[1]){
35906                         name = 'dblclick';
35907                     }
35908                 }
35909                 if (typeof(this.selModel.getSelections) != 'undefined') {
35910                     var cs = this.selModel.getSelections();
35911                     var ds = this.dataSource;
35912                     if (cs.length == 1 && ds.getAt(row) == cs[0]){
35913                         name = 'dblclick';
35914                     }
35915                 }
35916                 if (!name) {
35917                     return;
35918                 }
35919             }
35920             
35921             
35922             if(row !== false){
35923                 this.fireEvent("row" + name, this, row, e);
35924                 if(cell !== false){
35925                     this.fireEvent("cell" + name, this, row, cell, e);
35926                 }
35927             }
35928         }
35929     },
35930
35931     // private
35932     onClick : function(e){
35933         this.processEvent("click", e);
35934     },
35935    // private
35936     onTouchStart : function(e){
35937         this.processEvent("touchstart", e);
35938     },
35939
35940     // private
35941     onContextMenu : function(e, t){
35942         this.processEvent("contextmenu", e);
35943     },
35944
35945     // private
35946     onDblClick : function(e){
35947         this.processEvent("dblclick", e);
35948     },
35949
35950     // private
35951     walkCells : function(row, col, step, fn, scope){
35952         var cm = this.colModel, clen = cm.getColumnCount();
35953         var ds = this.dataSource, rlen = ds.getCount(), first = true;
35954         if(step < 0){
35955             if(col < 0){
35956                 row--;
35957                 first = false;
35958             }
35959             while(row >= 0){
35960                 if(!first){
35961                     col = clen-1;
35962                 }
35963                 first = false;
35964                 while(col >= 0){
35965                     if(fn.call(scope || this, row, col, cm) === true){
35966                         return [row, col];
35967                     }
35968                     col--;
35969                 }
35970                 row--;
35971             }
35972         } else {
35973             if(col >= clen){
35974                 row++;
35975                 first = false;
35976             }
35977             while(row < rlen){
35978                 if(!first){
35979                     col = 0;
35980                 }
35981                 first = false;
35982                 while(col < clen){
35983                     if(fn.call(scope || this, row, col, cm) === true){
35984                         return [row, col];
35985                     }
35986                     col++;
35987                 }
35988                 row++;
35989             }
35990         }
35991         return null;
35992     },
35993
35994     // private
35995     getSelections : function(){
35996         return this.selModel.getSelections();
35997     },
35998
35999     /**
36000      * Causes the grid to manually recalculate its dimensions. Generally this is done automatically,
36001      * but if manual update is required this method will initiate it.
36002      */
36003     autoSize : function(){
36004         if(this.rendered){
36005             this.view.layout();
36006             if(this.view.adjustForScroll){
36007                 this.view.adjustForScroll();
36008             }
36009         }
36010     },
36011
36012     /**
36013      * Returns the grid's underlying element.
36014      * @return {Element} The element
36015      */
36016     getGridEl : function(){
36017         return this.container;
36018     },
36019
36020     // private for compatibility, overridden by editor grid
36021     stopEditing : function(){},
36022
36023     /**
36024      * Returns the grid's SelectionModel.
36025      * @return {SelectionModel}
36026      */
36027     getSelectionModel : function(){
36028         if(!this.selModel){
36029             this.selModel = new Roo.grid.RowSelectionModel();
36030         }
36031         return this.selModel;
36032     },
36033
36034     /**
36035      * Returns the grid's DataSource.
36036      * @return {DataSource}
36037      */
36038     getDataSource : function(){
36039         return this.dataSource;
36040     },
36041
36042     /**
36043      * Returns the grid's ColumnModel.
36044      * @return {ColumnModel}
36045      */
36046     getColumnModel : function(){
36047         return this.colModel;
36048     },
36049
36050     /**
36051      * Returns the grid's GridView object.
36052      * @return {GridView}
36053      */
36054     getView : function(){
36055         if(!this.view){
36056             this.view = new Roo.grid.GridView(this.viewConfig);
36057         }
36058         return this.view;
36059     },
36060     /**
36061      * Called to get grid's drag proxy text, by default returns this.ddText.
36062      * @return {String}
36063      */
36064     getDragDropText : function(){
36065         var count = this.selModel.getCount();
36066         return String.format(this.ddText, count, count == 1 ? '' : 's');
36067     }
36068 });
36069 /**
36070  * Configures the text is the drag proxy (defaults to "%0 selected row(s)").
36071  * %0 is replaced with the number of selected rows.
36072  * @type String
36073  */
36074 Roo.grid.Grid.prototype.ddText = "{0} selected row{1}";/*
36075  * Based on:
36076  * Ext JS Library 1.1.1
36077  * Copyright(c) 2006-2007, Ext JS, LLC.
36078  *
36079  * Originally Released Under LGPL - original licence link has changed is not relivant.
36080  *
36081  * Fork - LGPL
36082  * <script type="text/javascript">
36083  */
36084  
36085 Roo.grid.AbstractGridView = function(){
36086         this.grid = null;
36087         
36088         this.events = {
36089             "beforerowremoved" : true,
36090             "beforerowsinserted" : true,
36091             "beforerefresh" : true,
36092             "rowremoved" : true,
36093             "rowsinserted" : true,
36094             "rowupdated" : true,
36095             "refresh" : true
36096         };
36097     Roo.grid.AbstractGridView.superclass.constructor.call(this);
36098 };
36099
36100 Roo.extend(Roo.grid.AbstractGridView, Roo.util.Observable, {
36101     rowClass : "x-grid-row",
36102     cellClass : "x-grid-cell",
36103     tdClass : "x-grid-td",
36104     hdClass : "x-grid-hd",
36105     splitClass : "x-grid-hd-split",
36106     
36107         init: function(grid){
36108         this.grid = grid;
36109                 var cid = this.grid.getGridEl().id;
36110         this.colSelector = "#" + cid + " ." + this.cellClass + "-";
36111         this.tdSelector = "#" + cid + " ." + this.tdClass + "-";
36112         this.hdSelector = "#" + cid + " ." + this.hdClass + "-";
36113         this.splitSelector = "#" + cid + " ." + this.splitClass + "-";
36114         },
36115         
36116         getColumnRenderers : function(){
36117         var renderers = [];
36118         var cm = this.grid.colModel;
36119         var colCount = cm.getColumnCount();
36120         for(var i = 0; i < colCount; i++){
36121             renderers[i] = cm.getRenderer(i);
36122         }
36123         return renderers;
36124     },
36125     
36126     getColumnIds : function(){
36127         var ids = [];
36128         var cm = this.grid.colModel;
36129         var colCount = cm.getColumnCount();
36130         for(var i = 0; i < colCount; i++){
36131             ids[i] = cm.getColumnId(i);
36132         }
36133         return ids;
36134     },
36135     
36136     getDataIndexes : function(){
36137         if(!this.indexMap){
36138             this.indexMap = this.buildIndexMap();
36139         }
36140         return this.indexMap.colToData;
36141     },
36142     
36143     getColumnIndexByDataIndex : function(dataIndex){
36144         if(!this.indexMap){
36145             this.indexMap = this.buildIndexMap();
36146         }
36147         return this.indexMap.dataToCol[dataIndex];
36148     },
36149     
36150     /**
36151      * Set a css style for a column dynamically. 
36152      * @param {Number} colIndex The index of the column
36153      * @param {String} name The css property name
36154      * @param {String} value The css value
36155      */
36156     setCSSStyle : function(colIndex, name, value){
36157         var selector = "#" + this.grid.id + " .x-grid-col-" + colIndex;
36158         Roo.util.CSS.updateRule(selector, name, value);
36159     },
36160     
36161     generateRules : function(cm){
36162         var ruleBuf = [], rulesId = this.grid.id + '-cssrules';
36163         Roo.util.CSS.removeStyleSheet(rulesId);
36164         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
36165             var cid = cm.getColumnId(i);
36166             ruleBuf.push(this.colSelector, cid, " {\n", cm.config[i].css, "}\n",
36167                          this.tdSelector, cid, " {\n}\n",
36168                          this.hdSelector, cid, " {\n}\n",
36169                          this.splitSelector, cid, " {\n}\n");
36170         }
36171         return Roo.util.CSS.createStyleSheet(ruleBuf.join(""), rulesId);
36172     }
36173 });/*
36174  * Based on:
36175  * Ext JS Library 1.1.1
36176  * Copyright(c) 2006-2007, Ext JS, LLC.
36177  *
36178  * Originally Released Under LGPL - original licence link has changed is not relivant.
36179  *
36180  * Fork - LGPL
36181  * <script type="text/javascript">
36182  */
36183
36184 // private
36185 // This is a support class used internally by the Grid components
36186 Roo.grid.HeaderDragZone = function(grid, hd, hd2){
36187     this.grid = grid;
36188     this.view = grid.getView();
36189     this.ddGroup = "gridHeader" + this.grid.getGridEl().id;
36190     Roo.grid.HeaderDragZone.superclass.constructor.call(this, hd);
36191     if(hd2){
36192         this.setHandleElId(Roo.id(hd));
36193         this.setOuterHandleElId(Roo.id(hd2));
36194     }
36195     this.scroll = false;
36196 };
36197 Roo.extend(Roo.grid.HeaderDragZone, Roo.dd.DragZone, {
36198     maxDragWidth: 120,
36199     getDragData : function(e){
36200         var t = Roo.lib.Event.getTarget(e);
36201         var h = this.view.findHeaderCell(t);
36202         if(h){
36203             return {ddel: h.firstChild, header:h};
36204         }
36205         return false;
36206     },
36207
36208     onInitDrag : function(e){
36209         this.view.headersDisabled = true;
36210         var clone = this.dragData.ddel.cloneNode(true);
36211         clone.id = Roo.id();
36212         clone.style.width = Math.min(this.dragData.header.offsetWidth,this.maxDragWidth) + "px";
36213         this.proxy.update(clone);
36214         return true;
36215     },
36216
36217     afterValidDrop : function(){
36218         var v = this.view;
36219         setTimeout(function(){
36220             v.headersDisabled = false;
36221         }, 50);
36222     },
36223
36224     afterInvalidDrop : function(){
36225         var v = this.view;
36226         setTimeout(function(){
36227             v.headersDisabled = false;
36228         }, 50);
36229     }
36230 });
36231 /*
36232  * Based on:
36233  * Ext JS Library 1.1.1
36234  * Copyright(c) 2006-2007, Ext JS, LLC.
36235  *
36236  * Originally Released Under LGPL - original licence link has changed is not relivant.
36237  *
36238  * Fork - LGPL
36239  * <script type="text/javascript">
36240  */
36241 // private
36242 // This is a support class used internally by the Grid components
36243 Roo.grid.HeaderDropZone = function(grid, hd, hd2){
36244     this.grid = grid;
36245     this.view = grid.getView();
36246     // split the proxies so they don't interfere with mouse events
36247     this.proxyTop = Roo.DomHelper.append(document.body, {
36248         cls:"col-move-top", html:"&#160;"
36249     }, true);
36250     this.proxyBottom = Roo.DomHelper.append(document.body, {
36251         cls:"col-move-bottom", html:"&#160;"
36252     }, true);
36253     this.proxyTop.hide = this.proxyBottom.hide = function(){
36254         this.setLeftTop(-100,-100);
36255         this.setStyle("visibility", "hidden");
36256     };
36257     this.ddGroup = "gridHeader" + this.grid.getGridEl().id;
36258     // temporarily disabled
36259     //Roo.dd.ScrollManager.register(this.view.scroller.dom);
36260     Roo.grid.HeaderDropZone.superclass.constructor.call(this, grid.getGridEl().dom);
36261 };
36262 Roo.extend(Roo.grid.HeaderDropZone, Roo.dd.DropZone, {
36263     proxyOffsets : [-4, -9],
36264     fly: Roo.Element.fly,
36265
36266     getTargetFromEvent : function(e){
36267         var t = Roo.lib.Event.getTarget(e);
36268         var cindex = this.view.findCellIndex(t);
36269         if(cindex !== false){
36270             return this.view.getHeaderCell(cindex);
36271         }
36272         return null;
36273     },
36274
36275     nextVisible : function(h){
36276         var v = this.view, cm = this.grid.colModel;
36277         h = h.nextSibling;
36278         while(h){
36279             if(!cm.isHidden(v.getCellIndex(h))){
36280                 return h;
36281             }
36282             h = h.nextSibling;
36283         }
36284         return null;
36285     },
36286
36287     prevVisible : function(h){
36288         var v = this.view, cm = this.grid.colModel;
36289         h = h.prevSibling;
36290         while(h){
36291             if(!cm.isHidden(v.getCellIndex(h))){
36292                 return h;
36293             }
36294             h = h.prevSibling;
36295         }
36296         return null;
36297     },
36298
36299     positionIndicator : function(h, n, e){
36300         var x = Roo.lib.Event.getPageX(e);
36301         var r = Roo.lib.Dom.getRegion(n.firstChild);
36302         var px, pt, py = r.top + this.proxyOffsets[1];
36303         if((r.right - x) <= (r.right-r.left)/2){
36304             px = r.right+this.view.borderWidth;
36305             pt = "after";
36306         }else{
36307             px = r.left;
36308             pt = "before";
36309         }
36310         var oldIndex = this.view.getCellIndex(h);
36311         var newIndex = this.view.getCellIndex(n);
36312
36313         if(this.grid.colModel.isFixed(newIndex)){
36314             return false;
36315         }
36316
36317         var locked = this.grid.colModel.isLocked(newIndex);
36318
36319         if(pt == "after"){
36320             newIndex++;
36321         }
36322         if(oldIndex < newIndex){
36323             newIndex--;
36324         }
36325         if(oldIndex == newIndex && (locked == this.grid.colModel.isLocked(oldIndex))){
36326             return false;
36327         }
36328         px +=  this.proxyOffsets[0];
36329         this.proxyTop.setLeftTop(px, py);
36330         this.proxyTop.show();
36331         if(!this.bottomOffset){
36332             this.bottomOffset = this.view.mainHd.getHeight();
36333         }
36334         this.proxyBottom.setLeftTop(px, py+this.proxyTop.dom.offsetHeight+this.bottomOffset);
36335         this.proxyBottom.show();
36336         return pt;
36337     },
36338
36339     onNodeEnter : function(n, dd, e, data){
36340         if(data.header != n){
36341             this.positionIndicator(data.header, n, e);
36342         }
36343     },
36344
36345     onNodeOver : function(n, dd, e, data){
36346         var result = false;
36347         if(data.header != n){
36348             result = this.positionIndicator(data.header, n, e);
36349         }
36350         if(!result){
36351             this.proxyTop.hide();
36352             this.proxyBottom.hide();
36353         }
36354         return result ? this.dropAllowed : this.dropNotAllowed;
36355     },
36356
36357     onNodeOut : function(n, dd, e, data){
36358         this.proxyTop.hide();
36359         this.proxyBottom.hide();
36360     },
36361
36362     onNodeDrop : function(n, dd, e, data){
36363         var h = data.header;
36364         if(h != n){
36365             var cm = this.grid.colModel;
36366             var x = Roo.lib.Event.getPageX(e);
36367             var r = Roo.lib.Dom.getRegion(n.firstChild);
36368             var pt = (r.right - x) <= ((r.right-r.left)/2) ? "after" : "before";
36369             var oldIndex = this.view.getCellIndex(h);
36370             var newIndex = this.view.getCellIndex(n);
36371             var locked = cm.isLocked(newIndex);
36372             if(pt == "after"){
36373                 newIndex++;
36374             }
36375             if(oldIndex < newIndex){
36376                 newIndex--;
36377             }
36378             if(oldIndex == newIndex && (locked == cm.isLocked(oldIndex))){
36379                 return false;
36380             }
36381             cm.setLocked(oldIndex, locked, true);
36382             cm.moveColumn(oldIndex, newIndex);
36383             this.grid.fireEvent("columnmove", oldIndex, newIndex);
36384             return true;
36385         }
36386         return false;
36387     }
36388 });
36389 /*
36390  * Based on:
36391  * Ext JS Library 1.1.1
36392  * Copyright(c) 2006-2007, Ext JS, LLC.
36393  *
36394  * Originally Released Under LGPL - original licence link has changed is not relivant.
36395  *
36396  * Fork - LGPL
36397  * <script type="text/javascript">
36398  */
36399   
36400 /**
36401  * @class Roo.grid.GridView
36402  * @extends Roo.util.Observable
36403  *
36404  * @constructor
36405  * @param {Object} config
36406  */
36407 Roo.grid.GridView = function(config){
36408     Roo.grid.GridView.superclass.constructor.call(this);
36409     this.el = null;
36410
36411     Roo.apply(this, config);
36412 };
36413
36414 Roo.extend(Roo.grid.GridView, Roo.grid.AbstractGridView, {
36415
36416     unselectable :  'unselectable="on"',
36417     unselectableCls :  'x-unselectable',
36418     
36419     
36420     rowClass : "x-grid-row",
36421
36422     cellClass : "x-grid-col",
36423
36424     tdClass : "x-grid-td",
36425
36426     hdClass : "x-grid-hd",
36427
36428     splitClass : "x-grid-split",
36429
36430     sortClasses : ["sort-asc", "sort-desc"],
36431
36432     enableMoveAnim : false,
36433
36434     hlColor: "C3DAF9",
36435
36436     dh : Roo.DomHelper,
36437
36438     fly : Roo.Element.fly,
36439
36440     css : Roo.util.CSS,
36441
36442     borderWidth: 1,
36443
36444     splitOffset: 3,
36445
36446     scrollIncrement : 22,
36447
36448     cellRE: /(?:.*?)x-grid-(?:hd|cell|csplit)-(?:[\d]+)-([\d]+)(?:.*?)/,
36449
36450     findRE: /\s?(?:x-grid-hd|x-grid-col|x-grid-csplit)\s/,
36451
36452     bind : function(ds, cm){
36453         if(this.ds){
36454             this.ds.un("load", this.onLoad, this);
36455             this.ds.un("datachanged", this.onDataChange, this);
36456             this.ds.un("add", this.onAdd, this);
36457             this.ds.un("remove", this.onRemove, this);
36458             this.ds.un("update", this.onUpdate, this);
36459             this.ds.un("clear", this.onClear, this);
36460         }
36461         if(ds){
36462             ds.on("load", this.onLoad, this);
36463             ds.on("datachanged", this.onDataChange, this);
36464             ds.on("add", this.onAdd, this);
36465             ds.on("remove", this.onRemove, this);
36466             ds.on("update", this.onUpdate, this);
36467             ds.on("clear", this.onClear, this);
36468         }
36469         this.ds = ds;
36470
36471         if(this.cm){
36472             this.cm.un("widthchange", this.onColWidthChange, this);
36473             this.cm.un("headerchange", this.onHeaderChange, this);
36474             this.cm.un("hiddenchange", this.onHiddenChange, this);
36475             this.cm.un("columnmoved", this.onColumnMove, this);
36476             this.cm.un("columnlockchange", this.onColumnLock, this);
36477         }
36478         if(cm){
36479             this.generateRules(cm);
36480             cm.on("widthchange", this.onColWidthChange, this);
36481             cm.on("headerchange", this.onHeaderChange, this);
36482             cm.on("hiddenchange", this.onHiddenChange, this);
36483             cm.on("columnmoved", this.onColumnMove, this);
36484             cm.on("columnlockchange", this.onColumnLock, this);
36485         }
36486         this.cm = cm;
36487     },
36488
36489     init: function(grid){
36490         Roo.grid.GridView.superclass.init.call(this, grid);
36491
36492         this.bind(grid.dataSource, grid.colModel);
36493
36494         grid.on("headerclick", this.handleHeaderClick, this);
36495
36496         if(grid.trackMouseOver){
36497             grid.on("mouseover", this.onRowOver, this);
36498             grid.on("mouseout", this.onRowOut, this);
36499         }
36500         grid.cancelTextSelection = function(){};
36501         this.gridId = grid.id;
36502
36503         var tpls = this.templates || {};
36504
36505         if(!tpls.master){
36506             tpls.master = new Roo.Template(
36507                '<div class="x-grid" hidefocus="true">',
36508                 '<a href="#" class="x-grid-focus" tabIndex="-1"></a>',
36509                   '<div class="x-grid-topbar"></div>',
36510                   '<div class="x-grid-scroller"><div></div></div>',
36511                   '<div class="x-grid-locked">',
36512                       '<div class="x-grid-header">{lockedHeader}</div>',
36513                       '<div class="x-grid-body">{lockedBody}</div>',
36514                   "</div>",
36515                   '<div class="x-grid-viewport">',
36516                       '<div class="x-grid-header">{header}</div>',
36517                       '<div class="x-grid-body">{body}</div>',
36518                   "</div>",
36519                   '<div class="x-grid-bottombar"></div>',
36520                  
36521                   '<div class="x-grid-resize-proxy">&#160;</div>',
36522                "</div>"
36523             );
36524             tpls.master.disableformats = true;
36525         }
36526
36527         if(!tpls.header){
36528             tpls.header = new Roo.Template(
36529                '<table border="0" cellspacing="0" cellpadding="0">',
36530                '<tbody><tr class="x-grid-hd-row">{cells}</tr></tbody>',
36531                "</table>{splits}"
36532             );
36533             tpls.header.disableformats = true;
36534         }
36535         tpls.header.compile();
36536
36537         if(!tpls.hcell){
36538             tpls.hcell = new Roo.Template(
36539                 '<td class="x-grid-hd x-grid-td-{id} {cellId}"><div title="{title}" class="x-grid-hd-inner x-grid-hd-{id}">',
36540                 '<div class="x-grid-hd-text ' + this.unselectableCls +  '" ' + this.unselectable +'>{value}<img class="x-grid-sort-icon" src="', Roo.BLANK_IMAGE_URL, '" /></div>',
36541                 "</div></td>"
36542              );
36543              tpls.hcell.disableFormats = true;
36544         }
36545         tpls.hcell.compile();
36546
36547         if(!tpls.hsplit){
36548             tpls.hsplit = new Roo.Template('<div class="x-grid-split {splitId} x-grid-split-{id}" style="{style} ' +
36549                                             this.unselectableCls +  '" ' + this.unselectable +'>&#160;</div>');
36550             tpls.hsplit.disableFormats = true;
36551         }
36552         tpls.hsplit.compile();
36553
36554         if(!tpls.body){
36555             tpls.body = new Roo.Template(
36556                '<table border="0" cellspacing="0" cellpadding="0">',
36557                "<tbody>{rows}</tbody>",
36558                "</table>"
36559             );
36560             tpls.body.disableFormats = true;
36561         }
36562         tpls.body.compile();
36563
36564         if(!tpls.row){
36565             tpls.row = new Roo.Template('<tr class="x-grid-row {alt}">{cells}</tr>');
36566             tpls.row.disableFormats = true;
36567         }
36568         tpls.row.compile();
36569
36570         if(!tpls.cell){
36571             tpls.cell = new Roo.Template(
36572                 '<td class="x-grid-col x-grid-td-{id} {cellId} {css}" tabIndex="0">',
36573                 '<div class="x-grid-col-{id} x-grid-cell-inner"><div class="x-grid-cell-text ' +
36574                     this.unselectableCls +  '" ' + this.unselectable +'" {attr}>{value}</div></div>',
36575                 "</td>"
36576             );
36577             tpls.cell.disableFormats = true;
36578         }
36579         tpls.cell.compile();
36580
36581         this.templates = tpls;
36582     },
36583
36584     // remap these for backwards compat
36585     onColWidthChange : function(){
36586         this.updateColumns.apply(this, arguments);
36587     },
36588     onHeaderChange : function(){
36589         this.updateHeaders.apply(this, arguments);
36590     }, 
36591     onHiddenChange : function(){
36592         this.handleHiddenChange.apply(this, arguments);
36593     },
36594     onColumnMove : function(){
36595         this.handleColumnMove.apply(this, arguments);
36596     },
36597     onColumnLock : function(){
36598         this.handleLockChange.apply(this, arguments);
36599     },
36600
36601     onDataChange : function(){
36602         this.refresh();
36603         this.updateHeaderSortState();
36604     },
36605
36606     onClear : function(){
36607         this.refresh();
36608     },
36609
36610     onUpdate : function(ds, record){
36611         this.refreshRow(record);
36612     },
36613
36614     refreshRow : function(record){
36615         var ds = this.ds, index;
36616         if(typeof record == 'number'){
36617             index = record;
36618             record = ds.getAt(index);
36619         }else{
36620             index = ds.indexOf(record);
36621         }
36622         this.insertRows(ds, index, index, true);
36623         this.onRemove(ds, record, index+1, true);
36624         this.syncRowHeights(index, index);
36625         this.layout();
36626         this.fireEvent("rowupdated", this, index, record);
36627     },
36628
36629     onAdd : function(ds, records, index){
36630         this.insertRows(ds, index, index + (records.length-1));
36631     },
36632
36633     onRemove : function(ds, record, index, isUpdate){
36634         if(isUpdate !== true){
36635             this.fireEvent("beforerowremoved", this, index, record);
36636         }
36637         var bt = this.getBodyTable(), lt = this.getLockedTable();
36638         if(bt.rows[index]){
36639             bt.firstChild.removeChild(bt.rows[index]);
36640         }
36641         if(lt.rows[index]){
36642             lt.firstChild.removeChild(lt.rows[index]);
36643         }
36644         if(isUpdate !== true){
36645             this.stripeRows(index);
36646             this.syncRowHeights(index, index);
36647             this.layout();
36648             this.fireEvent("rowremoved", this, index, record);
36649         }
36650     },
36651
36652     onLoad : function(){
36653         this.scrollToTop();
36654     },
36655
36656     /**
36657      * Scrolls the grid to the top
36658      */
36659     scrollToTop : function(){
36660         if(this.scroller){
36661             this.scroller.dom.scrollTop = 0;
36662             this.syncScroll();
36663         }
36664     },
36665
36666     /**
36667      * Gets a panel in the header of the grid that can be used for toolbars etc.
36668      * After modifying the contents of this panel a call to grid.autoSize() may be
36669      * required to register any changes in size.
36670      * @param {Boolean} doShow By default the header is hidden. Pass true to show the panel
36671      * @return Roo.Element
36672      */
36673     getHeaderPanel : function(doShow){
36674         if(doShow){
36675             this.headerPanel.show();
36676         }
36677         return this.headerPanel;
36678     },
36679
36680     /**
36681      * Gets a panel in the footer of the grid that can be used for toolbars etc.
36682      * After modifying the contents of this panel a call to grid.autoSize() may be
36683      * required to register any changes in size.
36684      * @param {Boolean} doShow By default the footer is hidden. Pass true to show the panel
36685      * @return Roo.Element
36686      */
36687     getFooterPanel : function(doShow){
36688         if(doShow){
36689             this.footerPanel.show();
36690         }
36691         return this.footerPanel;
36692     },
36693
36694     initElements : function(){
36695         var E = Roo.Element;
36696         var el = this.grid.getGridEl().dom.firstChild;
36697         var cs = el.childNodes;
36698
36699         this.el = new E(el);
36700         
36701          this.focusEl = new E(el.firstChild);
36702         this.focusEl.swallowEvent("click", true);
36703         
36704         this.headerPanel = new E(cs[1]);
36705         this.headerPanel.enableDisplayMode("block");
36706
36707         this.scroller = new E(cs[2]);
36708         this.scrollSizer = new E(this.scroller.dom.firstChild);
36709
36710         this.lockedWrap = new E(cs[3]);
36711         this.lockedHd = new E(this.lockedWrap.dom.firstChild);
36712         this.lockedBody = new E(this.lockedWrap.dom.childNodes[1]);
36713
36714         this.mainWrap = new E(cs[4]);
36715         this.mainHd = new E(this.mainWrap.dom.firstChild);
36716         this.mainBody = new E(this.mainWrap.dom.childNodes[1]);
36717
36718         this.footerPanel = new E(cs[5]);
36719         this.footerPanel.enableDisplayMode("block");
36720
36721         this.resizeProxy = new E(cs[6]);
36722
36723         this.headerSelector = String.format(
36724            '#{0} td.x-grid-hd, #{1} td.x-grid-hd',
36725            this.lockedHd.id, this.mainHd.id
36726         );
36727
36728         this.splitterSelector = String.format(
36729            '#{0} div.x-grid-split, #{1} div.x-grid-split',
36730            this.idToCssName(this.lockedHd.id), this.idToCssName(this.mainHd.id)
36731         );
36732     },
36733     idToCssName : function(s)
36734     {
36735         return s.replace(/[^a-z0-9]+/ig, '-');
36736     },
36737
36738     getHeaderCell : function(index){
36739         return Roo.DomQuery.select(this.headerSelector)[index];
36740     },
36741
36742     getHeaderCellMeasure : function(index){
36743         return this.getHeaderCell(index).firstChild;
36744     },
36745
36746     getHeaderCellText : function(index){
36747         return this.getHeaderCell(index).firstChild.firstChild;
36748     },
36749
36750     getLockedTable : function(){
36751         return this.lockedBody.dom.firstChild;
36752     },
36753
36754     getBodyTable : function(){
36755         return this.mainBody.dom.firstChild;
36756     },
36757
36758     getLockedRow : function(index){
36759         return this.getLockedTable().rows[index];
36760     },
36761
36762     getRow : function(index){
36763         return this.getBodyTable().rows[index];
36764     },
36765
36766     getRowComposite : function(index){
36767         if(!this.rowEl){
36768             this.rowEl = new Roo.CompositeElementLite();
36769         }
36770         var els = [], lrow, mrow;
36771         if(lrow = this.getLockedRow(index)){
36772             els.push(lrow);
36773         }
36774         if(mrow = this.getRow(index)){
36775             els.push(mrow);
36776         }
36777         this.rowEl.elements = els;
36778         return this.rowEl;
36779     },
36780     /**
36781      * Gets the 'td' of the cell
36782      * 
36783      * @param {Integer} rowIndex row to select
36784      * @param {Integer} colIndex column to select
36785      * 
36786      * @return {Object} 
36787      */
36788     getCell : function(rowIndex, colIndex){
36789         var locked = this.cm.getLockedCount();
36790         var source;
36791         if(colIndex < locked){
36792             source = this.lockedBody.dom.firstChild;
36793         }else{
36794             source = this.mainBody.dom.firstChild;
36795             colIndex -= locked;
36796         }
36797         return source.rows[rowIndex].childNodes[colIndex];
36798     },
36799
36800     getCellText : function(rowIndex, colIndex){
36801         return this.getCell(rowIndex, colIndex).firstChild.firstChild;
36802     },
36803
36804     getCellBox : function(cell){
36805         var b = this.fly(cell).getBox();
36806         if(Roo.isOpera){ // opera fails to report the Y
36807             b.y = cell.offsetTop + this.mainBody.getY();
36808         }
36809         return b;
36810     },
36811
36812     getCellIndex : function(cell){
36813         var id = String(cell.className).match(this.cellRE);
36814         if(id){
36815             return parseInt(id[1], 10);
36816         }
36817         return 0;
36818     },
36819
36820     findHeaderIndex : function(n){
36821         var r = Roo.fly(n).findParent("td." + this.hdClass, 6);
36822         return r ? this.getCellIndex(r) : false;
36823     },
36824
36825     findHeaderCell : function(n){
36826         var r = Roo.fly(n).findParent("td." + this.hdClass, 6);
36827         return r ? r : false;
36828     },
36829
36830     findRowIndex : function(n){
36831         if(!n){
36832             return false;
36833         }
36834         var r = Roo.fly(n).findParent("tr." + this.rowClass, 6);
36835         return r ? r.rowIndex : false;
36836     },
36837
36838     findCellIndex : function(node){
36839         var stop = this.el.dom;
36840         while(node && node != stop){
36841             if(this.findRE.test(node.className)){
36842                 return this.getCellIndex(node);
36843             }
36844             node = node.parentNode;
36845         }
36846         return false;
36847     },
36848
36849     getColumnId : function(index){
36850         return this.cm.getColumnId(index);
36851     },
36852
36853     getSplitters : function()
36854     {
36855         if(this.splitterSelector){
36856            return Roo.DomQuery.select(this.splitterSelector);
36857         }else{
36858             return null;
36859       }
36860     },
36861
36862     getSplitter : function(index){
36863         return this.getSplitters()[index];
36864     },
36865
36866     onRowOver : function(e, t){
36867         var row;
36868         if((row = this.findRowIndex(t)) !== false){
36869             this.getRowComposite(row).addClass("x-grid-row-over");
36870         }
36871     },
36872
36873     onRowOut : function(e, t){
36874         var row;
36875         if((row = this.findRowIndex(t)) !== false && row !== this.findRowIndex(e.getRelatedTarget())){
36876             this.getRowComposite(row).removeClass("x-grid-row-over");
36877         }
36878     },
36879
36880     renderHeaders : function(){
36881         var cm = this.cm;
36882         var ct = this.templates.hcell, ht = this.templates.header, st = this.templates.hsplit;
36883         var cb = [], lb = [], sb = [], lsb = [], p = {};
36884         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
36885             p.cellId = "x-grid-hd-0-" + i;
36886             p.splitId = "x-grid-csplit-0-" + i;
36887             p.id = cm.getColumnId(i);
36888             p.title = cm.getColumnTooltip(i) || "";
36889             p.value = cm.getColumnHeader(i) || "";
36890             p.style = (this.grid.enableColumnResize === false || !cm.isResizable(i) || cm.isFixed(i)) ? 'cursor:default' : '';
36891             if(!cm.isLocked(i)){
36892                 cb[cb.length] = ct.apply(p);
36893                 sb[sb.length] = st.apply(p);
36894             }else{
36895                 lb[lb.length] = ct.apply(p);
36896                 lsb[lsb.length] = st.apply(p);
36897             }
36898         }
36899         return [ht.apply({cells: lb.join(""), splits:lsb.join("")}),
36900                 ht.apply({cells: cb.join(""), splits:sb.join("")})];
36901     },
36902
36903     updateHeaders : function(){
36904         var html = this.renderHeaders();
36905         this.lockedHd.update(html[0]);
36906         this.mainHd.update(html[1]);
36907     },
36908
36909     /**
36910      * Focuses the specified row.
36911      * @param {Number} row The row index
36912      */
36913     focusRow : function(row)
36914     {
36915         //Roo.log('GridView.focusRow');
36916         var x = this.scroller.dom.scrollLeft;
36917         this.focusCell(row, 0, false);
36918         this.scroller.dom.scrollLeft = x;
36919     },
36920
36921     /**
36922      * Focuses the specified cell.
36923      * @param {Number} row The row index
36924      * @param {Number} col The column index
36925      * @param {Boolean} hscroll false to disable horizontal scrolling
36926      */
36927     focusCell : function(row, col, hscroll)
36928     {
36929         //Roo.log('GridView.focusCell');
36930         var el = this.ensureVisible(row, col, hscroll);
36931         this.focusEl.alignTo(el, "tl-tl");
36932         if(Roo.isGecko){
36933             this.focusEl.focus();
36934         }else{
36935             this.focusEl.focus.defer(1, this.focusEl);
36936         }
36937     },
36938
36939     /**
36940      * Scrolls the specified cell into view
36941      * @param {Number} row The row index
36942      * @param {Number} col The column index
36943      * @param {Boolean} hscroll false to disable horizontal scrolling
36944      */
36945     ensureVisible : function(row, col, hscroll)
36946     {
36947         //Roo.log('GridView.ensureVisible,' + row + ',' + col);
36948         //return null; //disable for testing.
36949         if(typeof row != "number"){
36950             row = row.rowIndex;
36951         }
36952         if(row < 0 && row >= this.ds.getCount()){
36953             return  null;
36954         }
36955         col = (col !== undefined ? col : 0);
36956         var cm = this.grid.colModel;
36957         while(cm.isHidden(col)){
36958             col++;
36959         }
36960
36961         var el = this.getCell(row, col);
36962         if(!el){
36963             return null;
36964         }
36965         var c = this.scroller.dom;
36966
36967         var ctop = parseInt(el.offsetTop, 10);
36968         var cleft = parseInt(el.offsetLeft, 10);
36969         var cbot = ctop + el.offsetHeight;
36970         var cright = cleft + el.offsetWidth;
36971         
36972         var ch = c.clientHeight - this.mainHd.dom.offsetHeight;
36973         var stop = parseInt(c.scrollTop, 10);
36974         var sleft = parseInt(c.scrollLeft, 10);
36975         var sbot = stop + ch;
36976         var sright = sleft + c.clientWidth;
36977         /*
36978         Roo.log('GridView.ensureVisible:' +
36979                 ' ctop:' + ctop +
36980                 ' c.clientHeight:' + c.clientHeight +
36981                 ' this.mainHd.dom.offsetHeight:' + this.mainHd.dom.offsetHeight +
36982                 ' stop:' + stop +
36983                 ' cbot:' + cbot +
36984                 ' sbot:' + sbot +
36985                 ' ch:' + ch  
36986                 );
36987         */
36988         if(ctop < stop){
36989              c.scrollTop = ctop;
36990             //Roo.log("set scrolltop to ctop DISABLE?");
36991         }else if(cbot > sbot){
36992             //Roo.log("set scrolltop to cbot-ch");
36993             c.scrollTop = cbot-ch;
36994         }
36995         
36996         if(hscroll !== false){
36997             if(cleft < sleft){
36998                 c.scrollLeft = cleft;
36999             }else if(cright > sright){
37000                 c.scrollLeft = cright-c.clientWidth;
37001             }
37002         }
37003          
37004         return el;
37005     },
37006
37007     updateColumns : function(){
37008         this.grid.stopEditing();
37009         var cm = this.grid.colModel, colIds = this.getColumnIds();
37010         //var totalWidth = cm.getTotalWidth();
37011         var pos = 0;
37012         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
37013             //if(cm.isHidden(i)) continue;
37014             var w = cm.getColumnWidth(i);
37015             this.css.updateRule(this.colSelector+this.idToCssName(colIds[i]), "width", (w - this.borderWidth) + "px");
37016             this.css.updateRule(this.hdSelector+this.idToCssName(colIds[i]), "width", (w - this.borderWidth) + "px");
37017         }
37018         this.updateSplitters();
37019     },
37020
37021     generateRules : function(cm){
37022         var ruleBuf = [], rulesId = this.idToCssName(this.grid.id)+ '-cssrules';
37023         Roo.util.CSS.removeStyleSheet(rulesId);
37024         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
37025             var cid = cm.getColumnId(i);
37026             var align = '';
37027             if(cm.config[i].align){
37028                 align = 'text-align:'+cm.config[i].align+';';
37029             }
37030             var hidden = '';
37031             if(cm.isHidden(i)){
37032                 hidden = 'display:none;';
37033             }
37034             var width = "width:" + (cm.getColumnWidth(i) - this.borderWidth) + "px;";
37035             ruleBuf.push(
37036                     this.colSelector, cid, " {\n", cm.config[i].css, align, width, "\n}\n",
37037                     this.hdSelector, cid, " {\n", align, width, "}\n",
37038                     this.tdSelector, cid, " {\n",hidden,"\n}\n",
37039                     this.splitSelector, cid, " {\n", hidden , "\n}\n");
37040         }
37041         return Roo.util.CSS.createStyleSheet(ruleBuf.join(""), rulesId);
37042     },
37043
37044     updateSplitters : function(){
37045         var cm = this.cm, s = this.getSplitters();
37046         if(s){ // splitters not created yet
37047             var pos = 0, locked = true;
37048             for(var i = 0, len = cm.getColumnCount(); i < len; i++){
37049                 if(cm.isHidden(i)) continue;
37050                 var w = cm.getColumnWidth(i); // make sure it's a number
37051                 if(!cm.isLocked(i) && locked){
37052                     pos = 0;
37053                     locked = false;
37054                 }
37055                 pos += w;
37056                 s[i].style.left = (pos-this.splitOffset) + "px";
37057             }
37058         }
37059     },
37060
37061     handleHiddenChange : function(colModel, colIndex, hidden){
37062         if(hidden){
37063             this.hideColumn(colIndex);
37064         }else{
37065             this.unhideColumn(colIndex);
37066         }
37067     },
37068
37069     hideColumn : function(colIndex){
37070         var cid = this.getColumnId(colIndex);
37071         this.css.updateRule(this.tdSelector+this.idToCssName(cid), "display", "none");
37072         this.css.updateRule(this.splitSelector+this.idToCssName(cid), "display", "none");
37073         if(Roo.isSafari){
37074             this.updateHeaders();
37075         }
37076         this.updateSplitters();
37077         this.layout();
37078     },
37079
37080     unhideColumn : function(colIndex){
37081         var cid = this.getColumnId(colIndex);
37082         this.css.updateRule(this.tdSelector+this.idToCssName(cid), "display", "");
37083         this.css.updateRule(this.splitSelector+this.idToCssName(cid), "display", "");
37084
37085         if(Roo.isSafari){
37086             this.updateHeaders();
37087         }
37088         this.updateSplitters();
37089         this.layout();
37090     },
37091
37092     insertRows : function(dm, firstRow, lastRow, isUpdate){
37093         if(firstRow == 0 && lastRow == dm.getCount()-1){
37094             this.refresh();
37095         }else{
37096             if(!isUpdate){
37097                 this.fireEvent("beforerowsinserted", this, firstRow, lastRow);
37098             }
37099             var s = this.getScrollState();
37100             var markup = this.renderRows(firstRow, lastRow);
37101             this.bufferRows(markup[0], this.getLockedTable(), firstRow);
37102             this.bufferRows(markup[1], this.getBodyTable(), firstRow);
37103             this.restoreScroll(s);
37104             if(!isUpdate){
37105                 this.fireEvent("rowsinserted", this, firstRow, lastRow);
37106                 this.syncRowHeights(firstRow, lastRow);
37107                 this.stripeRows(firstRow);
37108                 this.layout();
37109             }
37110         }
37111     },
37112
37113     bufferRows : function(markup, target, index){
37114         var before = null, trows = target.rows, tbody = target.tBodies[0];
37115         if(index < trows.length){
37116             before = trows[index];
37117         }
37118         var b = document.createElement("div");
37119         b.innerHTML = "<table><tbody>"+markup+"</tbody></table>";
37120         var rows = b.firstChild.rows;
37121         for(var i = 0, len = rows.length; i < len; i++){
37122             if(before){
37123                 tbody.insertBefore(rows[0], before);
37124             }else{
37125                 tbody.appendChild(rows[0]);
37126             }
37127         }
37128         b.innerHTML = "";
37129         b = null;
37130     },
37131
37132     deleteRows : function(dm, firstRow, lastRow){
37133         if(dm.getRowCount()<1){
37134             this.fireEvent("beforerefresh", this);
37135             this.mainBody.update("");
37136             this.lockedBody.update("");
37137             this.fireEvent("refresh", this);
37138         }else{
37139             this.fireEvent("beforerowsdeleted", this, firstRow, lastRow);
37140             var bt = this.getBodyTable();
37141             var tbody = bt.firstChild;
37142             var rows = bt.rows;
37143             for(var rowIndex = firstRow; rowIndex <= lastRow; rowIndex++){
37144                 tbody.removeChild(rows[firstRow]);
37145             }
37146             this.stripeRows(firstRow);
37147             this.fireEvent("rowsdeleted", this, firstRow, lastRow);
37148         }
37149     },
37150
37151     updateRows : function(dataSource, firstRow, lastRow){
37152         var s = this.getScrollState();
37153         this.refresh();
37154         this.restoreScroll(s);
37155     },
37156
37157     handleSort : function(dataSource, sortColumnIndex, sortDir, noRefresh){
37158         if(!noRefresh){
37159            this.refresh();
37160         }
37161         this.updateHeaderSortState();
37162     },
37163
37164     getScrollState : function(){
37165         
37166         var sb = this.scroller.dom;
37167         return {left: sb.scrollLeft, top: sb.scrollTop};
37168     },
37169
37170     stripeRows : function(startRow){
37171         if(!this.grid.stripeRows || this.ds.getCount() < 1){
37172             return;
37173         }
37174         startRow = startRow || 0;
37175         var rows = this.getBodyTable().rows;
37176         var lrows = this.getLockedTable().rows;
37177         var cls = ' x-grid-row-alt ';
37178         for(var i = startRow, len = rows.length; i < len; i++){
37179             var row = rows[i], lrow = lrows[i];
37180             var isAlt = ((i+1) % 2 == 0);
37181             var hasAlt = (' '+row.className + ' ').indexOf(cls) != -1;
37182             if(isAlt == hasAlt){
37183                 continue;
37184             }
37185             if(isAlt){
37186                 row.className += " x-grid-row-alt";
37187             }else{
37188                 row.className = row.className.replace("x-grid-row-alt", "");
37189             }
37190             if(lrow){
37191                 lrow.className = row.className;
37192             }
37193         }
37194     },
37195
37196     restoreScroll : function(state){
37197         //Roo.log('GridView.restoreScroll');
37198         var sb = this.scroller.dom;
37199         sb.scrollLeft = state.left;
37200         sb.scrollTop = state.top;
37201         this.syncScroll();
37202     },
37203
37204     syncScroll : function(){
37205         //Roo.log('GridView.syncScroll');
37206         var sb = this.scroller.dom;
37207         var sh = this.mainHd.dom;
37208         var bs = this.mainBody.dom;
37209         var lv = this.lockedBody.dom;
37210         sh.scrollLeft = bs.scrollLeft = sb.scrollLeft;
37211         lv.scrollTop = bs.scrollTop = sb.scrollTop;
37212     },
37213
37214     handleScroll : function(e){
37215         this.syncScroll();
37216         var sb = this.scroller.dom;
37217         this.grid.fireEvent("bodyscroll", sb.scrollLeft, sb.scrollTop);
37218         e.stopEvent();
37219     },
37220
37221     handleWheel : function(e){
37222         var d = e.getWheelDelta();
37223         this.scroller.dom.scrollTop -= d*22;
37224         // set this here to prevent jumpy scrolling on large tables
37225         this.lockedBody.dom.scrollTop = this.mainBody.dom.scrollTop = this.scroller.dom.scrollTop;
37226         e.stopEvent();
37227     },
37228
37229     renderRows : function(startRow, endRow){
37230         // pull in all the crap needed to render rows
37231         var g = this.grid, cm = g.colModel, ds = g.dataSource, stripe = g.stripeRows;
37232         var colCount = cm.getColumnCount();
37233
37234         if(ds.getCount() < 1){
37235             return ["", ""];
37236         }
37237
37238         // build a map for all the columns
37239         var cs = [];
37240         for(var i = 0; i < colCount; i++){
37241             var name = cm.getDataIndex(i);
37242             cs[i] = {
37243                 name : typeof name == 'undefined' ? ds.fields.get(i).name : name,
37244                 renderer : cm.getRenderer(i),
37245                 id : cm.getColumnId(i),
37246                 locked : cm.isLocked(i)
37247             };
37248         }
37249
37250         startRow = startRow || 0;
37251         endRow = typeof endRow == "undefined"? ds.getCount()-1 : endRow;
37252
37253         // records to render
37254         var rs = ds.getRange(startRow, endRow);
37255
37256         return this.doRender(cs, rs, ds, startRow, colCount, stripe);
37257     },
37258
37259     // As much as I hate to duplicate code, this was branched because FireFox really hates
37260     // [].join("") on strings. The performance difference was substantial enough to
37261     // branch this function
37262     doRender : Roo.isGecko ?
37263             function(cs, rs, ds, startRow, colCount, stripe){
37264                 var ts = this.templates, ct = ts.cell, rt = ts.row;
37265                 // buffers
37266                 var buf = "", lbuf = "", cb, lcb, c, p = {}, rp = {}, r, rowIndex;
37267                 
37268                 var hasListener = this.grid.hasListener('rowclass');
37269                 var rowcfg = {};
37270                 for(var j = 0, len = rs.length; j < len; j++){
37271                     r = rs[j]; cb = ""; lcb = ""; rowIndex = (j+startRow);
37272                     for(var i = 0; i < colCount; i++){
37273                         c = cs[i];
37274                         p.cellId = "x-grid-cell-" + rowIndex + "-" + i;
37275                         p.id = c.id;
37276                         p.css = p.attr = "";
37277                         p.value = c.renderer(r.data[c.name], p, r, rowIndex, i, ds);
37278                         if(p.value == undefined || p.value === "") p.value = "&#160;";
37279                         if(r.dirty && typeof r.modified[c.name] !== 'undefined'){
37280                             p.css += p.css ? ' x-grid-dirty-cell' : 'x-grid-dirty-cell';
37281                         }
37282                         var markup = ct.apply(p);
37283                         if(!c.locked){
37284                             cb+= markup;
37285                         }else{
37286                             lcb+= markup;
37287                         }
37288                     }
37289                     var alt = [];
37290                     if(stripe && ((rowIndex+1) % 2 == 0)){
37291                         alt.push("x-grid-row-alt")
37292                     }
37293                     if(r.dirty){
37294                         alt.push(  " x-grid-dirty-row");
37295                     }
37296                     rp.cells = lcb;
37297                     if(this.getRowClass){
37298                         alt.push(this.getRowClass(r, rowIndex));
37299                     }
37300                     if (hasListener) {
37301                         rowcfg = {
37302                              
37303                             record: r,
37304                             rowIndex : rowIndex,
37305                             rowClass : ''
37306                         }
37307                         this.grid.fireEvent('rowclass', this, rowcfg);
37308                         alt.push(rowcfg.rowClass);
37309                     }
37310                     rp.alt = alt.join(" ");
37311                     lbuf+= rt.apply(rp);
37312                     rp.cells = cb;
37313                     buf+=  rt.apply(rp);
37314                 }
37315                 return [lbuf, buf];
37316             } :
37317             function(cs, rs, ds, startRow, colCount, stripe){
37318                 var ts = this.templates, ct = ts.cell, rt = ts.row;
37319                 // buffers
37320                 var buf = [], lbuf = [], cb, lcb, c, p = {}, rp = {}, r, rowIndex;
37321                 var hasListener = this.grid.hasListener('rowclass');
37322  
37323                 var rowcfg = {};
37324                 for(var j = 0, len = rs.length; j < len; j++){
37325                     r = rs[j]; cb = []; lcb = []; rowIndex = (j+startRow);
37326                     for(var i = 0; i < colCount; i++){
37327                         c = cs[i];
37328                         p.cellId = "x-grid-cell-" + rowIndex + "-" + i;
37329                         p.id = c.id;
37330                         p.css = p.attr = "";
37331                         p.value = c.renderer(r.data[c.name], p, r, rowIndex, i, ds);
37332                         if(p.value == undefined || p.value === "") p.value = "&#160;";
37333                         if(r.dirty && typeof r.modified[c.name] !== 'undefined'){
37334                             p.css += p.css ? ' x-grid-dirty-cell' : 'x-grid-dirty-cell';
37335                         }
37336                         
37337                         var markup = ct.apply(p);
37338                         if(!c.locked){
37339                             cb[cb.length] = markup;
37340                         }else{
37341                             lcb[lcb.length] = markup;
37342                         }
37343                     }
37344                     var alt = [];
37345                     if(stripe && ((rowIndex+1) % 2 == 0)){
37346                         alt.push( "x-grid-row-alt");
37347                     }
37348                     if(r.dirty){
37349                         alt.push(" x-grid-dirty-row");
37350                     }
37351                     rp.cells = lcb;
37352                     if(this.getRowClass){
37353                         alt.push( this.getRowClass(r, rowIndex));
37354                     }
37355                     if (hasListener) {
37356                         rowcfg = {
37357                              
37358                             record: r,
37359                             rowIndex : rowIndex,
37360                             rowClass : ''
37361                         }
37362                         this.grid.fireEvent('rowclass', this, rowcfg);
37363                         alt.push(rowcfg.rowClass);
37364                     }
37365                     rp.alt = alt.join(" ");
37366                     rp.cells = lcb.join("");
37367                     lbuf[lbuf.length] = rt.apply(rp);
37368                     rp.cells = cb.join("");
37369                     buf[buf.length] =  rt.apply(rp);
37370                 }
37371                 return [lbuf.join(""), buf.join("")];
37372             },
37373
37374     renderBody : function(){
37375         var markup = this.renderRows();
37376         var bt = this.templates.body;
37377         return [bt.apply({rows: markup[0]}), bt.apply({rows: markup[1]})];
37378     },
37379
37380     /**
37381      * Refreshes the grid
37382      * @param {Boolean} headersToo
37383      */
37384     refresh : function(headersToo){
37385         this.fireEvent("beforerefresh", this);
37386         this.grid.stopEditing();
37387         var result = this.renderBody();
37388         this.lockedBody.update(result[0]);
37389         this.mainBody.update(result[1]);
37390         if(headersToo === true){
37391             this.updateHeaders();
37392             this.updateColumns();
37393             this.updateSplitters();
37394             this.updateHeaderSortState();
37395         }
37396         this.syncRowHeights();
37397         this.layout();
37398         this.fireEvent("refresh", this);
37399     },
37400
37401     handleColumnMove : function(cm, oldIndex, newIndex){
37402         this.indexMap = null;
37403         var s = this.getScrollState();
37404         this.refresh(true);
37405         this.restoreScroll(s);
37406         this.afterMove(newIndex);
37407     },
37408
37409     afterMove : function(colIndex){
37410         if(this.enableMoveAnim && Roo.enableFx){
37411             this.fly(this.getHeaderCell(colIndex).firstChild).highlight(this.hlColor);
37412         }
37413         // if multisort - fix sortOrder, and reload..
37414         if (this.grid.dataSource.multiSort) {
37415             // the we can call sort again..
37416             var dm = this.grid.dataSource;
37417             var cm = this.grid.colModel;
37418             var so = [];
37419             for(var i = 0; i < cm.config.length; i++ ) {
37420                 
37421                 if ((typeof(dm.sortToggle[cm.config[i].dataIndex]) == 'undefined')) {
37422                     continue; // dont' bother, it's not in sort list or being set.
37423                 }
37424                 
37425                 so.push(cm.config[i].dataIndex);
37426             };
37427             dm.sortOrder = so;
37428             dm.load(dm.lastOptions);
37429             
37430             
37431         }
37432         
37433     },
37434
37435     updateCell : function(dm, rowIndex, dataIndex){
37436         var colIndex = this.getColumnIndexByDataIndex(dataIndex);
37437         if(typeof colIndex == "undefined"){ // not present in grid
37438             return;
37439         }
37440         var cm = this.grid.colModel;
37441         var cell = this.getCell(rowIndex, colIndex);
37442         var cellText = this.getCellText(rowIndex, colIndex);
37443
37444         var p = {
37445             cellId : "x-grid-cell-" + rowIndex + "-" + colIndex,
37446             id : cm.getColumnId(colIndex),
37447             css: colIndex == cm.getColumnCount()-1 ? "x-grid-col-last" : ""
37448         };
37449         var renderer = cm.getRenderer(colIndex);
37450         var val = renderer(dm.getValueAt(rowIndex, dataIndex), p, rowIndex, colIndex, dm);
37451         if(typeof val == "undefined" || val === "") val = "&#160;";
37452         cellText.innerHTML = val;
37453         cell.className = this.cellClass + " " + this.idToCssName(p.cellId) + " " + p.css;
37454         this.syncRowHeights(rowIndex, rowIndex);
37455     },
37456
37457     calcColumnWidth : function(colIndex, maxRowsToMeasure){
37458         var maxWidth = 0;
37459         if(this.grid.autoSizeHeaders){
37460             var h = this.getHeaderCellMeasure(colIndex);
37461             maxWidth = Math.max(maxWidth, h.scrollWidth);
37462         }
37463         var tb, index;
37464         if(this.cm.isLocked(colIndex)){
37465             tb = this.getLockedTable();
37466             index = colIndex;
37467         }else{
37468             tb = this.getBodyTable();
37469             index = colIndex - this.cm.getLockedCount();
37470         }
37471         if(tb && tb.rows){
37472             var rows = tb.rows;
37473             var stopIndex = Math.min(maxRowsToMeasure || rows.length, rows.length);
37474             for(var i = 0; i < stopIndex; i++){
37475                 var cell = rows[i].childNodes[index].firstChild;
37476                 maxWidth = Math.max(maxWidth, cell.scrollWidth);
37477             }
37478         }
37479         return maxWidth + /*margin for error in IE*/ 5;
37480     },
37481     /**
37482      * Autofit a column to its content.
37483      * @param {Number} colIndex
37484      * @param {Boolean} forceMinSize true to force the column to go smaller if possible
37485      */
37486      autoSizeColumn : function(colIndex, forceMinSize, suppressEvent){
37487          if(this.cm.isHidden(colIndex)){
37488              return; // can't calc a hidden column
37489          }
37490         if(forceMinSize){
37491             var cid = this.cm.getColumnId(colIndex);
37492             this.css.updateRule(this.colSelector +this.idToCssName( cid), "width", this.grid.minColumnWidth + "px");
37493            if(this.grid.autoSizeHeaders){
37494                this.css.updateRule(this.hdSelector + this.idToCssName(cid), "width", this.grid.minColumnWidth + "px");
37495            }
37496         }
37497         var newWidth = this.calcColumnWidth(colIndex);
37498         this.cm.setColumnWidth(colIndex,
37499             Math.max(this.grid.minColumnWidth, newWidth), suppressEvent);
37500         if(!suppressEvent){
37501             this.grid.fireEvent("columnresize", colIndex, newWidth);
37502         }
37503     },
37504
37505     /**
37506      * Autofits all columns to their content and then expands to fit any extra space in the grid
37507      */
37508      autoSizeColumns : function(){
37509         var cm = this.grid.colModel;
37510         var colCount = cm.getColumnCount();
37511         for(var i = 0; i < colCount; i++){
37512             this.autoSizeColumn(i, true, true);
37513         }
37514         if(cm.getTotalWidth() < this.scroller.dom.clientWidth){
37515             this.fitColumns();
37516         }else{
37517             this.updateColumns();
37518             this.layout();
37519         }
37520     },
37521
37522     /**
37523      * Autofits all columns to the grid's width proportionate with their current size
37524      * @param {Boolean} reserveScrollSpace Reserve space for a scrollbar
37525      */
37526     fitColumns : function(reserveScrollSpace){
37527         var cm = this.grid.colModel;
37528         var colCount = cm.getColumnCount();
37529         var cols = [];
37530         var width = 0;
37531         var i, w;
37532         for (i = 0; i < colCount; i++){
37533             if(!cm.isHidden(i) && !cm.isFixed(i)){
37534                 w = cm.getColumnWidth(i);
37535                 cols.push(i);
37536                 cols.push(w);
37537                 width += w;
37538             }
37539         }
37540         var avail = Math.min(this.scroller.dom.clientWidth, this.el.getWidth());
37541         if(reserveScrollSpace){
37542             avail -= 17;
37543         }
37544         var frac = (avail - cm.getTotalWidth())/width;
37545         while (cols.length){
37546             w = cols.pop();
37547             i = cols.pop();
37548             cm.setColumnWidth(i, Math.floor(w + w*frac), true);
37549         }
37550         this.updateColumns();
37551         this.layout();
37552     },
37553
37554     onRowSelect : function(rowIndex){
37555         var row = this.getRowComposite(rowIndex);
37556         row.addClass("x-grid-row-selected");
37557     },
37558
37559     onRowDeselect : function(rowIndex){
37560         var row = this.getRowComposite(rowIndex);
37561         row.removeClass("x-grid-row-selected");
37562     },
37563
37564     onCellSelect : function(row, col){
37565         var cell = this.getCell(row, col);
37566         if(cell){
37567             Roo.fly(cell).addClass("x-grid-cell-selected");
37568         }
37569     },
37570
37571     onCellDeselect : function(row, col){
37572         var cell = this.getCell(row, col);
37573         if(cell){
37574             Roo.fly(cell).removeClass("x-grid-cell-selected");
37575         }
37576     },
37577
37578     updateHeaderSortState : function(){
37579         
37580         // sort state can be single { field: xxx, direction : yyy}
37581         // or   { xxx=>ASC , yyy : DESC ..... }
37582         
37583         var mstate = {};
37584         if (!this.ds.multiSort) { 
37585             var state = this.ds.getSortState();
37586             if(!state){
37587                 return;
37588             }
37589             mstate[state.field] = state.direction;
37590             // FIXME... - this is not used here.. but might be elsewhere..
37591             this.sortState = state;
37592             
37593         } else {
37594             mstate = this.ds.sortToggle;
37595         }
37596         //remove existing sort classes..
37597         
37598         var sc = this.sortClasses;
37599         var hds = this.el.select(this.headerSelector).removeClass(sc);
37600         
37601         for(var f in mstate) {
37602         
37603             var sortColumn = this.cm.findColumnIndex(f);
37604             
37605             if(sortColumn != -1){
37606                 var sortDir = mstate[f];        
37607                 hds.item(sortColumn).addClass(sc[sortDir == "DESC" ? 1 : 0]);
37608             }
37609         }
37610         
37611          
37612         
37613     },
37614
37615
37616     handleHeaderClick : function(g, index){
37617         if(this.headersDisabled){
37618             return;
37619         }
37620         var dm = g.dataSource, cm = g.colModel;
37621         if(!cm.isSortable(index)){
37622             return;
37623         }
37624         g.stopEditing();
37625         
37626         if (dm.multiSort) {
37627             // update the sortOrder
37628             var so = [];
37629             for(var i = 0; i < cm.config.length; i++ ) {
37630                 
37631                 if ((typeof(dm.sortToggle[cm.config[i].dataIndex]) == 'undefined') && (index != i)) {
37632                     continue; // dont' bother, it's not in sort list or being set.
37633                 }
37634                 
37635                 so.push(cm.config[i].dataIndex);
37636             };
37637             dm.sortOrder = so;
37638         }
37639         
37640         
37641         dm.sort(cm.getDataIndex(index));
37642     },
37643
37644
37645     destroy : function(){
37646         if(this.colMenu){
37647             this.colMenu.removeAll();
37648             Roo.menu.MenuMgr.unregister(this.colMenu);
37649             this.colMenu.getEl().remove();
37650             delete this.colMenu;
37651         }
37652         if(this.hmenu){
37653             this.hmenu.removeAll();
37654             Roo.menu.MenuMgr.unregister(this.hmenu);
37655             this.hmenu.getEl().remove();
37656             delete this.hmenu;
37657         }
37658         if(this.grid.enableColumnMove){
37659             var dds = Roo.dd.DDM.ids['gridHeader' + this.grid.getGridEl().id];
37660             if(dds){
37661                 for(var dd in dds){
37662                     if(!dds[dd].config.isTarget && dds[dd].dragElId){
37663                         var elid = dds[dd].dragElId;
37664                         dds[dd].unreg();
37665                         Roo.get(elid).remove();
37666                     } else if(dds[dd].config.isTarget){
37667                         dds[dd].proxyTop.remove();
37668                         dds[dd].proxyBottom.remove();
37669                         dds[dd].unreg();
37670                     }
37671                     if(Roo.dd.DDM.locationCache[dd]){
37672                         delete Roo.dd.DDM.locationCache[dd];
37673                     }
37674                 }
37675                 delete Roo.dd.DDM.ids['gridHeader' + this.grid.getGridEl().id];
37676             }
37677         }
37678         Roo.util.CSS.removeStyleSheet(this.idToCssName(this.grid.id) + '-cssrules');
37679         this.bind(null, null);
37680         Roo.EventManager.removeResizeListener(this.onWindowResize, this);
37681     },
37682
37683     handleLockChange : function(){
37684         this.refresh(true);
37685     },
37686
37687     onDenyColumnLock : function(){
37688
37689     },
37690
37691     onDenyColumnHide : function(){
37692
37693     },
37694
37695     handleHdMenuClick : function(item){
37696         var index = this.hdCtxIndex;
37697         var cm = this.cm, ds = this.ds;
37698         switch(item.id){
37699             case "asc":
37700                 ds.sort(cm.getDataIndex(index), "ASC");
37701                 break;
37702             case "desc":
37703                 ds.sort(cm.getDataIndex(index), "DESC");
37704                 break;
37705             case "lock":
37706                 var lc = cm.getLockedCount();
37707                 if(cm.getColumnCount(true) <= lc+1){
37708                     this.onDenyColumnLock();
37709                     return;
37710                 }
37711                 if(lc != index){
37712                     cm.setLocked(index, true, true);
37713                     cm.moveColumn(index, lc);
37714                     this.grid.fireEvent("columnmove", index, lc);
37715                 }else{
37716                     cm.setLocked(index, true);
37717                 }
37718             break;
37719             case "unlock":
37720                 var lc = cm.getLockedCount();
37721                 if((lc-1) != index){
37722                     cm.setLocked(index, false, true);
37723                     cm.moveColumn(index, lc-1);
37724                     this.grid.fireEvent("columnmove", index, lc-1);
37725                 }else{
37726                     cm.setLocked(index, false);
37727                 }
37728             break;
37729             default:
37730                 index = cm.getIndexById(item.id.substr(4));
37731                 if(index != -1){
37732                     if(item.checked && cm.getColumnCount(true) <= 1){
37733                         this.onDenyColumnHide();
37734                         return false;
37735                     }
37736                     cm.setHidden(index, item.checked);
37737                 }
37738         }
37739         return true;
37740     },
37741
37742     beforeColMenuShow : function(){
37743         var cm = this.cm,  colCount = cm.getColumnCount();
37744         this.colMenu.removeAll();
37745         for(var i = 0; i < colCount; i++){
37746             this.colMenu.add(new Roo.menu.CheckItem({
37747                 id: "col-"+cm.getColumnId(i),
37748                 text: cm.getColumnHeader(i),
37749                 checked: !cm.isHidden(i),
37750                 hideOnClick:false
37751             }));
37752         }
37753     },
37754
37755     handleHdCtx : function(g, index, e){
37756         e.stopEvent();
37757         var hd = this.getHeaderCell(index);
37758         this.hdCtxIndex = index;
37759         var ms = this.hmenu.items, cm = this.cm;
37760         ms.get("asc").setDisabled(!cm.isSortable(index));
37761         ms.get("desc").setDisabled(!cm.isSortable(index));
37762         if(this.grid.enableColLock !== false){
37763             ms.get("lock").setDisabled(cm.isLocked(index));
37764             ms.get("unlock").setDisabled(!cm.isLocked(index));
37765         }
37766         this.hmenu.show(hd, "tl-bl");
37767     },
37768
37769     handleHdOver : function(e){
37770         var hd = this.findHeaderCell(e.getTarget());
37771         if(hd && !this.headersDisabled){
37772             if(this.grid.colModel.isSortable(this.getCellIndex(hd))){
37773                this.fly(hd).addClass("x-grid-hd-over");
37774             }
37775         }
37776     },
37777
37778     handleHdOut : function(e){
37779         var hd = this.findHeaderCell(e.getTarget());
37780         if(hd){
37781             this.fly(hd).removeClass("x-grid-hd-over");
37782         }
37783     },
37784
37785     handleSplitDblClick : function(e, t){
37786         var i = this.getCellIndex(t);
37787         if(this.grid.enableColumnResize !== false && this.cm.isResizable(i) && !this.cm.isFixed(i)){
37788             this.autoSizeColumn(i, true);
37789             this.layout();
37790         }
37791     },
37792
37793     render : function(){
37794
37795         var cm = this.cm;
37796         var colCount = cm.getColumnCount();
37797
37798         if(this.grid.monitorWindowResize === true){
37799             Roo.EventManager.onWindowResize(this.onWindowResize, this, true);
37800         }
37801         var header = this.renderHeaders();
37802         var body = this.templates.body.apply({rows:""});
37803         var html = this.templates.master.apply({
37804             lockedBody: body,
37805             body: body,
37806             lockedHeader: header[0],
37807             header: header[1]
37808         });
37809
37810         //this.updateColumns();
37811
37812         this.grid.getGridEl().dom.innerHTML = html;
37813
37814         this.initElements();
37815         
37816         // a kludge to fix the random scolling effect in webkit
37817         this.el.on("scroll", function() {
37818             this.el.dom.scrollTop=0; // hopefully not recursive..
37819         },this);
37820
37821         this.scroller.on("scroll", this.handleScroll, this);
37822         this.lockedBody.on("mousewheel", this.handleWheel, this);
37823         this.mainBody.on("mousewheel", this.handleWheel, this);
37824
37825         this.mainHd.on("mouseover", this.handleHdOver, this);
37826         this.mainHd.on("mouseout", this.handleHdOut, this);
37827         this.mainHd.on("dblclick", this.handleSplitDblClick, this,
37828                 {delegate: "."+this.splitClass});
37829
37830         this.lockedHd.on("mouseover", this.handleHdOver, this);
37831         this.lockedHd.on("mouseout", this.handleHdOut, this);
37832         this.lockedHd.on("dblclick", this.handleSplitDblClick, this,
37833                 {delegate: "."+this.splitClass});
37834
37835         if(this.grid.enableColumnResize !== false && Roo.grid.SplitDragZone){
37836             new Roo.grid.SplitDragZone(this.grid, this.lockedHd.dom, this.mainHd.dom);
37837         }
37838
37839         this.updateSplitters();
37840
37841         if(this.grid.enableColumnMove && Roo.grid.HeaderDragZone){
37842             new Roo.grid.HeaderDragZone(this.grid, this.lockedHd.dom, this.mainHd.dom);
37843             new Roo.grid.HeaderDropZone(this.grid, this.lockedHd.dom, this.mainHd.dom);
37844         }
37845
37846         if(this.grid.enableCtxMenu !== false && Roo.menu.Menu){
37847             this.hmenu = new Roo.menu.Menu({id: this.grid.id + "-hctx"});
37848             this.hmenu.add(
37849                 {id:"asc", text: this.sortAscText, cls: "xg-hmenu-sort-asc"},
37850                 {id:"desc", text: this.sortDescText, cls: "xg-hmenu-sort-desc"}
37851             );
37852             if(this.grid.enableColLock !== false){
37853                 this.hmenu.add('-',
37854                     {id:"lock", text: this.lockText, cls: "xg-hmenu-lock"},
37855                     {id:"unlock", text: this.unlockText, cls: "xg-hmenu-unlock"}
37856                 );
37857             }
37858             if(this.grid.enableColumnHide !== false){
37859
37860                 this.colMenu = new Roo.menu.Menu({id:this.grid.id + "-hcols-menu"});
37861                 this.colMenu.on("beforeshow", this.beforeColMenuShow, this);
37862                 this.colMenu.on("itemclick", this.handleHdMenuClick, this);
37863
37864                 this.hmenu.add('-',
37865                     {id:"columns", text: this.columnsText, menu: this.colMenu}
37866                 );
37867             }
37868             this.hmenu.on("itemclick", this.handleHdMenuClick, this);
37869
37870             this.grid.on("headercontextmenu", this.handleHdCtx, this);
37871         }
37872
37873         if((this.grid.enableDragDrop || this.grid.enableDrag) && Roo.grid.GridDragZone){
37874             this.dd = new Roo.grid.GridDragZone(this.grid, {
37875                 ddGroup : this.grid.ddGroup || 'GridDD'
37876             });
37877             
37878         }
37879
37880         /*
37881         for(var i = 0; i < colCount; i++){
37882             if(cm.isHidden(i)){
37883                 this.hideColumn(i);
37884             }
37885             if(cm.config[i].align){
37886                 this.css.updateRule(this.colSelector + i, "textAlign", cm.config[i].align);
37887                 this.css.updateRule(this.hdSelector + i, "textAlign", cm.config[i].align);
37888             }
37889         }*/
37890         
37891         this.updateHeaderSortState();
37892
37893         this.beforeInitialResize();
37894         this.layout(true);
37895
37896         // two part rendering gives faster view to the user
37897         this.renderPhase2.defer(1, this);
37898     },
37899
37900     renderPhase2 : function(){
37901         // render the rows now
37902         this.refresh();
37903         if(this.grid.autoSizeColumns){
37904             this.autoSizeColumns();
37905         }
37906     },
37907
37908     beforeInitialResize : function(){
37909
37910     },
37911
37912     onColumnSplitterMoved : function(i, w){
37913         this.userResized = true;
37914         var cm = this.grid.colModel;
37915         cm.setColumnWidth(i, w, true);
37916         var cid = cm.getColumnId(i);
37917         this.css.updateRule(this.colSelector + this.idToCssName(cid), "width", (w-this.borderWidth) + "px");
37918         this.css.updateRule(this.hdSelector + this.idToCssName(cid), "width", (w-this.borderWidth) + "px");
37919         this.updateSplitters();
37920         this.layout();
37921         this.grid.fireEvent("columnresize", i, w);
37922     },
37923
37924     syncRowHeights : function(startIndex, endIndex){
37925         if(this.grid.enableRowHeightSync === true && this.cm.getLockedCount() > 0){
37926             startIndex = startIndex || 0;
37927             var mrows = this.getBodyTable().rows;
37928             var lrows = this.getLockedTable().rows;
37929             var len = mrows.length-1;
37930             endIndex = Math.min(endIndex || len, len);
37931             for(var i = startIndex; i <= endIndex; i++){
37932                 var m = mrows[i], l = lrows[i];
37933                 var h = Math.max(m.offsetHeight, l.offsetHeight);
37934                 m.style.height = l.style.height = h + "px";
37935             }
37936         }
37937     },
37938
37939     layout : function(initialRender, is2ndPass){
37940         var g = this.grid;
37941         var auto = g.autoHeight;
37942         var scrollOffset = 16;
37943         var c = g.getGridEl(), cm = this.cm,
37944                 expandCol = g.autoExpandColumn,
37945                 gv = this;
37946         //c.beginMeasure();
37947
37948         if(!c.dom.offsetWidth){ // display:none?
37949             if(initialRender){
37950                 this.lockedWrap.show();
37951                 this.mainWrap.show();
37952             }
37953             return;
37954         }
37955
37956         var hasLock = this.cm.isLocked(0);
37957
37958         var tbh = this.headerPanel.getHeight();
37959         var bbh = this.footerPanel.getHeight();
37960
37961         if(auto){
37962             var ch = this.getBodyTable().offsetHeight + tbh + bbh + this.mainHd.getHeight();
37963             var newHeight = ch + c.getBorderWidth("tb");
37964             if(g.maxHeight){
37965                 newHeight = Math.min(g.maxHeight, newHeight);
37966             }
37967             c.setHeight(newHeight);
37968         }
37969
37970         if(g.autoWidth){
37971             c.setWidth(cm.getTotalWidth()+c.getBorderWidth('lr'));
37972         }
37973
37974         var s = this.scroller;
37975
37976         var csize = c.getSize(true);
37977
37978         this.el.setSize(csize.width, csize.height);
37979
37980         this.headerPanel.setWidth(csize.width);
37981         this.footerPanel.setWidth(csize.width);
37982
37983         var hdHeight = this.mainHd.getHeight();
37984         var vw = csize.width;
37985         var vh = csize.height - (tbh + bbh);
37986
37987         s.setSize(vw, vh);
37988
37989         var bt = this.getBodyTable();
37990         var ltWidth = hasLock ?
37991                       Math.max(this.getLockedTable().offsetWidth, this.lockedHd.dom.firstChild.offsetWidth) : 0;
37992
37993         var scrollHeight = bt.offsetHeight;
37994         var scrollWidth = ltWidth + bt.offsetWidth;
37995         var vscroll = false, hscroll = false;
37996
37997         this.scrollSizer.setSize(scrollWidth, scrollHeight+hdHeight);
37998
37999         var lw = this.lockedWrap, mw = this.mainWrap;
38000         var lb = this.lockedBody, mb = this.mainBody;
38001
38002         setTimeout(function(){
38003             var t = s.dom.offsetTop;
38004             var w = s.dom.clientWidth,
38005                 h = s.dom.clientHeight;
38006
38007             lw.setTop(t);
38008             lw.setSize(ltWidth, h);
38009
38010             mw.setLeftTop(ltWidth, t);
38011             mw.setSize(w-ltWidth, h);
38012
38013             lb.setHeight(h-hdHeight);
38014             mb.setHeight(h-hdHeight);
38015
38016             if(is2ndPass !== true && !gv.userResized && expandCol){
38017                 // high speed resize without full column calculation
38018                 
38019                 var ci = cm.getIndexById(expandCol);
38020                 if (ci < 0) {
38021                     ci = cm.findColumnIndex(expandCol);
38022                 }
38023                 ci = Math.max(0, ci); // make sure it's got at least the first col.
38024                 var expandId = cm.getColumnId(ci);
38025                 var  tw = cm.getTotalWidth(false);
38026                 var currentWidth = cm.getColumnWidth(ci);
38027                 var cw = Math.min(Math.max(((w-tw)+currentWidth-2)-/*scrollbar*/(w <= s.dom.offsetWidth ? 0 : 18), g.autoExpandMin), g.autoExpandMax);
38028                 if(currentWidth != cw){
38029                     cm.setColumnWidth(ci, cw, true);
38030                     gv.css.updateRule(gv.colSelector+gv.idToCssName(expandId), "width", (cw - gv.borderWidth) + "px");
38031                     gv.css.updateRule(gv.hdSelector+gv.idToCssName(expandId), "width", (cw - gv.borderWidth) + "px");
38032                     gv.updateSplitters();
38033                     gv.layout(false, true);
38034                 }
38035             }
38036
38037             if(initialRender){
38038                 lw.show();
38039                 mw.show();
38040             }
38041             //c.endMeasure();
38042         }, 10);
38043     },
38044
38045     onWindowResize : function(){
38046         if(!this.grid.monitorWindowResize || this.grid.autoHeight){
38047             return;
38048         }
38049         this.layout();
38050     },
38051
38052     appendFooter : function(parentEl){
38053         return null;
38054     },
38055
38056     sortAscText : "Sort Ascending",
38057     sortDescText : "Sort Descending",
38058     lockText : "Lock Column",
38059     unlockText : "Unlock Column",
38060     columnsText : "Columns"
38061 });
38062
38063
38064 Roo.grid.GridView.ColumnDragZone = function(grid, hd){
38065     Roo.grid.GridView.ColumnDragZone.superclass.constructor.call(this, grid, hd, null);
38066     this.proxy.el.addClass('x-grid3-col-dd');
38067 };
38068
38069 Roo.extend(Roo.grid.GridView.ColumnDragZone, Roo.grid.HeaderDragZone, {
38070     handleMouseDown : function(e){
38071
38072     },
38073
38074     callHandleMouseDown : function(e){
38075         Roo.grid.GridView.ColumnDragZone.superclass.handleMouseDown.call(this, e);
38076     }
38077 });
38078 /*
38079  * Based on:
38080  * Ext JS Library 1.1.1
38081  * Copyright(c) 2006-2007, Ext JS, LLC.
38082  *
38083  * Originally Released Under LGPL - original licence link has changed is not relivant.
38084  *
38085  * Fork - LGPL
38086  * <script type="text/javascript">
38087  */
38088  
38089 // private
38090 // This is a support class used internally by the Grid components
38091 Roo.grid.SplitDragZone = function(grid, hd, hd2){
38092     this.grid = grid;
38093     this.view = grid.getView();
38094     this.proxy = this.view.resizeProxy;
38095     Roo.grid.SplitDragZone.superclass.constructor.call(this, hd,
38096         "gridSplitters" + this.grid.getGridEl().id, {
38097         dragElId : Roo.id(this.proxy.dom), resizeFrame:false
38098     });
38099     this.setHandleElId(Roo.id(hd));
38100     this.setOuterHandleElId(Roo.id(hd2));
38101     this.scroll = false;
38102 };
38103 Roo.extend(Roo.grid.SplitDragZone, Roo.dd.DDProxy, {
38104     fly: Roo.Element.fly,
38105
38106     b4StartDrag : function(x, y){
38107         this.view.headersDisabled = true;
38108         this.proxy.setHeight(this.view.mainWrap.getHeight());
38109         var w = this.cm.getColumnWidth(this.cellIndex);
38110         var minw = Math.max(w-this.grid.minColumnWidth, 0);
38111         this.resetConstraints();
38112         this.setXConstraint(minw, 1000);
38113         this.setYConstraint(0, 0);
38114         this.minX = x - minw;
38115         this.maxX = x + 1000;
38116         this.startPos = x;
38117         Roo.dd.DDProxy.prototype.b4StartDrag.call(this, x, y);
38118     },
38119
38120
38121     handleMouseDown : function(e){
38122         ev = Roo.EventObject.setEvent(e);
38123         var t = this.fly(ev.getTarget());
38124         if(t.hasClass("x-grid-split")){
38125             this.cellIndex = this.view.getCellIndex(t.dom);
38126             this.split = t.dom;
38127             this.cm = this.grid.colModel;
38128             if(this.cm.isResizable(this.cellIndex) && !this.cm.isFixed(this.cellIndex)){
38129                 Roo.grid.SplitDragZone.superclass.handleMouseDown.apply(this, arguments);
38130             }
38131         }
38132     },
38133
38134     endDrag : function(e){
38135         this.view.headersDisabled = false;
38136         var endX = Math.max(this.minX, Roo.lib.Event.getPageX(e));
38137         var diff = endX - this.startPos;
38138         this.view.onColumnSplitterMoved(this.cellIndex, this.cm.getColumnWidth(this.cellIndex)+diff);
38139     },
38140
38141     autoOffset : function(){
38142         this.setDelta(0,0);
38143     }
38144 });/*
38145  * Based on:
38146  * Ext JS Library 1.1.1
38147  * Copyright(c) 2006-2007, Ext JS, LLC.
38148  *
38149  * Originally Released Under LGPL - original licence link has changed is not relivant.
38150  *
38151  * Fork - LGPL
38152  * <script type="text/javascript">
38153  */
38154  
38155 // private
38156 // This is a support class used internally by the Grid components
38157 Roo.grid.GridDragZone = function(grid, config){
38158     this.view = grid.getView();
38159     Roo.grid.GridDragZone.superclass.constructor.call(this, this.view.mainBody.dom, config);
38160     if(this.view.lockedBody){
38161         this.setHandleElId(Roo.id(this.view.mainBody.dom));
38162         this.setOuterHandleElId(Roo.id(this.view.lockedBody.dom));
38163     }
38164     this.scroll = false;
38165     this.grid = grid;
38166     this.ddel = document.createElement('div');
38167     this.ddel.className = 'x-grid-dd-wrap';
38168 };
38169
38170 Roo.extend(Roo.grid.GridDragZone, Roo.dd.DragZone, {
38171     ddGroup : "GridDD",
38172
38173     getDragData : function(e){
38174         var t = Roo.lib.Event.getTarget(e);
38175         var rowIndex = this.view.findRowIndex(t);
38176         var sm = this.grid.selModel;
38177             
38178         //Roo.log(rowIndex);
38179         
38180         if (sm.getSelectedCell) {
38181             // cell selection..
38182             if (!sm.getSelectedCell()) {
38183                 return false;
38184             }
38185             if (rowIndex != sm.getSelectedCell()[0]) {
38186                 return false;
38187             }
38188         
38189         }
38190         
38191         if(rowIndex !== false){
38192             
38193             // if editorgrid.. 
38194             
38195             
38196             //Roo.log([ sm.getSelectedCell() ? sm.getSelectedCell()[0] : 'NO' , rowIndex ]);
38197                
38198             //if(!sm.isSelected(rowIndex) || e.hasModifier()){
38199               //  
38200             //}
38201             if (e.hasModifier()){
38202                 sm.handleMouseDown(e, t); // non modifier buttons are handled by row select.
38203             }
38204             
38205             Roo.log("getDragData");
38206             
38207             return {
38208                 grid: this.grid,
38209                 ddel: this.ddel,
38210                 rowIndex: rowIndex,
38211                 selections:sm.getSelections ? sm.getSelections() : (
38212                     sm.getSelectedCell() ? [ this.grid.ds.getAt(sm.getSelectedCell()[0]) ] : []
38213                 )
38214             };
38215         }
38216         return false;
38217     },
38218
38219     onInitDrag : function(e){
38220         var data = this.dragData;
38221         this.ddel.innerHTML = this.grid.getDragDropText();
38222         this.proxy.update(this.ddel);
38223         // fire start drag?
38224     },
38225
38226     afterRepair : function(){
38227         this.dragging = false;
38228     },
38229
38230     getRepairXY : function(e, data){
38231         return false;
38232     },
38233
38234     onEndDrag : function(data, e){
38235         // fire end drag?
38236     },
38237
38238     onValidDrop : function(dd, e, id){
38239         // fire drag drop?
38240         this.hideProxy();
38241     },
38242
38243     beforeInvalidDrop : function(e, id){
38244
38245     }
38246 });/*
38247  * Based on:
38248  * Ext JS Library 1.1.1
38249  * Copyright(c) 2006-2007, Ext JS, LLC.
38250  *
38251  * Originally Released Under LGPL - original licence link has changed is not relivant.
38252  *
38253  * Fork - LGPL
38254  * <script type="text/javascript">
38255  */
38256  
38257
38258 /**
38259  * @class Roo.grid.ColumnModel
38260  * @extends Roo.util.Observable
38261  * This is the default implementation of a ColumnModel used by the Grid. It defines
38262  * the columns in the grid.
38263  * <br>Usage:<br>
38264  <pre><code>
38265  var colModel = new Roo.grid.ColumnModel([
38266         {header: "Ticker", width: 60, sortable: true, locked: true},
38267         {header: "Company Name", width: 150, sortable: true},
38268         {header: "Market Cap.", width: 100, sortable: true},
38269         {header: "$ Sales", width: 100, sortable: true, renderer: money},
38270         {header: "Employees", width: 100, sortable: true, resizable: false}
38271  ]);
38272  </code></pre>
38273  * <p>
38274  
38275  * The config options listed for this class are options which may appear in each
38276  * individual column definition.
38277  * <br/>RooJS Fix - column id's are not sequential but use Roo.id() - fixes bugs with layouts.
38278  * @constructor
38279  * @param {Object} config An Array of column config objects. See this class's
38280  * config objects for details.
38281 */
38282 Roo.grid.ColumnModel = function(config){
38283         /**
38284      * The config passed into the constructor
38285      */
38286     this.config = config;
38287     this.lookup = {};
38288
38289     // if no id, create one
38290     // if the column does not have a dataIndex mapping,
38291     // map it to the order it is in the config
38292     for(var i = 0, len = config.length; i < len; i++){
38293         var c = config[i];
38294         if(typeof c.dataIndex == "undefined"){
38295             c.dataIndex = i;
38296         }
38297         if(typeof c.renderer == "string"){
38298             c.renderer = Roo.util.Format[c.renderer];
38299         }
38300         if(typeof c.id == "undefined"){
38301             c.id = Roo.id();
38302         }
38303         if(c.editor && c.editor.xtype){
38304             c.editor  = Roo.factory(c.editor, Roo.grid);
38305         }
38306         if(c.editor && c.editor.isFormField){
38307             c.editor = new Roo.grid.GridEditor(c.editor);
38308         }
38309         this.lookup[c.id] = c;
38310     }
38311
38312     /**
38313      * The width of columns which have no width specified (defaults to 100)
38314      * @type Number
38315      */
38316     this.defaultWidth = 100;
38317
38318     /**
38319      * Default sortable of columns which have no sortable specified (defaults to false)
38320      * @type Boolean
38321      */
38322     this.defaultSortable = false;
38323
38324     this.addEvents({
38325         /**
38326              * @event widthchange
38327              * Fires when the width of a column changes.
38328              * @param {ColumnModel} this
38329              * @param {Number} columnIndex The column index
38330              * @param {Number} newWidth The new width
38331              */
38332             "widthchange": true,
38333         /**
38334              * @event headerchange
38335              * Fires when the text of a header changes.
38336              * @param {ColumnModel} this
38337              * @param {Number} columnIndex The column index
38338              * @param {Number} newText The new header text
38339              */
38340             "headerchange": true,
38341         /**
38342              * @event hiddenchange
38343              * Fires when a column is hidden or "unhidden".
38344              * @param {ColumnModel} this
38345              * @param {Number} columnIndex The column index
38346              * @param {Boolean} hidden true if hidden, false otherwise
38347              */
38348             "hiddenchange": true,
38349             /**
38350          * @event columnmoved
38351          * Fires when a column is moved.
38352          * @param {ColumnModel} this
38353          * @param {Number} oldIndex
38354          * @param {Number} newIndex
38355          */
38356         "columnmoved" : true,
38357         /**
38358          * @event columlockchange
38359          * Fires when a column's locked state is changed
38360          * @param {ColumnModel} this
38361          * @param {Number} colIndex
38362          * @param {Boolean} locked true if locked
38363          */
38364         "columnlockchange" : true
38365     });
38366     Roo.grid.ColumnModel.superclass.constructor.call(this);
38367 };
38368 Roo.extend(Roo.grid.ColumnModel, Roo.util.Observable, {
38369     /**
38370      * @cfg {String} header The header text to display in the Grid view.
38371      */
38372     /**
38373      * @cfg {String} dataIndex (Optional) The name of the field in the grid's {@link Roo.data.Store}'s
38374      * {@link Roo.data.Record} definition from which to draw the column's value. If not
38375      * specified, the column's index is used as an index into the Record's data Array.
38376      */
38377     /**
38378      * @cfg {Number} width (Optional) The initial width in pixels of the column. Using this
38379      * instead of {@link Roo.grid.Grid#autoSizeColumns} is more efficient.
38380      */
38381     /**
38382      * @cfg {Boolean} sortable (Optional) True if sorting is to be allowed on this column.
38383      * Defaults to the value of the {@link #defaultSortable} property.
38384      * Whether local/remote sorting is used is specified in {@link Roo.data.Store#remoteSort}.
38385      */
38386     /**
38387      * @cfg {Boolean} locked (Optional) True to lock the column in place while scrolling the Grid.  Defaults to false.
38388      */
38389     /**
38390      * @cfg {Boolean} fixed (Optional) True if the column width cannot be changed.  Defaults to false.
38391      */
38392     /**
38393      * @cfg {Boolean} resizable (Optional) False to disable column resizing. Defaults to true.
38394      */
38395     /**
38396      * @cfg {Boolean} hidden (Optional) True to hide the column. Defaults to false.
38397      */
38398     /**
38399      * @cfg {Function} renderer (Optional) A function used to generate HTML markup for a cell
38400      * given the cell's data value. See {@link #setRenderer}. If not specified, the
38401      * default renderer uses the raw data value.
38402      */
38403        /**
38404      * @cfg {Roo.grid.GridEditor} editor (Optional) For grid editors - returns the grid editor 
38405      */
38406     /**
38407      * @cfg {String} align (Optional) Set the CSS text-align property of the column.  Defaults to undefined.
38408      */
38409
38410     /**
38411      * Returns the id of the column at the specified index.
38412      * @param {Number} index The column index
38413      * @return {String} the id
38414      */
38415     getColumnId : function(index){
38416         return this.config[index].id;
38417     },
38418
38419     /**
38420      * Returns the column for a specified id.
38421      * @param {String} id The column id
38422      * @return {Object} the column
38423      */
38424     getColumnById : function(id){
38425         return this.lookup[id];
38426     },
38427
38428     
38429     /**
38430      * Returns the column for a specified dataIndex.
38431      * @param {String} dataIndex The column dataIndex
38432      * @return {Object|Boolean} the column or false if not found
38433      */
38434     getColumnByDataIndex: function(dataIndex){
38435         var index = this.findColumnIndex(dataIndex);
38436         return index > -1 ? this.config[index] : false;
38437     },
38438     
38439     /**
38440      * Returns the index for a specified column id.
38441      * @param {String} id The column id
38442      * @return {Number} the index, or -1 if not found
38443      */
38444     getIndexById : function(id){
38445         for(var i = 0, len = this.config.length; i < len; i++){
38446             if(this.config[i].id == id){
38447                 return i;
38448             }
38449         }
38450         return -1;
38451     },
38452     
38453     /**
38454      * Returns the index for a specified column dataIndex.
38455      * @param {String} dataIndex The column dataIndex
38456      * @return {Number} the index, or -1 if not found
38457      */
38458     
38459     findColumnIndex : function(dataIndex){
38460         for(var i = 0, len = this.config.length; i < len; i++){
38461             if(this.config[i].dataIndex == dataIndex){
38462                 return i;
38463             }
38464         }
38465         return -1;
38466     },
38467     
38468     
38469     moveColumn : function(oldIndex, newIndex){
38470         var c = this.config[oldIndex];
38471         this.config.splice(oldIndex, 1);
38472         this.config.splice(newIndex, 0, c);
38473         this.dataMap = null;
38474         this.fireEvent("columnmoved", this, oldIndex, newIndex);
38475     },
38476
38477     isLocked : function(colIndex){
38478         return this.config[colIndex].locked === true;
38479     },
38480
38481     setLocked : function(colIndex, value, suppressEvent){
38482         if(this.isLocked(colIndex) == value){
38483             return;
38484         }
38485         this.config[colIndex].locked = value;
38486         if(!suppressEvent){
38487             this.fireEvent("columnlockchange", this, colIndex, value);
38488         }
38489     },
38490
38491     getTotalLockedWidth : function(){
38492         var totalWidth = 0;
38493         for(var i = 0; i < this.config.length; i++){
38494             if(this.isLocked(i) && !this.isHidden(i)){
38495                 this.totalWidth += this.getColumnWidth(i);
38496             }
38497         }
38498         return totalWidth;
38499     },
38500
38501     getLockedCount : function(){
38502         for(var i = 0, len = this.config.length; i < len; i++){
38503             if(!this.isLocked(i)){
38504                 return i;
38505             }
38506         }
38507     },
38508
38509     /**
38510      * Returns the number of columns.
38511      * @return {Number}
38512      */
38513     getColumnCount : function(visibleOnly){
38514         if(visibleOnly === true){
38515             var c = 0;
38516             for(var i = 0, len = this.config.length; i < len; i++){
38517                 if(!this.isHidden(i)){
38518                     c++;
38519                 }
38520             }
38521             return c;
38522         }
38523         return this.config.length;
38524     },
38525
38526     /**
38527      * Returns the column configs that return true by the passed function that is called with (columnConfig, index)
38528      * @param {Function} fn
38529      * @param {Object} scope (optional)
38530      * @return {Array} result
38531      */
38532     getColumnsBy : function(fn, scope){
38533         var r = [];
38534         for(var i = 0, len = this.config.length; i < len; i++){
38535             var c = this.config[i];
38536             if(fn.call(scope||this, c, i) === true){
38537                 r[r.length] = c;
38538             }
38539         }
38540         return r;
38541     },
38542
38543     /**
38544      * Returns true if the specified column is sortable.
38545      * @param {Number} col The column index
38546      * @return {Boolean}
38547      */
38548     isSortable : function(col){
38549         if(typeof this.config[col].sortable == "undefined"){
38550             return this.defaultSortable;
38551         }
38552         return this.config[col].sortable;
38553     },
38554
38555     /**
38556      * Returns the rendering (formatting) function defined for the column.
38557      * @param {Number} col The column index.
38558      * @return {Function} The function used to render the cell. See {@link #setRenderer}.
38559      */
38560     getRenderer : function(col){
38561         if(!this.config[col].renderer){
38562             return Roo.grid.ColumnModel.defaultRenderer;
38563         }
38564         return this.config[col].renderer;
38565     },
38566
38567     /**
38568      * Sets the rendering (formatting) function for a column.
38569      * @param {Number} col The column index
38570      * @param {Function} fn The function to use to process the cell's raw data
38571      * to return HTML markup for the grid view. The render function is called with
38572      * the following parameters:<ul>
38573      * <li>Data value.</li>
38574      * <li>Cell metadata. An object in which you may set the following attributes:<ul>
38575      * <li>css A CSS style string to apply to the table cell.</li>
38576      * <li>attr An HTML attribute definition string to apply to the data container element <i>within</i> the table cell.</li></ul>
38577      * <li>The {@link Roo.data.Record} from which the data was extracted.</li>
38578      * <li>Row index</li>
38579      * <li>Column index</li>
38580      * <li>The {@link Roo.data.Store} object from which the Record was extracted</li></ul>
38581      */
38582     setRenderer : function(col, fn){
38583         this.config[col].renderer = fn;
38584     },
38585
38586     /**
38587      * Returns the width for the specified column.
38588      * @param {Number} col The column index
38589      * @return {Number}
38590      */
38591     getColumnWidth : function(col){
38592         return this.config[col].width * 1 || this.defaultWidth;
38593     },
38594
38595     /**
38596      * Sets the width for a column.
38597      * @param {Number} col The column index
38598      * @param {Number} width The new width
38599      */
38600     setColumnWidth : function(col, width, suppressEvent){
38601         this.config[col].width = width;
38602         this.totalWidth = null;
38603         if(!suppressEvent){
38604              this.fireEvent("widthchange", this, col, width);
38605         }
38606     },
38607
38608     /**
38609      * Returns the total width of all columns.
38610      * @param {Boolean} includeHidden True to include hidden column widths
38611      * @return {Number}
38612      */
38613     getTotalWidth : function(includeHidden){
38614         if(!this.totalWidth){
38615             this.totalWidth = 0;
38616             for(var i = 0, len = this.config.length; i < len; i++){
38617                 if(includeHidden || !this.isHidden(i)){
38618                     this.totalWidth += this.getColumnWidth(i);
38619                 }
38620             }
38621         }
38622         return this.totalWidth;
38623     },
38624
38625     /**
38626      * Returns the header for the specified column.
38627      * @param {Number} col The column index
38628      * @return {String}
38629      */
38630     getColumnHeader : function(col){
38631         return this.config[col].header;
38632     },
38633
38634     /**
38635      * Sets the header for a column.
38636      * @param {Number} col The column index
38637      * @param {String} header The new header
38638      */
38639     setColumnHeader : function(col, header){
38640         this.config[col].header = header;
38641         this.fireEvent("headerchange", this, col, header);
38642     },
38643
38644     /**
38645      * Returns the tooltip for the specified column.
38646      * @param {Number} col The column index
38647      * @return {String}
38648      */
38649     getColumnTooltip : function(col){
38650             return this.config[col].tooltip;
38651     },
38652     /**
38653      * Sets the tooltip for a column.
38654      * @param {Number} col The column index
38655      * @param {String} tooltip The new tooltip
38656      */
38657     setColumnTooltip : function(col, tooltip){
38658             this.config[col].tooltip = tooltip;
38659     },
38660
38661     /**
38662      * Returns the dataIndex for the specified column.
38663      * @param {Number} col The column index
38664      * @return {Number}
38665      */
38666     getDataIndex : function(col){
38667         return this.config[col].dataIndex;
38668     },
38669
38670     /**
38671      * Sets the dataIndex for a column.
38672      * @param {Number} col The column index
38673      * @param {Number} dataIndex The new dataIndex
38674      */
38675     setDataIndex : function(col, dataIndex){
38676         this.config[col].dataIndex = dataIndex;
38677     },
38678
38679     
38680     
38681     /**
38682      * Returns true if the cell is editable.
38683      * @param {Number} colIndex The column index
38684      * @param {Number} rowIndex The row index
38685      * @return {Boolean}
38686      */
38687     isCellEditable : function(colIndex, rowIndex){
38688         return (this.config[colIndex].editable || (typeof this.config[colIndex].editable == "undefined" && this.config[colIndex].editor)) ? true : false;
38689     },
38690
38691     /**
38692      * Returns the editor defined for the cell/column.
38693      * return false or null to disable editing.
38694      * @param {Number} colIndex The column index
38695      * @param {Number} rowIndex The row index
38696      * @return {Object}
38697      */
38698     getCellEditor : function(colIndex, rowIndex){
38699         return this.config[colIndex].editor;
38700     },
38701
38702     /**
38703      * Sets if a column is editable.
38704      * @param {Number} col The column index
38705      * @param {Boolean} editable True if the column is editable
38706      */
38707     setEditable : function(col, editable){
38708         this.config[col].editable = editable;
38709     },
38710
38711
38712     /**
38713      * Returns true if the column is hidden.
38714      * @param {Number} colIndex The column index
38715      * @return {Boolean}
38716      */
38717     isHidden : function(colIndex){
38718         return this.config[colIndex].hidden;
38719     },
38720
38721
38722     /**
38723      * Returns true if the column width cannot be changed
38724      */
38725     isFixed : function(colIndex){
38726         return this.config[colIndex].fixed;
38727     },
38728
38729     /**
38730      * Returns true if the column can be resized
38731      * @return {Boolean}
38732      */
38733     isResizable : function(colIndex){
38734         return colIndex >= 0 && this.config[colIndex].resizable !== false && this.config[colIndex].fixed !== true;
38735     },
38736     /**
38737      * Sets if a column is hidden.
38738      * @param {Number} colIndex The column index
38739      * @param {Boolean} hidden True if the column is hidden
38740      */
38741     setHidden : function(colIndex, hidden){
38742         this.config[colIndex].hidden = hidden;
38743         this.totalWidth = null;
38744         this.fireEvent("hiddenchange", this, colIndex, hidden);
38745     },
38746
38747     /**
38748      * Sets the editor for a column.
38749      * @param {Number} col The column index
38750      * @param {Object} editor The editor object
38751      */
38752     setEditor : function(col, editor){
38753         this.config[col].editor = editor;
38754     }
38755 });
38756
38757 Roo.grid.ColumnModel.defaultRenderer = function(value){
38758         if(typeof value == "string" && value.length < 1){
38759             return "&#160;";
38760         }
38761         return value;
38762 };
38763
38764 // Alias for backwards compatibility
38765 Roo.grid.DefaultColumnModel = Roo.grid.ColumnModel;
38766 /*
38767  * Based on:
38768  * Ext JS Library 1.1.1
38769  * Copyright(c) 2006-2007, Ext JS, LLC.
38770  *
38771  * Originally Released Under LGPL - original licence link has changed is not relivant.
38772  *
38773  * Fork - LGPL
38774  * <script type="text/javascript">
38775  */
38776
38777 /**
38778  * @class Roo.grid.AbstractSelectionModel
38779  * @extends Roo.util.Observable
38780  * Abstract base class for grid SelectionModels.  It provides the interface that should be
38781  * implemented by descendant classes.  This class should not be directly instantiated.
38782  * @constructor
38783  */
38784 Roo.grid.AbstractSelectionModel = function(){
38785     this.locked = false;
38786     Roo.grid.AbstractSelectionModel.superclass.constructor.call(this);
38787 };
38788
38789 Roo.extend(Roo.grid.AbstractSelectionModel, Roo.util.Observable,  {
38790     /** @ignore Called by the grid automatically. Do not call directly. */
38791     init : function(grid){
38792         this.grid = grid;
38793         this.initEvents();
38794     },
38795
38796     /**
38797      * Locks the selections.
38798      */
38799     lock : function(){
38800         this.locked = true;
38801     },
38802
38803     /**
38804      * Unlocks the selections.
38805      */
38806     unlock : function(){
38807         this.locked = false;
38808     },
38809
38810     /**
38811      * Returns true if the selections are locked.
38812      * @return {Boolean}
38813      */
38814     isLocked : function(){
38815         return this.locked;
38816     }
38817 });/*
38818  * Based on:
38819  * Ext JS Library 1.1.1
38820  * Copyright(c) 2006-2007, Ext JS, LLC.
38821  *
38822  * Originally Released Under LGPL - original licence link has changed is not relivant.
38823  *
38824  * Fork - LGPL
38825  * <script type="text/javascript">
38826  */
38827 /**
38828  * @extends Roo.grid.AbstractSelectionModel
38829  * @class Roo.grid.RowSelectionModel
38830  * The default SelectionModel used by {@link Roo.grid.Grid}.
38831  * It supports multiple selections and keyboard selection/navigation. 
38832  * @constructor
38833  * @param {Object} config
38834  */
38835 Roo.grid.RowSelectionModel = function(config){
38836     Roo.apply(this, config);
38837     this.selections = new Roo.util.MixedCollection(false, function(o){
38838         return o.id;
38839     });
38840
38841     this.last = false;
38842     this.lastActive = false;
38843
38844     this.addEvents({
38845         /**
38846              * @event selectionchange
38847              * Fires when the selection changes
38848              * @param {SelectionModel} this
38849              */
38850             "selectionchange" : true,
38851         /**
38852              * @event afterselectionchange
38853              * Fires after the selection changes (eg. by key press or clicking)
38854              * @param {SelectionModel} this
38855              */
38856             "afterselectionchange" : true,
38857         /**
38858              * @event beforerowselect
38859              * Fires when a row is selected being selected, return false to cancel.
38860              * @param {SelectionModel} this
38861              * @param {Number} rowIndex The selected index
38862              * @param {Boolean} keepExisting False if other selections will be cleared
38863              */
38864             "beforerowselect" : true,
38865         /**
38866              * @event rowselect
38867              * Fires when a row is selected.
38868              * @param {SelectionModel} this
38869              * @param {Number} rowIndex The selected index
38870              * @param {Roo.data.Record} r The record
38871              */
38872             "rowselect" : true,
38873         /**
38874              * @event rowdeselect
38875              * Fires when a row is deselected.
38876              * @param {SelectionModel} this
38877              * @param {Number} rowIndex The selected index
38878              */
38879         "rowdeselect" : true
38880     });
38881     Roo.grid.RowSelectionModel.superclass.constructor.call(this);
38882     this.locked = false;
38883 };
38884
38885 Roo.extend(Roo.grid.RowSelectionModel, Roo.grid.AbstractSelectionModel,  {
38886     /**
38887      * @cfg {Boolean} singleSelect
38888      * True to allow selection of only one row at a time (defaults to false)
38889      */
38890     singleSelect : false,
38891
38892     // private
38893     initEvents : function(){
38894
38895         if(!this.grid.enableDragDrop && !this.grid.enableDrag){
38896             this.grid.on("mousedown", this.handleMouseDown, this);
38897         }else{ // allow click to work like normal
38898             this.grid.on("rowclick", this.handleDragableRowClick, this);
38899         }
38900
38901         this.rowNav = new Roo.KeyNav(this.grid.getGridEl(), {
38902             "up" : function(e){
38903                 if(!e.shiftKey){
38904                     this.selectPrevious(e.shiftKey);
38905                 }else if(this.last !== false && this.lastActive !== false){
38906                     var last = this.last;
38907                     this.selectRange(this.last,  this.lastActive-1);
38908                     this.grid.getView().focusRow(this.lastActive);
38909                     if(last !== false){
38910                         this.last = last;
38911                     }
38912                 }else{
38913                     this.selectFirstRow();
38914                 }
38915                 this.fireEvent("afterselectionchange", this);
38916             },
38917             "down" : function(e){
38918                 if(!e.shiftKey){
38919                     this.selectNext(e.shiftKey);
38920                 }else if(this.last !== false && this.lastActive !== false){
38921                     var last = this.last;
38922                     this.selectRange(this.last,  this.lastActive+1);
38923                     this.grid.getView().focusRow(this.lastActive);
38924                     if(last !== false){
38925                         this.last = last;
38926                     }
38927                 }else{
38928                     this.selectFirstRow();
38929                 }
38930                 this.fireEvent("afterselectionchange", this);
38931             },
38932             scope: this
38933         });
38934
38935         var view = this.grid.view;
38936         view.on("refresh", this.onRefresh, this);
38937         view.on("rowupdated", this.onRowUpdated, this);
38938         view.on("rowremoved", this.onRemove, this);
38939     },
38940
38941     // private
38942     onRefresh : function(){
38943         var ds = this.grid.dataSource, i, v = this.grid.view;
38944         var s = this.selections;
38945         s.each(function(r){
38946             if((i = ds.indexOfId(r.id)) != -1){
38947                 v.onRowSelect(i);
38948             }else{
38949                 s.remove(r);
38950             }
38951         });
38952     },
38953
38954     // private
38955     onRemove : function(v, index, r){
38956         this.selections.remove(r);
38957     },
38958
38959     // private
38960     onRowUpdated : function(v, index, r){
38961         if(this.isSelected(r)){
38962             v.onRowSelect(index);
38963         }
38964     },
38965
38966     /**
38967      * Select records.
38968      * @param {Array} records The records to select
38969      * @param {Boolean} keepExisting (optional) True to keep existing selections
38970      */
38971     selectRecords : function(records, keepExisting){
38972         if(!keepExisting){
38973             this.clearSelections();
38974         }
38975         var ds = this.grid.dataSource;
38976         for(var i = 0, len = records.length; i < len; i++){
38977             this.selectRow(ds.indexOf(records[i]), true);
38978         }
38979     },
38980
38981     /**
38982      * Gets the number of selected rows.
38983      * @return {Number}
38984      */
38985     getCount : function(){
38986         return this.selections.length;
38987     },
38988
38989     /**
38990      * Selects the first row in the grid.
38991      */
38992     selectFirstRow : function(){
38993         this.selectRow(0);
38994     },
38995
38996     /**
38997      * Select the last row.
38998      * @param {Boolean} keepExisting (optional) True to keep existing selections
38999      */
39000     selectLastRow : function(keepExisting){
39001         this.selectRow(this.grid.dataSource.getCount() - 1, keepExisting);
39002     },
39003
39004     /**
39005      * Selects the row immediately following the last selected row.
39006      * @param {Boolean} keepExisting (optional) True to keep existing selections
39007      */
39008     selectNext : function(keepExisting){
39009         if(this.last !== false && (this.last+1) < this.grid.dataSource.getCount()){
39010             this.selectRow(this.last+1, keepExisting);
39011             this.grid.getView().focusRow(this.last);
39012         }
39013     },
39014
39015     /**
39016      * Selects the row that precedes the last selected row.
39017      * @param {Boolean} keepExisting (optional) True to keep existing selections
39018      */
39019     selectPrevious : function(keepExisting){
39020         if(this.last){
39021             this.selectRow(this.last-1, keepExisting);
39022             this.grid.getView().focusRow(this.last);
39023         }
39024     },
39025
39026     /**
39027      * Returns the selected records
39028      * @return {Array} Array of selected records
39029      */
39030     getSelections : function(){
39031         return [].concat(this.selections.items);
39032     },
39033
39034     /**
39035      * Returns the first selected record.
39036      * @return {Record}
39037      */
39038     getSelected : function(){
39039         return this.selections.itemAt(0);
39040     },
39041
39042
39043     /**
39044      * Clears all selections.
39045      */
39046     clearSelections : function(fast){
39047         if(this.locked) return;
39048         if(fast !== true){
39049             var ds = this.grid.dataSource;
39050             var s = this.selections;
39051             s.each(function(r){
39052                 this.deselectRow(ds.indexOfId(r.id));
39053             }, this);
39054             s.clear();
39055         }else{
39056             this.selections.clear();
39057         }
39058         this.last = false;
39059     },
39060
39061
39062     /**
39063      * Selects all rows.
39064      */
39065     selectAll : function(){
39066         if(this.locked) return;
39067         this.selections.clear();
39068         for(var i = 0, len = this.grid.dataSource.getCount(); i < len; i++){
39069             this.selectRow(i, true);
39070         }
39071     },
39072
39073     /**
39074      * Returns True if there is a selection.
39075      * @return {Boolean}
39076      */
39077     hasSelection : function(){
39078         return this.selections.length > 0;
39079     },
39080
39081     /**
39082      * Returns True if the specified row is selected.
39083      * @param {Number/Record} record The record or index of the record to check
39084      * @return {Boolean}
39085      */
39086     isSelected : function(index){
39087         var r = typeof index == "number" ? this.grid.dataSource.getAt(index) : index;
39088         return (r && this.selections.key(r.id) ? true : false);
39089     },
39090
39091     /**
39092      * Returns True if the specified record id is selected.
39093      * @param {String} id The id of record to check
39094      * @return {Boolean}
39095      */
39096     isIdSelected : function(id){
39097         return (this.selections.key(id) ? true : false);
39098     },
39099
39100     // private
39101     handleMouseDown : function(e, t){
39102         var view = this.grid.getView(), rowIndex;
39103         if(this.isLocked() || (rowIndex = view.findRowIndex(t)) === false){
39104             return;
39105         };
39106         if(e.shiftKey && this.last !== false){
39107             var last = this.last;
39108             this.selectRange(last, rowIndex, e.ctrlKey);
39109             this.last = last; // reset the last
39110             view.focusRow(rowIndex);
39111         }else{
39112             var isSelected = this.isSelected(rowIndex);
39113             if(e.button !== 0 && isSelected){
39114                 view.focusRow(rowIndex);
39115             }else if(e.ctrlKey && isSelected){
39116                 this.deselectRow(rowIndex);
39117             }else if(!isSelected){
39118                 this.selectRow(rowIndex, e.button === 0 && (e.ctrlKey || e.shiftKey));
39119                 view.focusRow(rowIndex);
39120             }
39121         }
39122         this.fireEvent("afterselectionchange", this);
39123     },
39124     // private
39125     handleDragableRowClick :  function(grid, rowIndex, e) 
39126     {
39127         if(e.button === 0 && !e.shiftKey && !e.ctrlKey) {
39128             this.selectRow(rowIndex, false);
39129             grid.view.focusRow(rowIndex);
39130              this.fireEvent("afterselectionchange", this);
39131         }
39132     },
39133     
39134     /**
39135      * Selects multiple rows.
39136      * @param {Array} rows Array of the indexes of the row to select
39137      * @param {Boolean} keepExisting (optional) True to keep existing selections
39138      */
39139     selectRows : function(rows, keepExisting){
39140         if(!keepExisting){
39141             this.clearSelections();
39142         }
39143         for(var i = 0, len = rows.length; i < len; i++){
39144             this.selectRow(rows[i], true);
39145         }
39146     },
39147
39148     /**
39149      * Selects a range of rows. All rows in between startRow and endRow are also selected.
39150      * @param {Number} startRow The index of the first row in the range
39151      * @param {Number} endRow The index of the last row in the range
39152      * @param {Boolean} keepExisting (optional) True to retain existing selections
39153      */
39154     selectRange : function(startRow, endRow, keepExisting){
39155         if(this.locked) return;
39156         if(!keepExisting){
39157             this.clearSelections();
39158         }
39159         if(startRow <= endRow){
39160             for(var i = startRow; i <= endRow; i++){
39161                 this.selectRow(i, true);
39162             }
39163         }else{
39164             for(var i = startRow; i >= endRow; i--){
39165                 this.selectRow(i, true);
39166             }
39167         }
39168     },
39169
39170     /**
39171      * Deselects a range of rows. All rows in between startRow and endRow are also deselected.
39172      * @param {Number} startRow The index of the first row in the range
39173      * @param {Number} endRow The index of the last row in the range
39174      */
39175     deselectRange : function(startRow, endRow, preventViewNotify){
39176         if(this.locked) return;
39177         for(var i = startRow; i <= endRow; i++){
39178             this.deselectRow(i, preventViewNotify);
39179         }
39180     },
39181
39182     /**
39183      * Selects a row.
39184      * @param {Number} row The index of the row to select
39185      * @param {Boolean} keepExisting (optional) True to keep existing selections
39186      */
39187     selectRow : function(index, keepExisting, preventViewNotify){
39188         if(this.locked || (index < 0 || index >= this.grid.dataSource.getCount())) return;
39189         if(this.fireEvent("beforerowselect", this, index, keepExisting) !== false){
39190             if(!keepExisting || this.singleSelect){
39191                 this.clearSelections();
39192             }
39193             var r = this.grid.dataSource.getAt(index);
39194             this.selections.add(r);
39195             this.last = this.lastActive = index;
39196             if(!preventViewNotify){
39197                 this.grid.getView().onRowSelect(index);
39198             }
39199             this.fireEvent("rowselect", this, index, r);
39200             this.fireEvent("selectionchange", this);
39201         }
39202     },
39203
39204     /**
39205      * Deselects a row.
39206      * @param {Number} row The index of the row to deselect
39207      */
39208     deselectRow : function(index, preventViewNotify){
39209         if(this.locked) return;
39210         if(this.last == index){
39211             this.last = false;
39212         }
39213         if(this.lastActive == index){
39214             this.lastActive = false;
39215         }
39216         var r = this.grid.dataSource.getAt(index);
39217         this.selections.remove(r);
39218         if(!preventViewNotify){
39219             this.grid.getView().onRowDeselect(index);
39220         }
39221         this.fireEvent("rowdeselect", this, index);
39222         this.fireEvent("selectionchange", this);
39223     },
39224
39225     // private
39226     restoreLast : function(){
39227         if(this._last){
39228             this.last = this._last;
39229         }
39230     },
39231
39232     // private
39233     acceptsNav : function(row, col, cm){
39234         return !cm.isHidden(col) && cm.isCellEditable(col, row);
39235     },
39236
39237     // private
39238     onEditorKey : function(field, e){
39239         var k = e.getKey(), newCell, g = this.grid, ed = g.activeEditor;
39240         if(k == e.TAB){
39241             e.stopEvent();
39242             ed.completeEdit();
39243             if(e.shiftKey){
39244                 newCell = g.walkCells(ed.row, ed.col-1, -1, this.acceptsNav, this);
39245             }else{
39246                 newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
39247             }
39248         }else if(k == e.ENTER && !e.ctrlKey){
39249             e.stopEvent();
39250             ed.completeEdit();
39251             if(e.shiftKey){
39252                 newCell = g.walkCells(ed.row-1, ed.col, -1, this.acceptsNav, this);
39253             }else{
39254                 newCell = g.walkCells(ed.row+1, ed.col, 1, this.acceptsNav, this);
39255             }
39256         }else if(k == e.ESC){
39257             ed.cancelEdit();
39258         }
39259         if(newCell){
39260             g.startEditing(newCell[0], newCell[1]);
39261         }
39262     }
39263 });/*
39264  * Based on:
39265  * Ext JS Library 1.1.1
39266  * Copyright(c) 2006-2007, Ext JS, LLC.
39267  *
39268  * Originally Released Under LGPL - original licence link has changed is not relivant.
39269  *
39270  * Fork - LGPL
39271  * <script type="text/javascript">
39272  */
39273 /**
39274  * @class Roo.grid.CellSelectionModel
39275  * @extends Roo.grid.AbstractSelectionModel
39276  * This class provides the basic implementation for cell selection in a grid.
39277  * @constructor
39278  * @param {Object} config The object containing the configuration of this model.
39279  * @cfg {Boolean} enter_is_tab Enter behaves the same as tab. (eg. goes to next cell) default: false
39280  */
39281 Roo.grid.CellSelectionModel = function(config){
39282     Roo.apply(this, config);
39283
39284     this.selection = null;
39285
39286     this.addEvents({
39287         /**
39288              * @event beforerowselect
39289              * Fires before a cell is selected.
39290              * @param {SelectionModel} this
39291              * @param {Number} rowIndex The selected row index
39292              * @param {Number} colIndex The selected cell index
39293              */
39294             "beforecellselect" : true,
39295         /**
39296              * @event cellselect
39297              * Fires when a cell is selected.
39298              * @param {SelectionModel} this
39299              * @param {Number} rowIndex The selected row index
39300              * @param {Number} colIndex The selected cell index
39301              */
39302             "cellselect" : true,
39303         /**
39304              * @event selectionchange
39305              * Fires when the active selection changes.
39306              * @param {SelectionModel} this
39307              * @param {Object} selection null for no selection or an object (o) with two properties
39308                 <ul>
39309                 <li>o.record: the record object for the row the selection is in</li>
39310                 <li>o.cell: An array of [rowIndex, columnIndex]</li>
39311                 </ul>
39312              */
39313             "selectionchange" : true,
39314         /**
39315              * @event tabend
39316              * Fires when the tab (or enter) was pressed on the last editable cell
39317              * You can use this to trigger add new row.
39318              * @param {SelectionModel} this
39319              */
39320             "tabend" : true,
39321          /**
39322              * @event beforeeditnext
39323              * Fires before the next editable sell is made active
39324              * You can use this to skip to another cell or fire the tabend
39325              *    if you set cell to false
39326              * @param {Object} eventdata object : { cell : [ row, col ] } 
39327              */
39328             "beforeeditnext" : true
39329     });
39330     Roo.grid.CellSelectionModel.superclass.constructor.call(this);
39331 };
39332
39333 Roo.extend(Roo.grid.CellSelectionModel, Roo.grid.AbstractSelectionModel,  {
39334     
39335     enter_is_tab: false,
39336
39337     /** @ignore */
39338     initEvents : function(){
39339         this.grid.on("mousedown", this.handleMouseDown, this);
39340         this.grid.getGridEl().on(Roo.isIE ? "keydown" : "keypress", this.handleKeyDown, this);
39341         var view = this.grid.view;
39342         view.on("refresh", this.onViewChange, this);
39343         view.on("rowupdated", this.onRowUpdated, this);
39344         view.on("beforerowremoved", this.clearSelections, this);
39345         view.on("beforerowsinserted", this.clearSelections, this);
39346         if(this.grid.isEditor){
39347             this.grid.on("beforeedit", this.beforeEdit,  this);
39348         }
39349     },
39350
39351         //private
39352     beforeEdit : function(e){
39353         this.select(e.row, e.column, false, true, e.record);
39354     },
39355
39356         //private
39357     onRowUpdated : function(v, index, r){
39358         if(this.selection && this.selection.record == r){
39359             v.onCellSelect(index, this.selection.cell[1]);
39360         }
39361     },
39362
39363         //private
39364     onViewChange : function(){
39365         this.clearSelections(true);
39366     },
39367
39368         /**
39369          * Returns the currently selected cell,.
39370          * @return {Array} The selected cell (row, column) or null if none selected.
39371          */
39372     getSelectedCell : function(){
39373         return this.selection ? this.selection.cell : null;
39374     },
39375
39376     /**
39377      * Clears all selections.
39378      * @param {Boolean} true to prevent the gridview from being notified about the change.
39379      */
39380     clearSelections : function(preventNotify){
39381         var s = this.selection;
39382         if(s){
39383             if(preventNotify !== true){
39384                 this.grid.view.onCellDeselect(s.cell[0], s.cell[1]);
39385             }
39386             this.selection = null;
39387             this.fireEvent("selectionchange", this, null);
39388         }
39389     },
39390
39391     /**
39392      * Returns true if there is a selection.
39393      * @return {Boolean}
39394      */
39395     hasSelection : function(){
39396         return this.selection ? true : false;
39397     },
39398
39399     /** @ignore */
39400     handleMouseDown : function(e, t){
39401         var v = this.grid.getView();
39402         if(this.isLocked()){
39403             return;
39404         };
39405         var row = v.findRowIndex(t);
39406         var cell = v.findCellIndex(t);
39407         if(row !== false && cell !== false){
39408             this.select(row, cell);
39409         }
39410     },
39411
39412     /**
39413      * Selects a cell.
39414      * @param {Number} rowIndex
39415      * @param {Number} collIndex
39416      */
39417     select : function(rowIndex, colIndex, preventViewNotify, preventFocus, /*internal*/ r){
39418         if(this.fireEvent("beforecellselect", this, rowIndex, colIndex) !== false){
39419             this.clearSelections();
39420             r = r || this.grid.dataSource.getAt(rowIndex);
39421             this.selection = {
39422                 record : r,
39423                 cell : [rowIndex, colIndex]
39424             };
39425             if(!preventViewNotify){
39426                 var v = this.grid.getView();
39427                 v.onCellSelect(rowIndex, colIndex);
39428                 if(preventFocus !== true){
39429                     v.focusCell(rowIndex, colIndex);
39430                 }
39431             }
39432             this.fireEvent("cellselect", this, rowIndex, colIndex);
39433             this.fireEvent("selectionchange", this, this.selection);
39434         }
39435     },
39436
39437         //private
39438     isSelectable : function(rowIndex, colIndex, cm){
39439         return !cm.isHidden(colIndex);
39440     },
39441
39442     /** @ignore */
39443     handleKeyDown : function(e){
39444         //Roo.log('Cell Sel Model handleKeyDown');
39445         if(!e.isNavKeyPress()){
39446             return;
39447         }
39448         var g = this.grid, s = this.selection;
39449         if(!s){
39450             e.stopEvent();
39451             var cell = g.walkCells(0, 0, 1, this.isSelectable,  this);
39452             if(cell){
39453                 this.select(cell[0], cell[1]);
39454             }
39455             return;
39456         }
39457         var sm = this;
39458         var walk = function(row, col, step){
39459             return g.walkCells(row, col, step, sm.isSelectable,  sm);
39460         };
39461         var k = e.getKey(), r = s.cell[0], c = s.cell[1];
39462         var newCell;
39463
39464       
39465
39466         switch(k){
39467             case e.TAB:
39468                 // handled by onEditorKey
39469                 if (g.isEditor && g.editing) {
39470                     return;
39471                 }
39472                 if(e.shiftKey) {
39473                     newCell = walk(r, c-1, -1);
39474                 } else {
39475                     newCell = walk(r, c+1, 1);
39476                 }
39477                 break;
39478             
39479             case e.DOWN:
39480                newCell = walk(r+1, c, 1);
39481                 break;
39482             
39483             case e.UP:
39484                 newCell = walk(r-1, c, -1);
39485                 break;
39486             
39487             case e.RIGHT:
39488                 newCell = walk(r, c+1, 1);
39489                 break;
39490             
39491             case e.LEFT:
39492                 newCell = walk(r, c-1, -1);
39493                 break;
39494             
39495             case e.ENTER:
39496                 
39497                 if(g.isEditor && !g.editing){
39498                    g.startEditing(r, c);
39499                    e.stopEvent();
39500                    return;
39501                 }
39502                 
39503                 
39504              break;
39505         };
39506         if(newCell){
39507             this.select(newCell[0], newCell[1]);
39508             e.stopEvent();
39509             
39510         }
39511     },
39512
39513     acceptsNav : function(row, col, cm){
39514         return !cm.isHidden(col) && cm.isCellEditable(col, row);
39515     },
39516     /**
39517      * Selects a cell.
39518      * @param {Number} field (not used) - as it's normally used as a listener
39519      * @param {Number} e - event - fake it by using
39520      *
39521      * var e = Roo.EventObjectImpl.prototype;
39522      * e.keyCode = e.TAB
39523      *
39524      * 
39525      */
39526     onEditorKey : function(field, e){
39527         
39528         var k = e.getKey(),
39529             newCell,
39530             g = this.grid,
39531             ed = g.activeEditor,
39532             forward = false;
39533         ///Roo.log('onEditorKey' + k);
39534         
39535         
39536         if (this.enter_is_tab && k == e.ENTER) {
39537             k = e.TAB;
39538         }
39539         
39540         if(k == e.TAB){
39541             if(e.shiftKey){
39542                 newCell = g.walkCells(ed.row, ed.col-1, -1, this.acceptsNav, this);
39543             }else{
39544                 newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
39545                 forward = true;
39546             }
39547             
39548             e.stopEvent();
39549             
39550         } else if(k == e.ENTER &&  !e.ctrlKey){
39551             ed.completeEdit();
39552             e.stopEvent();
39553             newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
39554         
39555                 } else if(k == e.ESC){
39556             ed.cancelEdit();
39557         }
39558                 
39559         if (newCell) {
39560             var ecall = { cell : newCell, forward : forward };
39561             this.fireEvent('beforeeditnext', ecall );
39562             newCell = ecall.cell;
39563                         forward = ecall.forward;
39564         }
39565                 
39566         if(newCell){
39567             //Roo.log('next cell after edit');
39568             g.startEditing.defer(100, g, [newCell[0], newCell[1]]);
39569         } else if (forward) {
39570             // tabbed past last
39571             this.fireEvent.defer(100, this, ['tabend',this]);
39572         }
39573     }
39574 });/*
39575  * Based on:
39576  * Ext JS Library 1.1.1
39577  * Copyright(c) 2006-2007, Ext JS, LLC.
39578  *
39579  * Originally Released Under LGPL - original licence link has changed is not relivant.
39580  *
39581  * Fork - LGPL
39582  * <script type="text/javascript">
39583  */
39584  
39585 /**
39586  * @class Roo.grid.EditorGrid
39587  * @extends Roo.grid.Grid
39588  * Class for creating and editable grid.
39589  * @param {String/HTMLElement/Roo.Element} container The element into which this grid will be rendered - 
39590  * The container MUST have some type of size defined for the grid to fill. The container will be 
39591  * automatically set to position relative if it isn't already.
39592  * @param {Object} dataSource The data model to bind to
39593  * @param {Object} colModel The column model with info about this grid's columns
39594  */
39595 Roo.grid.EditorGrid = function(container, config){
39596     Roo.grid.EditorGrid.superclass.constructor.call(this, container, config);
39597     this.getGridEl().addClass("xedit-grid");
39598
39599     if(!this.selModel){
39600         this.selModel = new Roo.grid.CellSelectionModel();
39601     }
39602
39603     this.activeEditor = null;
39604
39605         this.addEvents({
39606             /**
39607              * @event beforeedit
39608              * Fires before cell editing is triggered. The edit event object has the following properties <br />
39609              * <ul style="padding:5px;padding-left:16px;">
39610              * <li>grid - This grid</li>
39611              * <li>record - The record being edited</li>
39612              * <li>field - The field name being edited</li>
39613              * <li>value - The value for the field being edited.</li>
39614              * <li>row - The grid row index</li>
39615              * <li>column - The grid column index</li>
39616              * <li>cancel - Set this to true to cancel the edit or return false from your handler.</li>
39617              * </ul>
39618              * @param {Object} e An edit event (see above for description)
39619              */
39620             "beforeedit" : true,
39621             /**
39622              * @event afteredit
39623              * Fires after a cell is edited. <br />
39624              * <ul style="padding:5px;padding-left:16px;">
39625              * <li>grid - This grid</li>
39626              * <li>record - The record being edited</li>
39627              * <li>field - The field name being edited</li>
39628              * <li>value - The value being set</li>
39629              * <li>originalValue - The original value for the field, before the edit.</li>
39630              * <li>row - The grid row index</li>
39631              * <li>column - The grid column index</li>
39632              * </ul>
39633              * @param {Object} e An edit event (see above for description)
39634              */
39635             "afteredit" : true,
39636             /**
39637              * @event validateedit
39638              * Fires after a cell is edited, but before the value is set in the record. 
39639          * You can use this to modify the value being set in the field, Return false
39640              * to cancel the change. The edit event object has the following properties <br />
39641              * <ul style="padding:5px;padding-left:16px;">
39642          * <li>editor - This editor</li>
39643              * <li>grid - This grid</li>
39644              * <li>record - The record being edited</li>
39645              * <li>field - The field name being edited</li>
39646              * <li>value - The value being set</li>
39647              * <li>originalValue - The original value for the field, before the edit.</li>
39648              * <li>row - The grid row index</li>
39649              * <li>column - The grid column index</li>
39650              * <li>cancel - Set this to true to cancel the edit or return false from your handler.</li>
39651              * </ul>
39652              * @param {Object} e An edit event (see above for description)
39653              */
39654             "validateedit" : true
39655         });
39656     this.on("bodyscroll", this.stopEditing,  this);
39657     this.on(this.clicksToEdit == 1 ? "cellclick" : "celldblclick", this.onCellDblClick,  this);
39658 };
39659
39660 Roo.extend(Roo.grid.EditorGrid, Roo.grid.Grid, {
39661     /**
39662      * @cfg {Number} clicksToEdit
39663      * The number of clicks on a cell required to display the cell's editor (defaults to 2)
39664      */
39665     clicksToEdit: 2,
39666
39667     // private
39668     isEditor : true,
39669     // private
39670     trackMouseOver: false, // causes very odd FF errors
39671
39672     onCellDblClick : function(g, row, col){
39673         this.startEditing(row, col);
39674     },
39675
39676     onEditComplete : function(ed, value, startValue){
39677         this.editing = false;
39678         this.activeEditor = null;
39679         ed.un("specialkey", this.selModel.onEditorKey, this.selModel);
39680         var r = ed.record;
39681         var field = this.colModel.getDataIndex(ed.col);
39682         var e = {
39683             grid: this,
39684             record: r,
39685             field: field,
39686             originalValue: startValue,
39687             value: value,
39688             row: ed.row,
39689             column: ed.col,
39690             cancel:false,
39691             editor: ed
39692         };
39693         var cell = Roo.get(this.view.getCell(ed.row,ed.col))
39694         cell.show();
39695           
39696         if(String(value) !== String(startValue)){
39697             
39698             if(this.fireEvent("validateedit", e) !== false && !e.cancel){
39699                 r.set(field, e.value);
39700                 // if we are dealing with a combo box..
39701                 // then we also set the 'name' colum to be the displayField
39702                 if (ed.field.displayField && ed.field.name) {
39703                     r.set(ed.field.name, ed.field.el.dom.value);
39704                 }
39705                 
39706                 delete e.cancel; //?? why!!!
39707                 this.fireEvent("afteredit", e);
39708             }
39709         } else {
39710             this.fireEvent("afteredit", e); // always fire it!
39711         }
39712         this.view.focusCell(ed.row, ed.col);
39713     },
39714
39715     /**
39716      * Starts editing the specified for the specified row/column
39717      * @param {Number} rowIndex
39718      * @param {Number} colIndex
39719      */
39720     startEditing : function(row, col){
39721         this.stopEditing();
39722         if(this.colModel.isCellEditable(col, row)){
39723             this.view.ensureVisible(row, col, true);
39724           
39725             var r = this.dataSource.getAt(row);
39726             var field = this.colModel.getDataIndex(col);
39727             var cell = Roo.get(this.view.getCell(row,col));
39728             var e = {
39729                 grid: this,
39730                 record: r,
39731                 field: field,
39732                 value: r.data[field],
39733                 row: row,
39734                 column: col,
39735                 cancel:false 
39736             };
39737             if(this.fireEvent("beforeedit", e) !== false && !e.cancel){
39738                 this.editing = true;
39739                 var ed = this.colModel.getCellEditor(col, row);
39740                 
39741                 if (!ed) {
39742                     return;
39743                 }
39744                 if(!ed.rendered){
39745                     ed.render(ed.parentEl || document.body);
39746                 }
39747                 ed.field.reset();
39748                
39749                 cell.hide();
39750                 
39751                 (function(){ // complex but required for focus issues in safari, ie and opera
39752                     ed.row = row;
39753                     ed.col = col;
39754                     ed.record = r;
39755                     ed.on("complete",   this.onEditComplete,        this,       {single: true});
39756                     ed.on("specialkey", this.selModel.onEditorKey,  this.selModel);
39757                     this.activeEditor = ed;
39758                     var v = r.data[field];
39759                     ed.startEdit(this.view.getCell(row, col), v);
39760                     // combo's with 'displayField and name set
39761                     if (ed.field.displayField && ed.field.name) {
39762                         ed.field.el.dom.value = r.data[ed.field.name];
39763                     }
39764                     
39765                     
39766                 }).defer(50, this);
39767             }
39768         }
39769     },
39770         
39771     /**
39772      * Stops any active editing
39773      */
39774     stopEditing : function(){
39775         if(this.activeEditor){
39776             this.activeEditor.completeEdit();
39777         }
39778         this.activeEditor = null;
39779     },
39780         
39781          /**
39782      * Called to get grid's drag proxy text, by default returns this.ddText.
39783      * @return {String}
39784      */
39785     getDragDropText : function(){
39786         var count = this.selModel.getSelectedCell() ? 1 : 0;
39787         return String.format(this.ddText, count, count == 1 ? '' : 's');
39788     }
39789         
39790 });/*
39791  * Based on:
39792  * Ext JS Library 1.1.1
39793  * Copyright(c) 2006-2007, Ext JS, LLC.
39794  *
39795  * Originally Released Under LGPL - original licence link has changed is not relivant.
39796  *
39797  * Fork - LGPL
39798  * <script type="text/javascript">
39799  */
39800
39801 // private - not really -- you end up using it !
39802 // This is a support class used internally by the Grid components
39803
39804 /**
39805  * @class Roo.grid.GridEditor
39806  * @extends Roo.Editor
39807  * Class for creating and editable grid elements.
39808  * @param {Object} config any settings (must include field)
39809  */
39810 Roo.grid.GridEditor = function(field, config){
39811     if (!config && field.field) {
39812         config = field;
39813         field = Roo.factory(config.field, Roo.form);
39814     }
39815     Roo.grid.GridEditor.superclass.constructor.call(this, field, config);
39816     field.monitorTab = false;
39817 };
39818
39819 Roo.extend(Roo.grid.GridEditor, Roo.Editor, {
39820     
39821     /**
39822      * @cfg {Roo.form.Field} field Field to wrap (or xtyped)
39823      */
39824     
39825     alignment: "tl-tl",
39826     autoSize: "width",
39827     hideEl : false,
39828     cls: "x-small-editor x-grid-editor",
39829     shim:false,
39830     shadow:"frame"
39831 });/*
39832  * Based on:
39833  * Ext JS Library 1.1.1
39834  * Copyright(c) 2006-2007, Ext JS, LLC.
39835  *
39836  * Originally Released Under LGPL - original licence link has changed is not relivant.
39837  *
39838  * Fork - LGPL
39839  * <script type="text/javascript">
39840  */
39841   
39842
39843   
39844 Roo.grid.PropertyRecord = Roo.data.Record.create([
39845     {name:'name',type:'string'},  'value'
39846 ]);
39847
39848
39849 Roo.grid.PropertyStore = function(grid, source){
39850     this.grid = grid;
39851     this.store = new Roo.data.Store({
39852         recordType : Roo.grid.PropertyRecord
39853     });
39854     this.store.on('update', this.onUpdate,  this);
39855     if(source){
39856         this.setSource(source);
39857     }
39858     Roo.grid.PropertyStore.superclass.constructor.call(this);
39859 };
39860
39861
39862
39863 Roo.extend(Roo.grid.PropertyStore, Roo.util.Observable, {
39864     setSource : function(o){
39865         this.source = o;
39866         this.store.removeAll();
39867         var data = [];
39868         for(var k in o){
39869             if(this.isEditableValue(o[k])){
39870                 data.push(new Roo.grid.PropertyRecord({name: k, value: o[k]}, k));
39871             }
39872         }
39873         this.store.loadRecords({records: data}, {}, true);
39874     },
39875
39876     onUpdate : function(ds, record, type){
39877         if(type == Roo.data.Record.EDIT){
39878             var v = record.data['value'];
39879             var oldValue = record.modified['value'];
39880             if(this.grid.fireEvent('beforepropertychange', this.source, record.id, v, oldValue) !== false){
39881                 this.source[record.id] = v;
39882                 record.commit();
39883                 this.grid.fireEvent('propertychange', this.source, record.id, v, oldValue);
39884             }else{
39885                 record.reject();
39886             }
39887         }
39888     },
39889
39890     getProperty : function(row){
39891        return this.store.getAt(row);
39892     },
39893
39894     isEditableValue: function(val){
39895         if(val && val instanceof Date){
39896             return true;
39897         }else if(typeof val == 'object' || typeof val == 'function'){
39898             return false;
39899         }
39900         return true;
39901     },
39902
39903     setValue : function(prop, value){
39904         this.source[prop] = value;
39905         this.store.getById(prop).set('value', value);
39906     },
39907
39908     getSource : function(){
39909         return this.source;
39910     }
39911 });
39912
39913 Roo.grid.PropertyColumnModel = function(grid, store){
39914     this.grid = grid;
39915     var g = Roo.grid;
39916     g.PropertyColumnModel.superclass.constructor.call(this, [
39917         {header: this.nameText, sortable: true, dataIndex:'name', id: 'name'},
39918         {header: this.valueText, resizable:false, dataIndex: 'value', id: 'value'}
39919     ]);
39920     this.store = store;
39921     this.bselect = Roo.DomHelper.append(document.body, {
39922         tag: 'select', style:'display:none', cls: 'x-grid-editor', children: [
39923             {tag: 'option', value: 'true', html: 'true'},
39924             {tag: 'option', value: 'false', html: 'false'}
39925         ]
39926     });
39927     Roo.id(this.bselect);
39928     var f = Roo.form;
39929     this.editors = {
39930         'date' : new g.GridEditor(new f.DateField({selectOnFocus:true})),
39931         'string' : new g.GridEditor(new f.TextField({selectOnFocus:true})),
39932         'number' : new g.GridEditor(new f.NumberField({selectOnFocus:true, style:'text-align:left;'})),
39933         'int' : new g.GridEditor(new f.NumberField({selectOnFocus:true, allowDecimals:false, style:'text-align:left;'})),
39934         'boolean' : new g.GridEditor(new f.Field({el:this.bselect,selectOnFocus:true}))
39935     };
39936     this.renderCellDelegate = this.renderCell.createDelegate(this);
39937     this.renderPropDelegate = this.renderProp.createDelegate(this);
39938 };
39939
39940 Roo.extend(Roo.grid.PropertyColumnModel, Roo.grid.ColumnModel, {
39941     
39942     
39943     nameText : 'Name',
39944     valueText : 'Value',
39945     
39946     dateFormat : 'm/j/Y',
39947     
39948     
39949     renderDate : function(dateVal){
39950         return dateVal.dateFormat(this.dateFormat);
39951     },
39952
39953     renderBool : function(bVal){
39954         return bVal ? 'true' : 'false';
39955     },
39956
39957     isCellEditable : function(colIndex, rowIndex){
39958         return colIndex == 1;
39959     },
39960
39961     getRenderer : function(col){
39962         return col == 1 ?
39963             this.renderCellDelegate : this.renderPropDelegate;
39964     },
39965
39966     renderProp : function(v){
39967         return this.getPropertyName(v);
39968     },
39969
39970     renderCell : function(val){
39971         var rv = val;
39972         if(val instanceof Date){
39973             rv = this.renderDate(val);
39974         }else if(typeof val == 'boolean'){
39975             rv = this.renderBool(val);
39976         }
39977         return Roo.util.Format.htmlEncode(rv);
39978     },
39979
39980     getPropertyName : function(name){
39981         var pn = this.grid.propertyNames;
39982         return pn && pn[name] ? pn[name] : name;
39983     },
39984
39985     getCellEditor : function(colIndex, rowIndex){
39986         var p = this.store.getProperty(rowIndex);
39987         var n = p.data['name'], val = p.data['value'];
39988         
39989         if(typeof(this.grid.customEditors[n]) == 'string'){
39990             return this.editors[this.grid.customEditors[n]];
39991         }
39992         if(typeof(this.grid.customEditors[n]) != 'undefined'){
39993             return this.grid.customEditors[n];
39994         }
39995         if(val instanceof Date){
39996             return this.editors['date'];
39997         }else if(typeof val == 'number'){
39998             return this.editors['number'];
39999         }else if(typeof val == 'boolean'){
40000             return this.editors['boolean'];
40001         }else{
40002             return this.editors['string'];
40003         }
40004     }
40005 });
40006
40007 /**
40008  * @class Roo.grid.PropertyGrid
40009  * @extends Roo.grid.EditorGrid
40010  * This class represents the  interface of a component based property grid control.
40011  * <br><br>Usage:<pre><code>
40012  var grid = new Roo.grid.PropertyGrid("my-container-id", {
40013       
40014  });
40015  // set any options
40016  grid.render();
40017  * </code></pre>
40018   
40019  * @constructor
40020  * @param {String/HTMLElement/Roo.Element} container The element into which this grid will be rendered -
40021  * The container MUST have some type of size defined for the grid to fill. The container will be
40022  * automatically set to position relative if it isn't already.
40023  * @param {Object} config A config object that sets properties on this grid.
40024  */
40025 Roo.grid.PropertyGrid = function(container, config){
40026     config = config || {};
40027     var store = new Roo.grid.PropertyStore(this);
40028     this.store = store;
40029     var cm = new Roo.grid.PropertyColumnModel(this, store);
40030     store.store.sort('name', 'ASC');
40031     Roo.grid.PropertyGrid.superclass.constructor.call(this, container, Roo.apply({
40032         ds: store.store,
40033         cm: cm,
40034         enableColLock:false,
40035         enableColumnMove:false,
40036         stripeRows:false,
40037         trackMouseOver: false,
40038         clicksToEdit:1
40039     }, config));
40040     this.getGridEl().addClass('x-props-grid');
40041     this.lastEditRow = null;
40042     this.on('columnresize', this.onColumnResize, this);
40043     this.addEvents({
40044          /**
40045              * @event beforepropertychange
40046              * Fires before a property changes (return false to stop?)
40047              * @param {Roo.grid.PropertyGrid} grid property grid? (check could be store)
40048              * @param {String} id Record Id
40049              * @param {String} newval New Value
40050          * @param {String} oldval Old Value
40051              */
40052         "beforepropertychange": true,
40053         /**
40054              * @event propertychange
40055              * Fires after a property changes
40056              * @param {Roo.grid.PropertyGrid} grid property grid? (check could be store)
40057              * @param {String} id Record Id
40058              * @param {String} newval New Value
40059          * @param {String} oldval Old Value
40060              */
40061         "propertychange": true
40062     });
40063     this.customEditors = this.customEditors || {};
40064 };
40065 Roo.extend(Roo.grid.PropertyGrid, Roo.grid.EditorGrid, {
40066     
40067      /**
40068      * @cfg {Object} customEditors map of colnames=> custom editors.
40069      * the custom editor can be one of the standard ones (date|string|number|int|boolean), or a
40070      * grid editor eg. Roo.grid.GridEditor(new Roo.form.TextArea({selectOnFocus:true})),
40071      * false disables editing of the field.
40072          */
40073     
40074       /**
40075      * @cfg {Object} propertyNames map of property Names to their displayed value
40076          */
40077     
40078     render : function(){
40079         Roo.grid.PropertyGrid.superclass.render.call(this);
40080         this.autoSize.defer(100, this);
40081     },
40082
40083     autoSize : function(){
40084         Roo.grid.PropertyGrid.superclass.autoSize.call(this);
40085         if(this.view){
40086             this.view.fitColumns();
40087         }
40088     },
40089
40090     onColumnResize : function(){
40091         this.colModel.setColumnWidth(1, this.container.getWidth(true)-this.colModel.getColumnWidth(0));
40092         this.autoSize();
40093     },
40094     /**
40095      * Sets the data for the Grid
40096      * accepts a Key => Value object of all the elements avaiable.
40097      * @param {Object} data  to appear in grid.
40098      */
40099     setSource : function(source){
40100         this.store.setSource(source);
40101         //this.autoSize();
40102     },
40103     /**
40104      * Gets all the data from the grid.
40105      * @return {Object} data  data stored in grid
40106      */
40107     getSource : function(){
40108         return this.store.getSource();
40109     }
40110 });/*
40111  * Based on:
40112  * Ext JS Library 1.1.1
40113  * Copyright(c) 2006-2007, Ext JS, LLC.
40114  *
40115  * Originally Released Under LGPL - original licence link has changed is not relivant.
40116  *
40117  * Fork - LGPL
40118  * <script type="text/javascript">
40119  */
40120  
40121 /**
40122  * @class Roo.LoadMask
40123  * A simple utility class for generically masking elements while loading data.  If the element being masked has
40124  * an underlying {@link Roo.data.Store}, the masking will be automatically synchronized with the store's loading
40125  * process and the mask element will be cached for reuse.  For all other elements, this mask will replace the
40126  * element's UpdateManager load indicator and will be destroyed after the initial load.
40127  * @constructor
40128  * Create a new LoadMask
40129  * @param {String/HTMLElement/Roo.Element} el The element or DOM node, or its id
40130  * @param {Object} config The config object
40131  */
40132 Roo.LoadMask = function(el, config){
40133     this.el = Roo.get(el);
40134     Roo.apply(this, config);
40135     if(this.store){
40136         this.store.on('beforeload', this.onBeforeLoad, this);
40137         this.store.on('load', this.onLoad, this);
40138         this.store.on('loadexception', this.onLoadException, this);
40139         this.removeMask = false;
40140     }else{
40141         var um = this.el.getUpdateManager();
40142         um.showLoadIndicator = false; // disable the default indicator
40143         um.on('beforeupdate', this.onBeforeLoad, this);
40144         um.on('update', this.onLoad, this);
40145         um.on('failure', this.onLoad, this);
40146         this.removeMask = true;
40147     }
40148 };
40149
40150 Roo.LoadMask.prototype = {
40151     /**
40152      * @cfg {Boolean} removeMask
40153      * True to create a single-use mask that is automatically destroyed after loading (useful for page loads),
40154      * False to persist the mask element reference for multiple uses (e.g., for paged data widgets).  Defaults to false.
40155      */
40156     /**
40157      * @cfg {String} msg
40158      * The text to display in a centered loading message box (defaults to 'Loading...')
40159      */
40160     msg : 'Loading...',
40161     /**
40162      * @cfg {String} msgCls
40163      * The CSS class to apply to the loading message element (defaults to "x-mask-loading")
40164      */
40165     msgCls : 'x-mask-loading',
40166
40167     /**
40168      * Read-only. True if the mask is currently disabled so that it will not be displayed (defaults to false)
40169      * @type Boolean
40170      */
40171     disabled: false,
40172
40173     /**
40174      * Disables the mask to prevent it from being displayed
40175      */
40176     disable : function(){
40177        this.disabled = true;
40178     },
40179
40180     /**
40181      * Enables the mask so that it can be displayed
40182      */
40183     enable : function(){
40184         this.disabled = false;
40185     },
40186     
40187     onLoadException : function()
40188     {
40189         Roo.log(arguments);
40190         
40191         if (typeof(arguments[3]) != 'undefined') {
40192             Roo.MessageBox.alert("Error loading",arguments[3]);
40193         } 
40194         /*
40195         try {
40196             if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
40197                 Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
40198             }   
40199         } catch(e) {
40200             
40201         }
40202         */
40203     
40204         
40205         
40206         this.el.unmask(this.removeMask);
40207     },
40208     // private
40209     onLoad : function()
40210     {
40211         this.el.unmask(this.removeMask);
40212     },
40213
40214     // private
40215     onBeforeLoad : function(){
40216         if(!this.disabled){
40217             this.el.mask(this.msg, this.msgCls);
40218         }
40219     },
40220
40221     // private
40222     destroy : function(){
40223         if(this.store){
40224             this.store.un('beforeload', this.onBeforeLoad, this);
40225             this.store.un('load', this.onLoad, this);
40226             this.store.un('loadexception', this.onLoadException, this);
40227         }else{
40228             var um = this.el.getUpdateManager();
40229             um.un('beforeupdate', this.onBeforeLoad, this);
40230             um.un('update', this.onLoad, this);
40231             um.un('failure', this.onLoad, this);
40232         }
40233     }
40234 };/*
40235  * Based on:
40236  * Ext JS Library 1.1.1
40237  * Copyright(c) 2006-2007, Ext JS, LLC.
40238  *
40239  * Originally Released Under LGPL - original licence link has changed is not relivant.
40240  *
40241  * Fork - LGPL
40242  * <script type="text/javascript">
40243  */
40244
40245
40246 /**
40247  * @class Roo.XTemplate
40248  * @extends Roo.Template
40249  * Provides a template that can have nested templates for loops or conditionals. The syntax is:
40250 <pre><code>
40251 var t = new Roo.XTemplate(
40252         '&lt;select name="{name}"&gt;',
40253                 '&lt;tpl for="options"&gt;&lt;option value="{value:trim}"&gt;{text:ellipsis(10)}&lt;/option&gt;&lt;/tpl&gt;',
40254         '&lt;/select&gt;'
40255 );
40256  
40257 // then append, applying the master template values
40258  </code></pre>
40259  *
40260  * Supported features:
40261  *
40262  *  Tags:
40263
40264 <pre><code>
40265       {a_variable} - output encoded.
40266       {a_variable.format:("Y-m-d")} - call a method on the variable
40267       {a_variable:raw} - unencoded output
40268       {a_variable:toFixed(1,2)} - Roo.util.Format."toFixed"
40269       {a_variable:this.method_on_template(...)} - call a method on the template object.
40270  
40271 </code></pre>
40272  *  The tpl tag:
40273 <pre><code>
40274         &lt;tpl for="a_variable or condition.."&gt;&lt;/tpl&gt;
40275         &lt;tpl if="a_variable or condition"&gt;&lt;/tpl&gt;
40276         &lt;tpl exec="some javascript"&gt;&lt;/tpl&gt;
40277         &lt;tpl name="named_template"&gt;&lt;/tpl&gt; (experimental)
40278   
40279         &lt;tpl for="."&gt;&lt;/tpl&gt; - just iterate the property..
40280         &lt;tpl for=".."&gt;&lt;/tpl&gt; - iterates with the parent (probably the template) 
40281 </code></pre>
40282  *      
40283  */
40284 Roo.XTemplate = function()
40285 {
40286     Roo.XTemplate.superclass.constructor.apply(this, arguments);
40287     if (this.html) {
40288         this.compile();
40289     }
40290 };
40291
40292
40293 Roo.extend(Roo.XTemplate, Roo.Template, {
40294
40295     /**
40296      * The various sub templates
40297      */
40298     tpls : false,
40299     /**
40300      *
40301      * basic tag replacing syntax
40302      * WORD:WORD()
40303      *
40304      * // you can fake an object call by doing this
40305      *  x.t:(test,tesT) 
40306      * 
40307      */
40308     re : /\{([\w-\.]+)(?:\:([\w\.]*)(?:\((.*?)?\))?)?\}/g,
40309
40310     /**
40311      * compile the template
40312      *
40313      * This is not recursive, so I'm not sure how nested templates are really going to be handled..
40314      *
40315      */
40316     compile: function()
40317     {
40318         var s = this.html;
40319      
40320         s = ['<tpl>', s, '</tpl>'].join('');
40321     
40322         var re     = /<tpl\b[^>]*>((?:(?=([^<]+))\2|<(?!tpl\b[^>]*>))*?)<\/tpl>/,
40323             nameRe = /^<tpl\b[^>]*?for="(.*?)"/,
40324             ifRe   = /^<tpl\b[^>]*?if="(.*?)"/,
40325             execRe = /^<tpl\b[^>]*?exec="(.*?)"/,
40326             namedRe = /^<tpl\b[^>]*?name="(\w+)"/,  // named templates..
40327             m,
40328             id     = 0,
40329             tpls   = [];
40330     
40331         while(true == !!(m = s.match(re))){
40332             var forMatch   = m[0].match(nameRe),
40333                 ifMatch   = m[0].match(ifRe),
40334                 execMatch   = m[0].match(execRe),
40335                 namedMatch   = m[0].match(namedRe),
40336                 
40337                 exp  = null, 
40338                 fn   = null,
40339                 exec = null,
40340                 name = forMatch && forMatch[1] ? forMatch[1] : '';
40341                 
40342             if (ifMatch) {
40343                 // if - puts fn into test..
40344                 exp = ifMatch && ifMatch[1] ? ifMatch[1] : null;
40345                 if(exp){
40346                    fn = new Function('values', 'parent', 'with(values){ return '+(Roo.util.Format.htmlDecode(exp))+'; }');
40347                 }
40348             }
40349             
40350             if (execMatch) {
40351                 // exec - calls a function... returns empty if true is  returned.
40352                 exp = execMatch && execMatch[1] ? execMatch[1] : null;
40353                 if(exp){
40354                    exec = new Function('values', 'parent', 'with(values){ '+(Roo.util.Format.htmlDecode(exp))+'; }');
40355                 }
40356             }
40357             
40358             
40359             if (name) {
40360                 // for = 
40361                 switch(name){
40362                     case '.':  name = new Function('values', 'parent', 'with(values){ return values; }'); break;
40363                     case '..': name = new Function('values', 'parent', 'with(values){ return parent; }'); break;
40364                     default:   name = new Function('values', 'parent', 'with(values){ return '+name+'; }');
40365                 }
40366             }
40367             var uid = namedMatch ? namedMatch[1] : id;
40368             
40369             
40370             tpls.push({
40371                 id:     namedMatch ? namedMatch[1] : id,
40372                 target: name,
40373                 exec:   exec,
40374                 test:   fn,
40375                 body:   m[1] || ''
40376             });
40377             if (namedMatch) {
40378                 s = s.replace(m[0], '');
40379             } else { 
40380                 s = s.replace(m[0], '{xtpl'+ id + '}');
40381             }
40382             ++id;
40383         }
40384         this.tpls = [];
40385         for(var i = tpls.length-1; i >= 0; --i){
40386             this.compileTpl(tpls[i]);
40387             this.tpls[tpls[i].id] = tpls[i];
40388         }
40389         this.master = tpls[tpls.length-1];
40390         return this;
40391     },
40392     /**
40393      * same as applyTemplate, except it's done to one of the subTemplates
40394      * when using named templates, you can do:
40395      *
40396      * var str = pl.applySubTemplate('your-name', values);
40397      *
40398      * 
40399      * @param {Number} id of the template
40400      * @param {Object} values to apply to template
40401      * @param {Object} parent (normaly the instance of this object)
40402      */
40403     applySubTemplate : function(id, values, parent)
40404     {
40405         
40406         
40407         var t = this.tpls[id];
40408         
40409         
40410         try { 
40411             if(t.test && !t.test.call(this, values, parent)){
40412                 return '';
40413             }
40414         } catch(e) {
40415             Roo.log("Xtemplate.applySubTemplate 'test': Exception thrown");
40416             Roo.log(e.toString());
40417             Roo.log(t.test);
40418             return ''
40419         }
40420         try { 
40421             
40422             if(t.exec && t.exec.call(this, values, parent)){
40423                 return '';
40424             }
40425         } catch(e) {
40426             Roo.log("Xtemplate.applySubTemplate 'exec': Exception thrown");
40427             Roo.log(e.toString());
40428             Roo.log(t.exec);
40429             return ''
40430         }
40431         try {
40432             var vs = t.target ? t.target.call(this, values, parent) : values;
40433             parent = t.target ? values : parent;
40434             if(t.target && vs instanceof Array){
40435                 var buf = [];
40436                 for(var i = 0, len = vs.length; i < len; i++){
40437                     buf[buf.length] = t.compiled.call(this, vs[i], parent);
40438                 }
40439                 return buf.join('');
40440             }
40441             return t.compiled.call(this, vs, parent);
40442         } catch (e) {
40443             Roo.log("Xtemplate.applySubTemplate : Exception thrown");
40444             Roo.log(e.toString());
40445             Roo.log(t.compiled);
40446             return '';
40447         }
40448     },
40449
40450     compileTpl : function(tpl)
40451     {
40452         var fm = Roo.util.Format;
40453         var useF = this.disableFormats !== true;
40454         var sep = Roo.isGecko ? "+" : ",";
40455         var undef = function(str) {
40456             Roo.log("Property not found :"  + str);
40457             return '';
40458         };
40459         
40460         var fn = function(m, name, format, args)
40461         {
40462             //Roo.log(arguments);
40463             args = args ? args.replace(/\\'/g,"'") : args;
40464             //["{TEST:(a,b,c)}", "TEST", "", "a,b,c", 0, "{TEST:(a,b,c)}"]
40465             if (typeof(format) == 'undefined') {
40466                 format= 'htmlEncode';
40467             }
40468             if (format == 'raw' ) {
40469                 format = false;
40470             }
40471             
40472             if(name.substr(0, 4) == 'xtpl'){
40473                 return "'"+ sep +'this.applySubTemplate('+name.substr(4)+', values, parent)'+sep+"'";
40474             }
40475             
40476             // build an array of options to determine if value is undefined..
40477             
40478             // basically get 'xxxx.yyyy' then do
40479             // (typeof(xxxx) == 'undefined' || typeof(xxx.yyyy) == 'undefined') ?
40480             //    (function () { Roo.log("Property not found"); return ''; })() :
40481             //    ......
40482             
40483             var udef_ar = [];
40484             var lookfor = '';
40485             Roo.each(name.split('.'), function(st) {
40486                 lookfor += (lookfor.length ? '.': '') + st;
40487                 udef_ar.push(  "(typeof(" + lookfor + ") == 'undefined')"  );
40488             });
40489             
40490             var udef_st = '((' + udef_ar.join(" || ") +") ? undef('" + name + "') : "; // .. needs )
40491             
40492             
40493             if(format && useF){
40494                 
40495                 args = args ? ',' + args : "";
40496                  
40497                 if(format.substr(0, 5) != "this."){
40498                     format = "fm." + format + '(';
40499                 }else{
40500                     format = 'this.call("'+ format.substr(5) + '", ';
40501                     args = ", values";
40502                 }
40503                 
40504                 return "'"+ sep +   udef_st   +    format + name + args + "))"+sep+"'";
40505             }
40506              
40507             if (args.length) {
40508                 // called with xxyx.yuu:(test,test)
40509                 // change to ()
40510                 return "'"+ sep + udef_st  + name + '(' +  args + "))"+sep+"'";
40511             }
40512             // raw.. - :raw modifier..
40513             return "'"+ sep + udef_st  + name + ")"+sep+"'";
40514             
40515         };
40516         var body;
40517         // branched to use + in gecko and [].join() in others
40518         if(Roo.isGecko){
40519             body = "tpl.compiled = function(values, parent){  with(values) { return '" +
40520                    tpl.body.replace(/(\r\n|\n)/g, '\\n').replace(/'/g, "\\'").replace(this.re, fn) +
40521                     "';};};";
40522         }else{
40523             body = ["tpl.compiled = function(values, parent){  with (values) { return ['"];
40524             body.push(tpl.body.replace(/(\r\n|\n)/g,
40525                             '\\n').replace(/'/g, "\\'").replace(this.re, fn));
40526             body.push("'].join('');};};");
40527             body = body.join('');
40528         }
40529         
40530         Roo.debug && Roo.log(body.replace(/\\n/,'\n'));
40531        
40532         /** eval:var:tpl eval:var:fm eval:var:useF eval:var:undef  */
40533         eval(body);
40534         
40535         return this;
40536     },
40537
40538     applyTemplate : function(values){
40539         return this.master.compiled.call(this, values, {});
40540         //var s = this.subs;
40541     },
40542
40543     apply : function(){
40544         return this.applyTemplate.apply(this, arguments);
40545     }
40546
40547  });
40548
40549 Roo.XTemplate.from = function(el){
40550     el = Roo.getDom(el);
40551     return new Roo.XTemplate(el.value || el.innerHTML);
40552 };/*
40553  * Original code for Roojs - LGPL
40554  * <script type="text/javascript">
40555  */
40556  
40557 /**
40558  * @class Roo.XComponent
40559  * A delayed Element creator...
40560  * Or a way to group chunks of interface together.
40561  * 
40562  * Mypart.xyx = new Roo.XComponent({
40563
40564     parent : 'Mypart.xyz', // empty == document.element.!!
40565     order : '001',
40566     name : 'xxxx'
40567     region : 'xxxx'
40568     disabled : function() {} 
40569      
40570     tree : function() { // return an tree of xtype declared components
40571         var MODULE = this;
40572         return 
40573         {
40574             xtype : 'NestedLayoutPanel',
40575             // technicall
40576         }
40577      ]
40578  *})
40579  *
40580  *
40581  * It can be used to build a big heiracy, with parent etc.
40582  * or you can just use this to render a single compoent to a dom element
40583  * MYPART.render(Roo.Element | String(id) | dom_element )
40584  * 
40585  * @extends Roo.util.Observable
40586  * @constructor
40587  * @param cfg {Object} configuration of component
40588  * 
40589  */
40590 Roo.XComponent = function(cfg) {
40591     Roo.apply(this, cfg);
40592     this.addEvents({ 
40593         /**
40594              * @event built
40595              * Fires when this the componnt is built
40596              * @param {Roo.XComponent} c the component
40597              */
40598         'built' : true
40599         
40600     });
40601     this.region = this.region || 'center'; // default..
40602     Roo.XComponent.register(this);
40603     this.modules = false;
40604     this.el = false; // where the layout goes..
40605     
40606     
40607 }
40608 Roo.extend(Roo.XComponent, Roo.util.Observable, {
40609     /**
40610      * @property el
40611      * The created element (with Roo.factory())
40612      * @type {Roo.Layout}
40613      */
40614     el  : false,
40615     
40616     /**
40617      * @property el
40618      * for BC  - use el in new code
40619      * @type {Roo.Layout}
40620      */
40621     panel : false,
40622     
40623     /**
40624      * @property layout
40625      * for BC  - use el in new code
40626      * @type {Roo.Layout}
40627      */
40628     layout : false,
40629     
40630      /**
40631      * @cfg {Function|boolean} disabled
40632      * If this module is disabled by some rule, return true from the funtion
40633      */
40634     disabled : false,
40635     
40636     /**
40637      * @cfg {String} parent 
40638      * Name of parent element which it get xtype added to..
40639      */
40640     parent: false,
40641     
40642     /**
40643      * @cfg {String} order
40644      * Used to set the order in which elements are created (usefull for multiple tabs)
40645      */
40646     
40647     order : false,
40648     /**
40649      * @cfg {String} name
40650      * String to display while loading.
40651      */
40652     name : false,
40653     /**
40654      * @cfg {String} region
40655      * Region to render component to (defaults to center)
40656      */
40657     region : 'center',
40658     
40659     /**
40660      * @cfg {Array} items
40661      * A single item array - the first element is the root of the tree..
40662      * It's done this way to stay compatible with the Xtype system...
40663      */
40664     items : false,
40665     
40666     /**
40667      * @property _tree
40668      * The method that retuns the tree of parts that make up this compoennt 
40669      * @type {function}
40670      */
40671     _tree  : false,
40672     
40673      /**
40674      * render
40675      * render element to dom or tree
40676      * @param {Roo.Element|String|DomElement} optional render to if parent is not set.
40677      */
40678     
40679     render : function(el)
40680     {
40681         
40682         el = el || false;
40683         var hp = this.parent ? 1 : 0;
40684         
40685         if (!el && typeof(this.parent) == 'string' && this.parent.substring(0,1) == '#') {
40686             // if parent is a '#.....' string, then let's use that..
40687             var ename = this.parent.substr(1)
40688             this.parent = false;
40689             el = Roo.get(ename);
40690             if (!el) {
40691                 Roo.log("Warning - element can not be found :#" + ename );
40692                 return;
40693             }
40694         }
40695         
40696         
40697         if (!this.parent) {
40698             
40699             el = el ? Roo.get(el) : false;      
40700             
40701             // it's a top level one..
40702             this.parent =  {
40703                 el : new Roo.BorderLayout(el || document.body, {
40704                 
40705                      center: {
40706                          titlebar: false,
40707                          autoScroll:false,
40708                          closeOnTab: true,
40709                          tabPosition: 'top',
40710                           //resizeTabs: true,
40711                          alwaysShowTabs: el && hp? false :  true,
40712                          hideTabs: el || !hp ? true :  false,
40713                          minTabWidth: 140
40714                      }
40715                  })
40716             }
40717         }
40718         
40719                 if (!this.parent.el) {
40720                         // probably an old style ctor, which has been disabled.
40721                         return;
40722                         
40723                 }
40724                 // The 'tree' method is  '_tree now' 
40725             
40726         var tree = this._tree ? this._tree() : this.tree();
40727         tree.region = tree.region || this.region;
40728         this.el = this.parent.el.addxtype(tree);
40729         this.fireEvent('built', this);
40730         
40731         this.panel = this.el;
40732         this.layout = this.panel.layout;
40733                 this.parentLayout = this.parent.layout  || false;  
40734          
40735     }
40736     
40737 });
40738
40739 Roo.apply(Roo.XComponent, {
40740     /**
40741      * @property  hideProgress
40742      * true to disable the building progress bar.. usefull on single page renders.
40743      * @type Boolean
40744      */
40745     hideProgress : false,
40746     /**
40747      * @property  buildCompleted
40748      * True when the builder has completed building the interface.
40749      * @type Boolean
40750      */
40751     buildCompleted : false,
40752      
40753     /**
40754      * @property  topModule
40755      * the upper most module - uses document.element as it's constructor.
40756      * @type Object
40757      */
40758      
40759     topModule  : false,
40760       
40761     /**
40762      * @property  modules
40763      * array of modules to be created by registration system.
40764      * @type {Array} of Roo.XComponent
40765      */
40766     
40767     modules : [],
40768     /**
40769      * @property  elmodules
40770      * array of modules to be created by which use #ID 
40771      * @type {Array} of Roo.XComponent
40772      */
40773      
40774     elmodules : [],
40775
40776     
40777     /**
40778      * Register components to be built later.
40779      *
40780      * This solves the following issues
40781      * - Building is not done on page load, but after an authentication process has occured.
40782      * - Interface elements are registered on page load
40783      * - Parent Interface elements may not be loaded before child, so this handles that..
40784      * 
40785      *
40786      * example:
40787      * 
40788      * MyApp.register({
40789           order : '000001',
40790           module : 'Pman.Tab.projectMgr',
40791           region : 'center',
40792           parent : 'Pman.layout',
40793           disabled : false,  // or use a function..
40794         })
40795      
40796      * * @param {Object} details about module
40797      */
40798     register : function(obj) {
40799                 
40800         Roo.XComponent.event.fireEvent('register', obj);
40801         switch(typeof(obj.disabled) ) {
40802                 
40803             case 'undefined':
40804                 break;
40805             
40806             case 'function':
40807                 if ( obj.disabled() ) {
40808                         return;
40809                 }
40810                 break;
40811             
40812             default:
40813                 if (obj.disabled) {
40814                         return;
40815                 }
40816                 break;
40817         }
40818                 
40819         this.modules.push(obj);
40820          
40821     },
40822     /**
40823      * convert a string to an object..
40824      * eg. 'AAA.BBB' -> finds AAA.BBB
40825
40826      */
40827     
40828     toObject : function(str)
40829     {
40830         if (!str || typeof(str) == 'object') {
40831             return str;
40832         }
40833         if (str.substring(0,1) == '#') {
40834             return str;
40835         }
40836
40837         var ar = str.split('.');
40838         var rt, o;
40839         rt = ar.shift();
40840             /** eval:var:o */
40841         try {
40842             eval('if (typeof ' + rt + ' == "undefined"){ o = false;} o = ' + rt + ';');
40843         } catch (e) {
40844             throw "Module not found : " + str;
40845         }
40846         
40847         if (o === false) {
40848             throw "Module not found : " + str;
40849         }
40850         Roo.each(ar, function(e) {
40851             if (typeof(o[e]) == 'undefined') {
40852                 throw "Module not found : " + str;
40853             }
40854             o = o[e];
40855         });
40856         
40857         return o;
40858         
40859     },
40860     
40861     
40862     /**
40863      * move modules into their correct place in the tree..
40864      * 
40865      */
40866     preBuild : function ()
40867     {
40868         var _t = this;
40869         Roo.each(this.modules , function (obj)
40870         {
40871             Roo.XComponent.event.fireEvent('beforebuild', obj);
40872             
40873             var opar = obj.parent;
40874             try { 
40875                 obj.parent = this.toObject(opar);
40876             } catch(e) {
40877                 Roo.log("parent:toObject failed: " + e.toString());
40878                 return;
40879             }
40880             
40881             if (!obj.parent) {
40882                 Roo.debug && Roo.log("GOT top level module");
40883                 Roo.debug && Roo.log(obj);
40884                 obj.modules = new Roo.util.MixedCollection(false, 
40885                     function(o) { return o.order + '' }
40886                 );
40887                 this.topModule = obj;
40888                 return;
40889             }
40890                         // parent is a string (usually a dom element name..)
40891             if (typeof(obj.parent) == 'string') {
40892                 this.elmodules.push(obj);
40893                 return;
40894             }
40895             if (obj.parent.constructor != Roo.XComponent) {
40896                 Roo.log("Warning : Object Parent is not instance of XComponent:" + obj.name)
40897             }
40898             if (!obj.parent.modules) {
40899                 obj.parent.modules = new Roo.util.MixedCollection(false, 
40900                     function(o) { return o.order + '' }
40901                 );
40902             }
40903             if (obj.parent.disabled) {
40904                 obj.disabled = true;
40905             }
40906             obj.parent.modules.add(obj);
40907         }, this);
40908     },
40909     
40910      /**
40911      * make a list of modules to build.
40912      * @return {Array} list of modules. 
40913      */ 
40914     
40915     buildOrder : function()
40916     {
40917         var _this = this;
40918         var cmp = function(a,b) {   
40919             return String(a).toUpperCase() > String(b).toUpperCase() ? 1 : -1;
40920         };
40921         if ((!this.topModule || !this.topModule.modules) && !this.elmodules.length) {
40922             throw "No top level modules to build";
40923         }
40924         
40925         // make a flat list in order of modules to build.
40926         var mods = this.topModule ? [ this.topModule ] : [];
40927                 
40928         
40929         // elmodules (is a list of DOM based modules )
40930         Roo.each(this.elmodules, function(e) {
40931             mods.push(e);
40932             if (!this.topModule &&
40933                 typeof(e.parent) == 'string' &&
40934                 e.parent.substring(0,1) == '#' &&
40935                 Roo.get(e.parent.substr(1))
40936                ) {
40937                 
40938                 _this.topModule = e;
40939             }
40940             
40941         });
40942
40943         
40944         // add modules to their parents..
40945         var addMod = function(m) {
40946             Roo.debug && Roo.log("build Order: add: " + m.name);
40947                 
40948             mods.push(m);
40949             if (m.modules && !m.disabled) {
40950                 Roo.debug && Roo.log("build Order: " + m.modules.length + " child modules");
40951                 m.modules.keySort('ASC',  cmp );
40952                 Roo.debug && Roo.log("build Order: " + m.modules.length + " child modules (after sort)");
40953     
40954                 m.modules.each(addMod);
40955             } else {
40956                 Roo.debug && Roo.log("build Order: no child modules");
40957             }
40958             // not sure if this is used any more..
40959             if (m.finalize) {
40960                 m.finalize.name = m.name + " (clean up) ";
40961                 mods.push(m.finalize);
40962             }
40963             
40964         }
40965         if (this.topModule && this.topModule.modules) { 
40966             this.topModule.modules.keySort('ASC',  cmp );
40967             this.topModule.modules.each(addMod);
40968         } 
40969         return mods;
40970     },
40971     
40972      /**
40973      * Build the registered modules.
40974      * @param {Object} parent element.
40975      * @param {Function} optional method to call after module has been added.
40976      * 
40977      */ 
40978    
40979     build : function() 
40980     {
40981         
40982         this.preBuild();
40983         var mods = this.buildOrder();
40984       
40985         //this.allmods = mods;
40986         //Roo.debug && Roo.log(mods);
40987         //return;
40988         if (!mods.length) { // should not happen
40989             throw "NO modules!!!";
40990         }
40991         
40992         
40993         var msg = "Building Interface...";
40994         // flash it up as modal - so we store the mask!?
40995         if (!this.hideProgress) {
40996             Roo.MessageBox.show({ title: 'loading' });
40997             Roo.MessageBox.show({
40998                title: "Please wait...",
40999                msg: msg,
41000                width:450,
41001                progress:true,
41002                closable:false,
41003                modal: false
41004               
41005             });
41006         }
41007         var total = mods.length;
41008         
41009         var _this = this;
41010         var progressRun = function() {
41011             if (!mods.length) {
41012                 Roo.debug && Roo.log('hide?');
41013                 if (!this.hideProgress) {
41014                     Roo.MessageBox.hide();
41015                 }
41016                 Roo.XComponent.event.fireEvent('buildcomplete', _this.topModule);
41017                 
41018                 // THE END...
41019                 return false;   
41020             }
41021             
41022             var m = mods.shift();
41023             
41024             
41025             Roo.debug && Roo.log(m);
41026             // not sure if this is supported any more.. - modules that are are just function
41027             if (typeof(m) == 'function') { 
41028                 m.call(this);
41029                 return progressRun.defer(10, _this);
41030             } 
41031             
41032             
41033             msg = "Building Interface " + (total  - mods.length) + 
41034                     " of " + total + 
41035                     (m.name ? (' - ' + m.name) : '');
41036                         Roo.debug && Roo.log(msg);
41037             if (!this.hideProgress) { 
41038                 Roo.MessageBox.updateProgress(  (total  - mods.length)/total, msg  );
41039             }
41040             
41041          
41042             // is the module disabled?
41043             var disabled = (typeof(m.disabled) == 'function') ?
41044                 m.disabled.call(m.module.disabled) : m.disabled;    
41045             
41046             
41047             if (disabled) {
41048                 return progressRun(); // we do not update the display!
41049             }
41050             
41051             // now build 
41052             
41053                         
41054                         
41055             m.render();
41056             // it's 10 on top level, and 1 on others??? why...
41057             return progressRun.defer(10, _this);
41058              
41059         }
41060         progressRun.defer(1, _this);
41061      
41062         
41063         
41064     },
41065         
41066         
41067         /**
41068          * Event Object.
41069          *
41070          *
41071          */
41072         event: false, 
41073     /**
41074          * wrapper for event.on - aliased later..  
41075          * Typically use to register a event handler for register:
41076          *
41077          * eg. Roo.XComponent.on('register', function(comp) { comp.disable = true } );
41078          *
41079          */
41080     on : false
41081    
41082     
41083     
41084 });
41085
41086 Roo.XComponent.event = new Roo.util.Observable({
41087                 events : { 
41088                         /**
41089                          * @event register
41090                          * Fires when an Component is registered,
41091                          * set the disable property on the Component to stop registration.
41092                          * @param {Roo.XComponent} c the component being registerd.
41093                          * 
41094                          */
41095                         'register' : true,
41096             /**
41097                          * @event beforebuild
41098                          * Fires before each Component is built
41099                          * can be used to apply permissions.
41100                          * @param {Roo.XComponent} c the component being registerd.
41101                          * 
41102                          */
41103                         'beforebuild' : true,
41104                         /**
41105                          * @event buildcomplete
41106                          * Fires on the top level element when all elements have been built
41107                          * @param {Roo.XComponent} the top level component.
41108                          */
41109                         'buildcomplete' : true
41110                         
41111                 }
41112 });
41113
41114 Roo.XComponent.on = Roo.XComponent.event.on.createDelegate(Roo.XComponent.event); 
41115